xref: /openbmc/bmcweb/features/redfish/lib/ethernet.hpp (revision dbb59d4dd8e4fc13b1cad11fd3a093f374a2a7ed)
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 <app.hpp>
19 #include <boost/container/flat_map.hpp>
20 #include <boost/container/flat_set.hpp>
21 #include <dbus_singleton.hpp>
22 #include <dbus_utility.hpp>
23 #include <error_messages.hpp>
24 #include <registries/privilege_registry.hpp>
25 #include <utils/json_utils.hpp>
26 
27 #include <optional>
28 #include <regex>
29 
30 namespace redfish
31 {
32 
33 /**
34  * DBus types primitives for several generic DBus interfaces
35  * TODO(Pawel) consider move this to separate file into boost::dbus
36  */
37 using PropertiesMapType =
38     boost::container::flat_map<std::string, dbus::utility::DbusVariantType>;
39 
40 enum class LinkType
41 {
42     Local,
43     Global
44 };
45 
46 /**
47  * Structure for keeping IPv4 data required by Redfish
48  */
49 struct IPv4AddressData
50 {
51     std::string id;
52     std::string address;
53     std::string domain;
54     std::string gateway;
55     std::string netmask;
56     std::string origin;
57     LinkType linktype;
58     bool isActive;
59 
60     bool operator<(const IPv4AddressData& obj) const
61     {
62         return id < obj.id;
63     }
64 };
65 
66 /**
67  * Structure for keeping IPv6 data required by Redfish
68  */
69 struct IPv6AddressData
70 {
71     std::string id;
72     std::string address;
73     std::string origin;
74     uint8_t prefixLength;
75 
76     bool operator<(const IPv6AddressData& obj) const
77     {
78         return id < obj.id;
79     }
80 };
81 /**
82  * Structure for keeping basic single Ethernet Interface information
83  * available from DBus
84  */
85 struct EthernetInterfaceData
86 {
87     uint32_t speed;
88     size_t mtuSize;
89     bool auto_neg;
90     bool DNSEnabled;
91     bool NTPEnabled;
92     bool HostNameEnabled;
93     bool SendHostNameEnabled;
94     bool linkUp;
95     bool nicEnabled;
96     std::string DHCPEnabled;
97     std::string operatingMode;
98     std::string hostname;
99     std::string default_gateway;
100     std::string ipv6_default_gateway;
101     std::string mac_address;
102     std::vector<std::uint32_t> vlan_id;
103     std::vector<std::string> nameServers;
104     std::vector<std::string> staticNameServers;
105     std::vector<std::string> domainnames;
106 };
107 
108 struct DHCPParameters
109 {
110     std::optional<bool> dhcpv4Enabled;
111     std::optional<bool> useDNSServers;
112     std::optional<bool> useNTPServers;
113     std::optional<bool> useUseDomainName;
114     std::optional<std::string> dhcpv6OperatingMode;
115 };
116 
117 // Helper function that changes bits netmask notation (i.e. /24)
118 // into full dot notation
119 inline std::string getNetmask(unsigned int bits)
120 {
121     uint32_t value = 0xffffffff << (32 - bits);
122     std::string netmask = std::to_string((value >> 24) & 0xff) + "." +
123                           std::to_string((value >> 16) & 0xff) + "." +
124                           std::to_string((value >> 8) & 0xff) + "." +
125                           std::to_string(value & 0xff);
126     return netmask;
127 }
128 
129 inline bool translateDHCPEnabledToBool(const std::string& inputDHCP,
130                                        bool isIPv4)
131 {
132     if (isIPv4)
133     {
134         return (
135             (inputDHCP ==
136              "xyz.openbmc_project.Network.EthernetInterface.DHCPConf.v4") ||
137             (inputDHCP ==
138              "xyz.openbmc_project.Network.EthernetInterface.DHCPConf.both"));
139     }
140     return ((inputDHCP ==
141              "xyz.openbmc_project.Network.EthernetInterface.DHCPConf.v6") ||
142             (inputDHCP ==
143              "xyz.openbmc_project.Network.EthernetInterface.DHCPConf.both"));
144 }
145 
146 inline std::string getDhcpEnabledEnumeration(bool isIPv4, bool isIPv6)
147 {
148     if (isIPv4 && isIPv6)
149     {
150         return "xyz.openbmc_project.Network.EthernetInterface.DHCPConf.both";
151     }
152     if (isIPv4)
153     {
154         return "xyz.openbmc_project.Network.EthernetInterface.DHCPConf.v4";
155     }
156     if (isIPv6)
157     {
158         return "xyz.openbmc_project.Network.EthernetInterface.DHCPConf.v6";
159     }
160     return "xyz.openbmc_project.Network.EthernetInterface.DHCPConf.none";
161 }
162 
163 inline std::string
164     translateAddressOriginDbusToRedfish(const std::string& inputOrigin,
165                                         bool isIPv4)
166 {
167     if (inputOrigin == "xyz.openbmc_project.Network.IP.AddressOrigin.Static")
168     {
169         return "Static";
170     }
171     if (inputOrigin == "xyz.openbmc_project.Network.IP.AddressOrigin.LinkLocal")
172     {
173         if (isIPv4)
174         {
175             return "IPv4LinkLocal";
176         }
177         return "LinkLocal";
178     }
179     if (inputOrigin == "xyz.openbmc_project.Network.IP.AddressOrigin.DHCP")
180     {
181         if (isIPv4)
182         {
183             return "DHCP";
184         }
185         return "DHCPv6";
186     }
187     if (inputOrigin == "xyz.openbmc_project.Network.IP.AddressOrigin.SLAAC")
188     {
189         return "SLAAC";
190     }
191     return "";
192 }
193 
194 inline bool
195     extractEthernetInterfaceData(const std::string& ethifaceId,
196                                  dbus::utility::ManagedObjectType& dbusData,
197                                  EthernetInterfaceData& ethData)
198 {
199     bool idFound = false;
200     for (auto& objpath : dbusData)
201     {
202         for (auto& ifacePair : objpath.second)
203         {
204             if (objpath.first == "/xyz/openbmc_project/network/" + ethifaceId)
205             {
206                 idFound = true;
207                 if (ifacePair.first == "xyz.openbmc_project.Network.MACAddress")
208                 {
209                     for (const auto& propertyPair : ifacePair.second)
210                     {
211                         if (propertyPair.first == "MACAddress")
212                         {
213                             const std::string* mac =
214                                 std::get_if<std::string>(&propertyPair.second);
215                             if (mac != nullptr)
216                             {
217                                 ethData.mac_address = *mac;
218                             }
219                         }
220                     }
221                 }
222                 else if (ifacePair.first == "xyz.openbmc_project.Network.VLAN")
223                 {
224                     for (const auto& propertyPair : ifacePair.second)
225                     {
226                         if (propertyPair.first == "Id")
227                         {
228                             const uint32_t* id =
229                                 std::get_if<uint32_t>(&propertyPair.second);
230                             if (id != nullptr)
231                             {
232                                 ethData.vlan_id.push_back(*id);
233                             }
234                         }
235                     }
236                 }
237                 else if (ifacePair.first ==
238                          "xyz.openbmc_project.Network.EthernetInterface")
239                 {
240                     for (const auto& propertyPair : ifacePair.second)
241                     {
242                         if (propertyPair.first == "AutoNeg")
243                         {
244                             const bool* autoNeg =
245                                 std::get_if<bool>(&propertyPair.second);
246                             if (autoNeg != nullptr)
247                             {
248                                 ethData.auto_neg = *autoNeg;
249                             }
250                         }
251                         else if (propertyPair.first == "Speed")
252                         {
253                             const uint32_t* speed =
254                                 std::get_if<uint32_t>(&propertyPair.second);
255                             if (speed != nullptr)
256                             {
257                                 ethData.speed = *speed;
258                             }
259                         }
260                         else if (propertyPair.first == "MTU")
261                         {
262                             const uint32_t* mtuSize =
263                                 std::get_if<uint32_t>(&propertyPair.second);
264                             if (mtuSize != nullptr)
265                             {
266                                 ethData.mtuSize = *mtuSize;
267                             }
268                         }
269                         else if (propertyPair.first == "LinkUp")
270                         {
271                             const bool* linkUp =
272                                 std::get_if<bool>(&propertyPair.second);
273                             if (linkUp != nullptr)
274                             {
275                                 ethData.linkUp = *linkUp;
276                             }
277                         }
278                         else if (propertyPair.first == "NICEnabled")
279                         {
280                             const bool* nicEnabled =
281                                 std::get_if<bool>(&propertyPair.second);
282                             if (nicEnabled != nullptr)
283                             {
284                                 ethData.nicEnabled = *nicEnabled;
285                             }
286                         }
287                         else if (propertyPair.first == "Nameservers")
288                         {
289                             const std::vector<std::string>* nameservers =
290                                 std::get_if<std::vector<std::string>>(
291                                     &propertyPair.second);
292                             if (nameservers != nullptr)
293                             {
294                                 ethData.nameServers = *nameservers;
295                             }
296                         }
297                         else if (propertyPair.first == "StaticNameServers")
298                         {
299                             const std::vector<std::string>* staticNameServers =
300                                 std::get_if<std::vector<std::string>>(
301                                     &propertyPair.second);
302                             if (staticNameServers != nullptr)
303                             {
304                                 ethData.staticNameServers = *staticNameServers;
305                             }
306                         }
307                         else if (propertyPair.first == "DHCPEnabled")
308                         {
309                             const std::string* dhcpEnabled =
310                                 std::get_if<std::string>(&propertyPair.second);
311                             if (dhcpEnabled != nullptr)
312                             {
313                                 ethData.DHCPEnabled = *dhcpEnabled;
314                             }
315                         }
316                         else if (propertyPair.first == "DomainName")
317                         {
318                             const std::vector<std::string>* domainNames =
319                                 std::get_if<std::vector<std::string>>(
320                                     &propertyPair.second);
321                             if (domainNames != nullptr)
322                             {
323                                 ethData.domainnames = *domainNames;
324                             }
325                         }
326                         else if (propertyPair.first == "DefaultGateway")
327                         {
328                             const std::string* defaultGateway =
329                                 std::get_if<std::string>(&propertyPair.second);
330                             if (defaultGateway != nullptr)
331                             {
332                                 std::string defaultGatewayStr = *defaultGateway;
333                                 if (defaultGatewayStr.empty())
334                                 {
335                                     ethData.default_gateway = "0.0.0.0";
336                                 }
337                                 else
338                                 {
339                                     ethData.default_gateway = defaultGatewayStr;
340                                 }
341                             }
342                         }
343                         else if (propertyPair.first == "DefaultGateway6")
344                         {
345                             const std::string* defaultGateway6 =
346                                 std::get_if<std::string>(&propertyPair.second);
347                             if (defaultGateway6 != nullptr)
348                             {
349                                 std::string defaultGateway6Str =
350                                     *defaultGateway6;
351                                 if (defaultGateway6Str.empty())
352                                 {
353                                     ethData.ipv6_default_gateway =
354                                         "0:0:0:0:0:0:0:0";
355                                 }
356                                 else
357                                 {
358                                     ethData.ipv6_default_gateway =
359                                         defaultGateway6Str;
360                                 }
361                             }
362                         }
363                     }
364                 }
365             }
366 
367             if (objpath.first == "/xyz/openbmc_project/network/config/dhcp")
368             {
369                 if (ifacePair.first ==
370                     "xyz.openbmc_project.Network.DHCPConfiguration")
371                 {
372                     for (const auto& propertyPair : ifacePair.second)
373                     {
374                         if (propertyPair.first == "DNSEnabled")
375                         {
376                             const bool* dnsEnabled =
377                                 std::get_if<bool>(&propertyPair.second);
378                             if (dnsEnabled != nullptr)
379                             {
380                                 ethData.DNSEnabled = *dnsEnabled;
381                             }
382                         }
383                         else if (propertyPair.first == "NTPEnabled")
384                         {
385                             const bool* ntpEnabled =
386                                 std::get_if<bool>(&propertyPair.second);
387                             if (ntpEnabled != nullptr)
388                             {
389                                 ethData.NTPEnabled = *ntpEnabled;
390                             }
391                         }
392                         else if (propertyPair.first == "HostNameEnabled")
393                         {
394                             const bool* hostNameEnabled =
395                                 std::get_if<bool>(&propertyPair.second);
396                             if (hostNameEnabled != nullptr)
397                             {
398                                 ethData.HostNameEnabled = *hostNameEnabled;
399                             }
400                         }
401                         else if (propertyPair.first == "SendHostNameEnabled")
402                         {
403                             const bool* sendHostNameEnabled =
404                                 std::get_if<bool>(&propertyPair.second);
405                             if (sendHostNameEnabled != nullptr)
406                             {
407                                 ethData.SendHostNameEnabled =
408                                     *sendHostNameEnabled;
409                             }
410                         }
411                     }
412                 }
413             }
414             // System configuration shows up in the global namespace, so no need
415             // to check eth number
416             if (ifacePair.first ==
417                 "xyz.openbmc_project.Network.SystemConfiguration")
418             {
419                 for (const auto& propertyPair : ifacePair.second)
420                 {
421                     if (propertyPair.first == "HostName")
422                     {
423                         const std::string* hostname =
424                             std::get_if<std::string>(&propertyPair.second);
425                         if (hostname != nullptr)
426                         {
427                             ethData.hostname = *hostname;
428                         }
429                     }
430                 }
431             }
432         }
433     }
434     return idFound;
435 }
436 
437 // Helper function that extracts data for single ethernet ipv6 address
438 inline void
439     extractIPV6Data(const std::string& ethifaceId,
440                     const dbus::utility::ManagedObjectType& dbusData,
441                     boost::container::flat_set<IPv6AddressData>& ipv6Config)
442 {
443     const std::string ipv6PathStart =
444         "/xyz/openbmc_project/network/" + ethifaceId + "/ipv6/";
445 
446     // Since there might be several IPv6 configurations aligned with
447     // single ethernet interface, loop over all of them
448     for (const auto& objpath : dbusData)
449     {
450         // Check if proper pattern for object path appears
451         if (boost::starts_with(objpath.first.str, ipv6PathStart))
452         {
453             for (const auto& interface : objpath.second)
454             {
455                 if (interface.first == "xyz.openbmc_project.Network.IP")
456                 {
457                     // Instance IPv6AddressData structure, and set as
458                     // appropriate
459                     std::pair<
460                         boost::container::flat_set<IPv6AddressData>::iterator,
461                         bool>
462                         it = ipv6Config.insert(IPv6AddressData{});
463                     IPv6AddressData& ipv6Address = *it.first;
464                     ipv6Address.id =
465                         objpath.first.str.substr(ipv6PathStart.size());
466                     for (const auto& property : interface.second)
467                     {
468                         if (property.first == "Address")
469                         {
470                             const std::string* address =
471                                 std::get_if<std::string>(&property.second);
472                             if (address != nullptr)
473                             {
474                                 ipv6Address.address = *address;
475                             }
476                         }
477                         else if (property.first == "Origin")
478                         {
479                             const std::string* origin =
480                                 std::get_if<std::string>(&property.second);
481                             if (origin != nullptr)
482                             {
483                                 ipv6Address.origin =
484                                     translateAddressOriginDbusToRedfish(*origin,
485                                                                         false);
486                             }
487                         }
488                         else if (property.first == "PrefixLength")
489                         {
490                             const uint8_t* prefix =
491                                 std::get_if<uint8_t>(&property.second);
492                             if (prefix != nullptr)
493                             {
494                                 ipv6Address.prefixLength = *prefix;
495                             }
496                         }
497                         else if (property.first == "Type" ||
498                                  property.first == "Gateway")
499                         {
500                             // Type & Gateway is not used
501                         }
502                         else
503                         {
504                             BMCWEB_LOG_ERROR
505                                 << "Got extra property: " << property.first
506                                 << " on the " << objpath.first.str << " object";
507                         }
508                     }
509                 }
510             }
511         }
512     }
513 }
514 
515 // Helper function that extracts data for single ethernet ipv4 address
516 inline void
517     extractIPData(const std::string& ethifaceId,
518                   const dbus::utility::ManagedObjectType& dbusData,
519                   boost::container::flat_set<IPv4AddressData>& ipv4Config)
520 {
521     const std::string ipv4PathStart =
522         "/xyz/openbmc_project/network/" + ethifaceId + "/ipv4/";
523 
524     // Since there might be several IPv4 configurations aligned with
525     // single ethernet interface, loop over all of them
526     for (const auto& objpath : dbusData)
527     {
528         // Check if proper pattern for object path appears
529         if (boost::starts_with(objpath.first.str, ipv4PathStart))
530         {
531             for (const auto& interface : objpath.second)
532             {
533                 if (interface.first == "xyz.openbmc_project.Network.IP")
534                 {
535                     // Instance IPv4AddressData structure, and set as
536                     // appropriate
537                     std::pair<
538                         boost::container::flat_set<IPv4AddressData>::iterator,
539                         bool>
540                         it = ipv4Config.insert(IPv4AddressData{});
541                     IPv4AddressData& ipv4Address = *it.first;
542                     ipv4Address.id =
543                         objpath.first.str.substr(ipv4PathStart.size());
544                     for (const auto& property : interface.second)
545                     {
546                         if (property.first == "Address")
547                         {
548                             const std::string* address =
549                                 std::get_if<std::string>(&property.second);
550                             if (address != nullptr)
551                             {
552                                 ipv4Address.address = *address;
553                             }
554                         }
555                         else if (property.first == "Origin")
556                         {
557                             const std::string* origin =
558                                 std::get_if<std::string>(&property.second);
559                             if (origin != nullptr)
560                             {
561                                 ipv4Address.origin =
562                                     translateAddressOriginDbusToRedfish(*origin,
563                                                                         true);
564                             }
565                         }
566                         else if (property.first == "PrefixLength")
567                         {
568                             const uint8_t* mask =
569                                 std::get_if<uint8_t>(&property.second);
570                             if (mask != nullptr)
571                             {
572                                 // convert it to the string
573                                 ipv4Address.netmask = getNetmask(*mask);
574                             }
575                         }
576                         else if (property.first == "Type" ||
577                                  property.first == "Gateway")
578                         {
579                             // Type & Gateway is not used
580                         }
581                         else
582                         {
583                             BMCWEB_LOG_ERROR
584                                 << "Got extra property: " << property.first
585                                 << " on the " << objpath.first.str << " object";
586                         }
587                     }
588                     // Check if given address is local, or global
589                     ipv4Address.linktype =
590                         boost::starts_with(ipv4Address.address, "169.254.")
591                             ? LinkType::Local
592                             : LinkType::Global;
593                 }
594             }
595         }
596     }
597 }
598 
599 /**
600  * @brief Sets given Id on the given VLAN interface through D-Bus
601  *
602  * @param[in] ifaceId       Id of VLAN interface that should be modified
603  * @param[in] inputVlanId   New ID of the VLAN
604  * @param[in] callback      Function that will be called after the operation
605  *
606  * @return None.
607  */
608 template <typename CallbackFunc>
609 void changeVlanId(const std::string& ifaceId, const uint32_t& inputVlanId,
610                   CallbackFunc&& callback)
611 {
612     crow::connections::systemBus->async_method_call(
613         callback, "xyz.openbmc_project.Network",
614         std::string("/xyz/openbmc_project/network/") + ifaceId,
615         "org.freedesktop.DBus.Properties", "Set",
616         "xyz.openbmc_project.Network.VLAN", "Id",
617         dbus::utility::DbusVariantType(inputVlanId));
618 }
619 
620 /**
621  * @brief Helper function that verifies IP address to check if it is in
622  *        proper format. If bits pointer is provided, also calculates active
623  *        bit count for Subnet Mask.
624  *
625  * @param[in]  ip     IP that will be verified
626  * @param[out] bits   Calculated mask in bits notation
627  *
628  * @return true in case of success, false otherwise
629  */
630 inline bool ipv4VerifyIpAndGetBitcount(const std::string& ip,
631                                        uint8_t* bits = nullptr)
632 {
633     std::vector<std::string> bytesInMask;
634 
635     boost::split(bytesInMask, ip, boost::is_any_of("."));
636 
637     static const constexpr int ipV4AddressSectionsCount = 4;
638     if (bytesInMask.size() != ipV4AddressSectionsCount)
639     {
640         return false;
641     }
642 
643     if (bits != nullptr)
644     {
645         *bits = 0;
646     }
647 
648     char* endPtr = nullptr;
649     long previousValue = 255;
650     bool firstZeroInByteHit = false;
651     for (const std::string& byte : bytesInMask)
652     {
653         if (byte.empty())
654         {
655             return false;
656         }
657 
658         // Use strtol instead of stroi to avoid exceptions
659         long value = std::strtol(byte.c_str(), &endPtr, 10);
660 
661         // endPtr should point to the end of the string, otherwise given string
662         // is not 100% number
663         if (*endPtr != '\0')
664         {
665             return false;
666         }
667 
668         // Value should be contained in byte
669         if (value < 0 || value > 255)
670         {
671             return false;
672         }
673 
674         if (bits != nullptr)
675         {
676             // Mask has to be continuous between bytes
677             if (previousValue != 255 && value != 0)
678             {
679                 return false;
680             }
681 
682             // Mask has to be continuous inside bytes
683             firstZeroInByteHit = false;
684 
685             // Count bits
686             for (long bitIdx = 7; bitIdx >= 0; bitIdx--)
687             {
688                 if ((value & (1L << bitIdx)) != 0)
689                 {
690                     if (firstZeroInByteHit)
691                     {
692                         // Continuity not preserved
693                         return false;
694                     }
695                     (*bits)++;
696                 }
697                 else
698                 {
699                     firstZeroInByteHit = true;
700                 }
701             }
702         }
703 
704         previousValue = value;
705     }
706 
707     return true;
708 }
709 
710 /**
711  * @brief Deletes given IPv4 interface
712  *
713  * @param[in] ifaceId     Id of interface whose IP should be deleted
714  * @param[in] ipHash      DBus Hash id of IP that should be deleted
715  * @param[io] asyncResp   Response object that will be returned to client
716  *
717  * @return None
718  */
719 inline void deleteIPv4(const std::string& ifaceId, const std::string& ipHash,
720                        const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
721 {
722     crow::connections::systemBus->async_method_call(
723         [asyncResp](const boost::system::error_code ec) {
724             if (ec)
725             {
726                 messages::internalError(asyncResp->res);
727             }
728         },
729         "xyz.openbmc_project.Network",
730         "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
731         "xyz.openbmc_project.Object.Delete", "Delete");
732 }
733 
734 inline void updateIPv4DefaultGateway(
735     const std::string& ifaceId, const std::string& gateway,
736     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
737 {
738     crow::connections::systemBus->async_method_call(
739         [asyncResp](const boost::system::error_code ec) {
740             if (ec)
741             {
742                 messages::internalError(asyncResp->res);
743                 return;
744             }
745             asyncResp->res.result(boost::beast::http::status::no_content);
746         },
747         "xyz.openbmc_project.Network",
748         "/xyz/openbmc_project/network/" + ifaceId,
749         "org.freedesktop.DBus.Properties", "Set",
750         "xyz.openbmc_project.Network.EthernetInterface", "DefaultGateway",
751         dbus::utility::DbusVariantType(gateway));
752 }
753 /**
754  * @brief Creates a static IPv4 entry
755  *
756  * @param[in] ifaceId      Id of interface upon which to create the IPv4 entry
757  * @param[in] prefixLength IPv4 prefix syntax for the subnet mask
758  * @param[in] gateway      IPv4 address of this interfaces gateway
759  * @param[in] address      IPv4 address to assign to this interface
760  * @param[io] asyncResp    Response object that will be returned to client
761  *
762  * @return None
763  */
764 inline void createIPv4(const std::string& ifaceId, uint8_t prefixLength,
765                        const std::string& gateway, const std::string& address,
766                        const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
767 {
768     auto createIpHandler = [asyncResp, ifaceId,
769                             gateway](const boost::system::error_code ec) {
770         if (ec)
771         {
772             messages::internalError(asyncResp->res);
773             return;
774         }
775         updateIPv4DefaultGateway(ifaceId, gateway, asyncResp);
776     };
777 
778     crow::connections::systemBus->async_method_call(
779         std::move(createIpHandler), "xyz.openbmc_project.Network",
780         "/xyz/openbmc_project/network/" + ifaceId,
781         "xyz.openbmc_project.Network.IP.Create", "IP",
782         "xyz.openbmc_project.Network.IP.Protocol.IPv4", address, prefixLength,
783         gateway);
784 }
785 
786 /**
787  * @brief Deletes the IPv4 entry for this interface and creates a replacement
788  * static IPv4 entry
789  *
790  * @param[in] ifaceId      Id of interface upon which to create the IPv4 entry
791  * @param[in] id           The unique hash entry identifying the DBus entry
792  * @param[in] prefixLength IPv4 prefix syntax for the subnet mask
793  * @param[in] gateway      IPv4 address of this interfaces gateway
794  * @param[in] address      IPv4 address to assign to this interface
795  * @param[io] asyncResp    Response object that will be returned to client
796  *
797  * @return None
798  */
799 inline void
800     deleteAndCreateIPv4(const std::string& ifaceId, const std::string& id,
801                         uint8_t prefixLength, const std::string& gateway,
802                         const std::string& address,
803                         const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
804 {
805     crow::connections::systemBus->async_method_call(
806         [asyncResp, ifaceId, address, prefixLength,
807          gateway](const boost::system::error_code ec) {
808             if (ec)
809             {
810                 messages::internalError(asyncResp->res);
811                 return;
812             }
813 
814             crow::connections::systemBus->async_method_call(
815                 [asyncResp, ifaceId,
816                  gateway](const boost::system::error_code ec2) {
817                     if (ec2)
818                     {
819                         messages::internalError(asyncResp->res);
820                         return;
821                     }
822                     updateIPv4DefaultGateway(ifaceId, gateway, asyncResp);
823                 },
824                 "xyz.openbmc_project.Network",
825                 "/xyz/openbmc_project/network/" + ifaceId,
826                 "xyz.openbmc_project.Network.IP.Create", "IP",
827                 "xyz.openbmc_project.Network.IP.Protocol.IPv4", address,
828                 prefixLength, gateway);
829         },
830         "xyz.openbmc_project.Network",
831         +"/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + id,
832         "xyz.openbmc_project.Object.Delete", "Delete");
833 }
834 
835 /**
836  * @brief Deletes given IPv6
837  *
838  * @param[in] ifaceId     Id of interface whose IP should be deleted
839  * @param[in] ipHash      DBus Hash id of IP that should be deleted
840  * @param[io] asyncResp   Response object that will be returned to client
841  *
842  * @return None
843  */
844 inline void deleteIPv6(const std::string& ifaceId, const std::string& ipHash,
845                        const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
846 {
847     crow::connections::systemBus->async_method_call(
848         [asyncResp](const boost::system::error_code ec) {
849             if (ec)
850             {
851                 messages::internalError(asyncResp->res);
852             }
853         },
854         "xyz.openbmc_project.Network",
855         "/xyz/openbmc_project/network/" + ifaceId + "/ipv6/" + ipHash,
856         "xyz.openbmc_project.Object.Delete", "Delete");
857 }
858 
859 /**
860  * @brief Deletes the IPv6 entry for this interface and creates a replacement
861  * static IPv6 entry
862  *
863  * @param[in] ifaceId      Id of interface upon which to create the IPv6 entry
864  * @param[in] id           The unique hash entry identifying the DBus entry
865  * @param[in] prefixLength IPv6 prefix syntax for the subnet mask
866  * @param[in] address      IPv6 address to assign to this interface
867  * @param[io] asyncResp    Response object that will be returned to client
868  *
869  * @return None
870  */
871 inline void
872     deleteAndCreateIPv6(const std::string& ifaceId, const std::string& id,
873                         uint8_t prefixLength, const std::string& address,
874                         const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
875 {
876     crow::connections::systemBus->async_method_call(
877         [asyncResp, ifaceId, address,
878          prefixLength](const boost::system::error_code ec) {
879             if (ec)
880             {
881                 messages::internalError(asyncResp->res);
882             }
883             crow::connections::systemBus->async_method_call(
884                 [asyncResp](const boost::system::error_code ec2) {
885                     if (ec2)
886                     {
887                         messages::internalError(asyncResp->res);
888                     }
889                 },
890                 "xyz.openbmc_project.Network",
891                 "/xyz/openbmc_project/network/" + ifaceId,
892                 "xyz.openbmc_project.Network.IP.Create", "IP",
893                 "xyz.openbmc_project.Network.IP.Protocol.IPv6", address,
894                 prefixLength, "");
895         },
896         "xyz.openbmc_project.Network",
897         +"/xyz/openbmc_project/network/" + ifaceId + "/ipv6/" + id,
898         "xyz.openbmc_project.Object.Delete", "Delete");
899 }
900 
901 /**
902  * @brief Creates IPv6 with given data
903  *
904  * @param[in] ifaceId      Id of interface whose IP should be added
905  * @param[in] prefixLength Prefix length that needs to be added
906  * @param[in] address      IP address that needs to be added
907  * @param[io] asyncResp    Response object that will be returned to client
908  *
909  * @return None
910  */
911 inline void createIPv6(const std::string& ifaceId, uint8_t prefixLength,
912                        const std::string& address,
913                        const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
914 {
915     auto createIpHandler = [asyncResp](const boost::system::error_code ec) {
916         if (ec)
917         {
918             messages::internalError(asyncResp->res);
919         }
920     };
921     // Passing null for gateway, as per redfish spec IPv6StaticAddresses object
922     // does not have associated gateway property
923     crow::connections::systemBus->async_method_call(
924         std::move(createIpHandler), "xyz.openbmc_project.Network",
925         "/xyz/openbmc_project/network/" + ifaceId,
926         "xyz.openbmc_project.Network.IP.Create", "IP",
927         "xyz.openbmc_project.Network.IP.Protocol.IPv6", address, prefixLength,
928         "");
929 }
930 
931 /**
932  * Function that retrieves all properties for given Ethernet Interface
933  * Object
934  * from EntityManager Network Manager
935  * @param ethiface_id a eth interface id to query on DBus
936  * @param callback a function that shall be called to convert Dbus output
937  * into JSON
938  */
939 template <typename CallbackFunc>
940 void getEthernetIfaceData(const std::string& ethifaceId,
941                           CallbackFunc&& callback)
942 {
943     crow::connections::systemBus->async_method_call(
944         [ethifaceId{std::string{ethifaceId}},
945          callback{std::forward<CallbackFunc>(callback)}](
946             const boost::system::error_code errorCode,
947             dbus::utility::ManagedObjectType& resp) {
948             EthernetInterfaceData ethData{};
949             boost::container::flat_set<IPv4AddressData> ipv4Data;
950             boost::container::flat_set<IPv6AddressData> ipv6Data;
951 
952             if (errorCode)
953             {
954                 callback(false, ethData, ipv4Data, ipv6Data);
955                 return;
956             }
957 
958             bool found =
959                 extractEthernetInterfaceData(ethifaceId, resp, ethData);
960             if (!found)
961             {
962                 callback(false, ethData, ipv4Data, ipv6Data);
963                 return;
964             }
965 
966             extractIPData(ethifaceId, resp, ipv4Data);
967             // Fix global GW
968             for (IPv4AddressData& ipv4 : ipv4Data)
969             {
970                 if (((ipv4.linktype == LinkType::Global) &&
971                      (ipv4.gateway == "0.0.0.0")) ||
972                     (ipv4.origin == "DHCP") || (ipv4.origin == "Static"))
973                 {
974                     ipv4.gateway = ethData.default_gateway;
975                 }
976             }
977 
978             extractIPV6Data(ethifaceId, resp, ipv6Data);
979             // Finally make a callback with useful data
980             callback(true, ethData, ipv4Data, ipv6Data);
981         },
982         "xyz.openbmc_project.Network", "/xyz/openbmc_project/network",
983         "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
984 }
985 
986 /**
987  * Function that retrieves all Ethernet Interfaces available through Network
988  * Manager
989  * @param callback a function that shall be called to convert Dbus output
990  * into JSON.
991  */
992 template <typename CallbackFunc>
993 void getEthernetIfaceList(CallbackFunc&& callback)
994 {
995     crow::connections::systemBus->async_method_call(
996         [callback{std::forward<CallbackFunc>(callback)}](
997             const boost::system::error_code errorCode,
998             dbus::utility::ManagedObjectType& resp) {
999             // Callback requires vector<string> to retrieve all available
1000             // ethernet interfaces
1001             boost::container::flat_set<std::string> ifaceList;
1002             ifaceList.reserve(resp.size());
1003             if (errorCode)
1004             {
1005                 callback(false, ifaceList);
1006                 return;
1007             }
1008 
1009             // Iterate over all retrieved ObjectPaths.
1010             for (const auto& objpath : resp)
1011             {
1012                 // And all interfaces available for certain ObjectPath.
1013                 for (const auto& interface : objpath.second)
1014                 {
1015                     // If interface is
1016                     // xyz.openbmc_project.Network.EthernetInterface, this is
1017                     // what we're looking for.
1018                     if (interface.first ==
1019                         "xyz.openbmc_project.Network.EthernetInterface")
1020                     {
1021                         std::string ifaceId = objpath.first.filename();
1022                         if (ifaceId.empty())
1023                         {
1024                             continue;
1025                         }
1026                         // and put it into output vector.
1027                         ifaceList.emplace(ifaceId);
1028                     }
1029                 }
1030             }
1031             // Finally make a callback with useful data
1032             callback(true, ifaceList);
1033         },
1034         "xyz.openbmc_project.Network", "/xyz/openbmc_project/network",
1035         "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1036 }
1037 
1038 inline void
1039     handleHostnamePatch(const std::string& hostname,
1040                         const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1041 {
1042     // SHOULD handle host names of up to 255 characters(RFC 1123)
1043     if (hostname.length() > 255)
1044     {
1045         messages::propertyValueFormatError(asyncResp->res, hostname,
1046                                            "HostName");
1047         return;
1048     }
1049     crow::connections::systemBus->async_method_call(
1050         [asyncResp](const boost::system::error_code ec) {
1051             if (ec)
1052             {
1053                 messages::internalError(asyncResp->res);
1054             }
1055         },
1056         "xyz.openbmc_project.Network", "/xyz/openbmc_project/network/config",
1057         "org.freedesktop.DBus.Properties", "Set",
1058         "xyz.openbmc_project.Network.SystemConfiguration", "HostName",
1059         dbus::utility::DbusVariantType(hostname));
1060 }
1061 
1062 inline void
1063     handleMTUSizePatch(const std::string& ifaceId, const size_t mtuSize,
1064                        const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1065 {
1066     sdbusplus::message::object_path objPath =
1067         "/xyz/openbmc_project/network/" + ifaceId;
1068     crow::connections::systemBus->async_method_call(
1069         [asyncResp](const boost::system::error_code ec) {
1070             if (ec)
1071             {
1072                 messages::internalError(asyncResp->res);
1073             }
1074         },
1075         "xyz.openbmc_project.Network", objPath,
1076         "org.freedesktop.DBus.Properties", "Set",
1077         "xyz.openbmc_project.Network.EthernetInterface", "MTU",
1078         std::variant<size_t>(mtuSize));
1079 }
1080 
1081 inline void
1082     handleDomainnamePatch(const std::string& ifaceId,
1083                           const std::string& domainname,
1084                           const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1085 {
1086     std::vector<std::string> vectorDomainname = {domainname};
1087     crow::connections::systemBus->async_method_call(
1088         [asyncResp](const boost::system::error_code ec) {
1089             if (ec)
1090             {
1091                 messages::internalError(asyncResp->res);
1092             }
1093         },
1094         "xyz.openbmc_project.Network",
1095         "/xyz/openbmc_project/network/" + ifaceId,
1096         "org.freedesktop.DBus.Properties", "Set",
1097         "xyz.openbmc_project.Network.EthernetInterface", "DomainName",
1098         dbus::utility::DbusVariantType(vectorDomainname));
1099 }
1100 
1101 inline bool isHostnameValid(const std::string& hostname)
1102 {
1103     // A valid host name can never have the dotted-decimal form (RFC 1123)
1104     if (std::all_of(hostname.begin(), hostname.end(), ::isdigit))
1105     {
1106         return false;
1107     }
1108     // Each label(hostname/subdomains) within a valid FQDN
1109     // MUST handle host names of up to 63 characters (RFC 1123)
1110     // labels cannot start or end with hyphens (RFC 952)
1111     // labels can start with numbers (RFC 1123)
1112     const std::regex pattern(
1113         "^[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9]$");
1114 
1115     return std::regex_match(hostname, pattern);
1116 }
1117 
1118 inline bool isDomainnameValid(const std::string& domainname)
1119 {
1120     // Can have multiple subdomains
1121     // Top Level Domain's min length is 2 character
1122     const std::regex pattern(
1123         "^([A-Za-z0-9][a-zA-Z0-9\\-]{1,61}|[a-zA-Z0-9]{1,30}\\.)*[a-zA-Z]{2,}$");
1124 
1125     return std::regex_match(domainname, pattern);
1126 }
1127 
1128 inline void handleFqdnPatch(const std::string& ifaceId, const std::string& fqdn,
1129                             const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1130 {
1131     // Total length of FQDN must not exceed 255 characters(RFC 1035)
1132     if (fqdn.length() > 255)
1133     {
1134         messages::propertyValueFormatError(asyncResp->res, fqdn, "FQDN");
1135         return;
1136     }
1137 
1138     size_t pos = fqdn.find('.');
1139     if (pos == std::string::npos)
1140     {
1141         messages::propertyValueFormatError(asyncResp->res, fqdn, "FQDN");
1142         return;
1143     }
1144 
1145     std::string hostname;
1146     std::string domainname;
1147     domainname = (fqdn).substr(pos + 1);
1148     hostname = (fqdn).substr(0, pos);
1149 
1150     if (!isHostnameValid(hostname) || !isDomainnameValid(domainname))
1151     {
1152         messages::propertyValueFormatError(asyncResp->res, fqdn, "FQDN");
1153         return;
1154     }
1155 
1156     handleHostnamePatch(hostname, asyncResp);
1157     handleDomainnamePatch(ifaceId, domainname, asyncResp);
1158 }
1159 
1160 inline void
1161     handleMACAddressPatch(const std::string& ifaceId,
1162                           const std::string& macAddress,
1163                           const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1164 {
1165     crow::connections::systemBus->async_method_call(
1166         [asyncResp, macAddress](const boost::system::error_code ec) {
1167             if (ec)
1168             {
1169                 messages::internalError(asyncResp->res);
1170                 return;
1171             }
1172         },
1173         "xyz.openbmc_project.Network",
1174         "/xyz/openbmc_project/network/" + ifaceId,
1175         "org.freedesktop.DBus.Properties", "Set",
1176         "xyz.openbmc_project.Network.MACAddress", "MACAddress",
1177         dbus::utility::DbusVariantType(macAddress));
1178 }
1179 
1180 inline void setDHCPEnabled(const std::string& ifaceId,
1181                            const std::string& propertyName, const bool v4Value,
1182                            const bool v6Value,
1183                            const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1184 {
1185     const std::string dhcp = getDhcpEnabledEnumeration(v4Value, v6Value);
1186     crow::connections::systemBus->async_method_call(
1187         [asyncResp](const boost::system::error_code ec) {
1188             if (ec)
1189             {
1190                 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
1191                 messages::internalError(asyncResp->res);
1192                 return;
1193             }
1194             messages::success(asyncResp->res);
1195         },
1196         "xyz.openbmc_project.Network",
1197         "/xyz/openbmc_project/network/" + ifaceId,
1198         "org.freedesktop.DBus.Properties", "Set",
1199         "xyz.openbmc_project.Network.EthernetInterface", propertyName,
1200         dbus::utility::DbusVariantType{dhcp});
1201 }
1202 
1203 inline void setEthernetInterfaceBoolProperty(
1204     const std::string& ifaceId, const std::string& propertyName,
1205     const bool& value, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1206 {
1207     crow::connections::systemBus->async_method_call(
1208         [asyncResp](const boost::system::error_code ec) {
1209             if (ec)
1210             {
1211                 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
1212                 messages::internalError(asyncResp->res);
1213                 return;
1214             }
1215         },
1216         "xyz.openbmc_project.Network",
1217         "/xyz/openbmc_project/network/" + ifaceId,
1218         "org.freedesktop.DBus.Properties", "Set",
1219         "xyz.openbmc_project.Network.EthernetInterface", propertyName,
1220         dbus::utility::DbusVariantType{value});
1221 }
1222 
1223 inline void setDHCPv4Config(const std::string& propertyName, const bool& value,
1224                             const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1225 {
1226     BMCWEB_LOG_DEBUG << propertyName << " = " << value;
1227     crow::connections::systemBus->async_method_call(
1228         [asyncResp](const boost::system::error_code ec) {
1229             if (ec)
1230             {
1231                 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
1232                 messages::internalError(asyncResp->res);
1233                 return;
1234             }
1235         },
1236         "xyz.openbmc_project.Network",
1237         "/xyz/openbmc_project/network/config/dhcp",
1238         "org.freedesktop.DBus.Properties", "Set",
1239         "xyz.openbmc_project.Network.DHCPConfiguration", propertyName,
1240         dbus::utility::DbusVariantType{value});
1241 }
1242 
1243 inline void handleDHCPPatch(const std::string& ifaceId,
1244                             const EthernetInterfaceData& ethData,
1245                             const DHCPParameters& v4dhcpParms,
1246                             const DHCPParameters& v6dhcpParms,
1247                             const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1248 {
1249     bool ipv4Active = translateDHCPEnabledToBool(ethData.DHCPEnabled, true);
1250     bool ipv6Active = translateDHCPEnabledToBool(ethData.DHCPEnabled, false);
1251 
1252     bool nextv4DHCPState =
1253         v4dhcpParms.dhcpv4Enabled ? *v4dhcpParms.dhcpv4Enabled : ipv4Active;
1254 
1255     bool nextv6DHCPState{};
1256     if (v6dhcpParms.dhcpv6OperatingMode)
1257     {
1258         if ((*v6dhcpParms.dhcpv6OperatingMode != "Stateful") &&
1259             (*v6dhcpParms.dhcpv6OperatingMode != "Stateless") &&
1260             (*v6dhcpParms.dhcpv6OperatingMode != "Disabled"))
1261         {
1262             messages::propertyValueFormatError(asyncResp->res,
1263                                                *v6dhcpParms.dhcpv6OperatingMode,
1264                                                "OperatingMode");
1265             return;
1266         }
1267         nextv6DHCPState = (*v6dhcpParms.dhcpv6OperatingMode == "Stateful");
1268     }
1269     else
1270     {
1271         nextv6DHCPState = ipv6Active;
1272     }
1273 
1274     bool nextDNS{};
1275     if (v4dhcpParms.useDNSServers && v6dhcpParms.useDNSServers)
1276     {
1277         if (*v4dhcpParms.useDNSServers != *v6dhcpParms.useDNSServers)
1278         {
1279             messages::generalError(asyncResp->res);
1280             return;
1281         }
1282         nextDNS = *v4dhcpParms.useDNSServers;
1283     }
1284     else if (v4dhcpParms.useDNSServers)
1285     {
1286         nextDNS = *v4dhcpParms.useDNSServers;
1287     }
1288     else if (v6dhcpParms.useDNSServers)
1289     {
1290         nextDNS = *v6dhcpParms.useDNSServers;
1291     }
1292     else
1293     {
1294         nextDNS = ethData.DNSEnabled;
1295     }
1296 
1297     bool nextNTP{};
1298     if (v4dhcpParms.useNTPServers && v6dhcpParms.useNTPServers)
1299     {
1300         if (*v4dhcpParms.useNTPServers != *v6dhcpParms.useNTPServers)
1301         {
1302             messages::generalError(asyncResp->res);
1303             return;
1304         }
1305         nextNTP = *v4dhcpParms.useNTPServers;
1306     }
1307     else if (v4dhcpParms.useNTPServers)
1308     {
1309         nextNTP = *v4dhcpParms.useNTPServers;
1310     }
1311     else if (v6dhcpParms.useNTPServers)
1312     {
1313         nextNTP = *v6dhcpParms.useNTPServers;
1314     }
1315     else
1316     {
1317         nextNTP = ethData.NTPEnabled;
1318     }
1319 
1320     bool nextUseDomain{};
1321     if (v4dhcpParms.useUseDomainName && v6dhcpParms.useUseDomainName)
1322     {
1323         if (*v4dhcpParms.useUseDomainName != *v6dhcpParms.useUseDomainName)
1324         {
1325             messages::generalError(asyncResp->res);
1326             return;
1327         }
1328         nextUseDomain = *v4dhcpParms.useUseDomainName;
1329     }
1330     else if (v4dhcpParms.useUseDomainName)
1331     {
1332         nextUseDomain = *v4dhcpParms.useUseDomainName;
1333     }
1334     else if (v6dhcpParms.useUseDomainName)
1335     {
1336         nextUseDomain = *v6dhcpParms.useUseDomainName;
1337     }
1338     else
1339     {
1340         nextUseDomain = ethData.HostNameEnabled;
1341     }
1342 
1343     BMCWEB_LOG_DEBUG << "set DHCPEnabled...";
1344     setDHCPEnabled(ifaceId, "DHCPEnabled", nextv4DHCPState, nextv6DHCPState,
1345                    asyncResp);
1346     BMCWEB_LOG_DEBUG << "set DNSEnabled...";
1347     setDHCPv4Config("DNSEnabled", nextDNS, asyncResp);
1348     BMCWEB_LOG_DEBUG << "set NTPEnabled...";
1349     setDHCPv4Config("NTPEnabled", nextNTP, asyncResp);
1350     BMCWEB_LOG_DEBUG << "set HostNameEnabled...";
1351     setDHCPv4Config("HostNameEnabled", nextUseDomain, asyncResp);
1352 }
1353 
1354 inline boost::container::flat_set<IPv4AddressData>::const_iterator
1355     getNextStaticIpEntry(
1356         const boost::container::flat_set<IPv4AddressData>::const_iterator& head,
1357         const boost::container::flat_set<IPv4AddressData>::const_iterator& end)
1358 {
1359     return std::find_if(head, end, [](const IPv4AddressData& value) {
1360         return value.origin == "Static";
1361     });
1362 }
1363 
1364 inline boost::container::flat_set<IPv6AddressData>::const_iterator
1365     getNextStaticIpEntry(
1366         const boost::container::flat_set<IPv6AddressData>::const_iterator& head,
1367         const boost::container::flat_set<IPv6AddressData>::const_iterator& end)
1368 {
1369     return std::find_if(head, end, [](const IPv6AddressData& value) {
1370         return value.origin == "Static";
1371     });
1372 }
1373 
1374 inline void handleIPv4StaticPatch(
1375     const std::string& ifaceId, nlohmann::json& input,
1376     const boost::container::flat_set<IPv4AddressData>& ipv4Data,
1377     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1378 {
1379     if ((!input.is_array()) || input.empty())
1380     {
1381         messages::propertyValueTypeError(
1382             asyncResp->res,
1383             input.dump(2, ' ', true, nlohmann::json::error_handler_t::replace),
1384             "IPv4StaticAddresses");
1385         return;
1386     }
1387 
1388     unsigned entryIdx = 1;
1389     // Find the first static IP address currently active on the NIC and
1390     // match it to the first JSON element in the IPv4StaticAddresses array.
1391     // Match each subsequent JSON element to the next static IP programmed
1392     // into the NIC.
1393     boost::container::flat_set<IPv4AddressData>::const_iterator nicIpEntry =
1394         getNextStaticIpEntry(ipv4Data.cbegin(), ipv4Data.cend());
1395 
1396     for (nlohmann::json& thisJson : input)
1397     {
1398         std::string pathString =
1399             "IPv4StaticAddresses/" + std::to_string(entryIdx);
1400 
1401         if (!thisJson.is_null() && !thisJson.empty())
1402         {
1403             std::optional<std::string> address;
1404             std::optional<std::string> subnetMask;
1405             std::optional<std::string> gateway;
1406 
1407             if (!json_util::readJson(thisJson, asyncResp->res, "Address",
1408                                      address, "SubnetMask", subnetMask,
1409                                      "Gateway", gateway))
1410             {
1411                 messages::propertyValueFormatError(
1412                     asyncResp->res,
1413                     thisJson.dump(2, ' ', true,
1414                                   nlohmann::json::error_handler_t::replace),
1415                     pathString);
1416                 return;
1417             }
1418 
1419             // Find the address/subnet/gateway values. Any values that are
1420             // not explicitly provided are assumed to be unmodified from the
1421             // current state of the interface. Merge existing state into the
1422             // current request.
1423             const std::string* addr = nullptr;
1424             const std::string* gw = nullptr;
1425             uint8_t prefixLength = 0;
1426             bool errorInEntry = false;
1427             if (address)
1428             {
1429                 if (ipv4VerifyIpAndGetBitcount(*address))
1430                 {
1431                     addr = &(*address);
1432                 }
1433                 else
1434                 {
1435                     messages::propertyValueFormatError(asyncResp->res, *address,
1436                                                        pathString + "/Address");
1437                     errorInEntry = true;
1438                 }
1439             }
1440             else if (nicIpEntry != ipv4Data.cend())
1441             {
1442                 addr = &(nicIpEntry->address);
1443             }
1444             else
1445             {
1446                 messages::propertyMissing(asyncResp->res,
1447                                           pathString + "/Address");
1448                 errorInEntry = true;
1449             }
1450 
1451             if (subnetMask)
1452             {
1453                 if (!ipv4VerifyIpAndGetBitcount(*subnetMask, &prefixLength))
1454                 {
1455                     messages::propertyValueFormatError(
1456                         asyncResp->res, *subnetMask,
1457                         pathString + "/SubnetMask");
1458                     errorInEntry = true;
1459                 }
1460             }
1461             else if (nicIpEntry != ipv4Data.cend())
1462             {
1463                 if (!ipv4VerifyIpAndGetBitcount(nicIpEntry->netmask,
1464                                                 &prefixLength))
1465                 {
1466                     messages::propertyValueFormatError(
1467                         asyncResp->res, nicIpEntry->netmask,
1468                         pathString + "/SubnetMask");
1469                     errorInEntry = true;
1470                 }
1471             }
1472             else
1473             {
1474                 messages::propertyMissing(asyncResp->res,
1475                                           pathString + "/SubnetMask");
1476                 errorInEntry = true;
1477             }
1478 
1479             if (gateway)
1480             {
1481                 if (ipv4VerifyIpAndGetBitcount(*gateway))
1482                 {
1483                     gw = &(*gateway);
1484                 }
1485                 else
1486                 {
1487                     messages::propertyValueFormatError(asyncResp->res, *gateway,
1488                                                        pathString + "/Gateway");
1489                     errorInEntry = true;
1490                 }
1491             }
1492             else if (nicIpEntry != ipv4Data.cend())
1493             {
1494                 gw = &nicIpEntry->gateway;
1495             }
1496             else
1497             {
1498                 messages::propertyMissing(asyncResp->res,
1499                                           pathString + "/Gateway");
1500                 errorInEntry = true;
1501             }
1502 
1503             if (errorInEntry)
1504             {
1505                 return;
1506             }
1507 
1508             if (nicIpEntry != ipv4Data.cend())
1509             {
1510                 deleteAndCreateIPv4(ifaceId, nicIpEntry->id, prefixLength, *gw,
1511                                     *addr, asyncResp);
1512                 nicIpEntry =
1513                     getNextStaticIpEntry(++nicIpEntry, ipv4Data.cend());
1514             }
1515             else
1516             {
1517                 createIPv4(ifaceId, prefixLength, *gateway, *address,
1518                            asyncResp);
1519             }
1520             entryIdx++;
1521         }
1522         else
1523         {
1524             if (nicIpEntry == ipv4Data.cend())
1525             {
1526                 // Requesting a DELETE/DO NOT MODIFY action for an item
1527                 // that isn't present on the eth(n) interface. Input JSON is
1528                 // in error, so bail out.
1529                 if (thisJson.is_null())
1530                 {
1531                     messages::resourceCannotBeDeleted(asyncResp->res);
1532                     return;
1533                 }
1534                 messages::propertyValueFormatError(
1535                     asyncResp->res,
1536                     thisJson.dump(2, ' ', true,
1537                                   nlohmann::json::error_handler_t::replace),
1538                     pathString);
1539                 return;
1540             }
1541 
1542             if (thisJson.is_null())
1543             {
1544                 deleteIPv4(ifaceId, nicIpEntry->id, asyncResp);
1545             }
1546             if (nicIpEntry != ipv4Data.cend())
1547             {
1548                 nicIpEntry =
1549                     getNextStaticIpEntry(++nicIpEntry, ipv4Data.cend());
1550             }
1551             entryIdx++;
1552         }
1553     }
1554 }
1555 
1556 inline void handleStaticNameServersPatch(
1557     const std::string& ifaceId,
1558     const std::vector<std::string>& updatedStaticNameServers,
1559     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1560 {
1561     crow::connections::systemBus->async_method_call(
1562         [asyncResp](const boost::system::error_code ec) {
1563             if (ec)
1564             {
1565                 messages::internalError(asyncResp->res);
1566                 return;
1567             }
1568         },
1569         "xyz.openbmc_project.Network",
1570         "/xyz/openbmc_project/network/" + ifaceId,
1571         "org.freedesktop.DBus.Properties", "Set",
1572         "xyz.openbmc_project.Network.EthernetInterface", "StaticNameServers",
1573         dbus::utility::DbusVariantType{updatedStaticNameServers});
1574 }
1575 
1576 inline void handleIPv6StaticAddressesPatch(
1577     const std::string& ifaceId, const nlohmann::json& input,
1578     const boost::container::flat_set<IPv6AddressData>& ipv6Data,
1579     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1580 {
1581     if (!input.is_array() || input.empty())
1582     {
1583         messages::propertyValueTypeError(
1584             asyncResp->res,
1585             input.dump(2, ' ', true, nlohmann::json::error_handler_t::replace),
1586             "IPv6StaticAddresses");
1587         return;
1588     }
1589     size_t entryIdx = 1;
1590     boost::container::flat_set<IPv6AddressData>::const_iterator nicIpEntry =
1591         getNextStaticIpEntry(ipv6Data.cbegin(), ipv6Data.cend());
1592     for (const nlohmann::json& thisJson : input)
1593     {
1594         std::string pathString =
1595             "IPv6StaticAddresses/" + std::to_string(entryIdx);
1596 
1597         if (!thisJson.is_null() && !thisJson.empty())
1598         {
1599             std::optional<std::string> address;
1600             std::optional<uint8_t> prefixLength;
1601             nlohmann::json thisJsonCopy = thisJson;
1602             if (!json_util::readJson(thisJsonCopy, asyncResp->res, "Address",
1603                                      address, "PrefixLength", prefixLength))
1604             {
1605                 messages::propertyValueFormatError(
1606                     asyncResp->res,
1607                     thisJson.dump(2, ' ', true,
1608                                   nlohmann::json::error_handler_t::replace),
1609                     pathString);
1610                 return;
1611             }
1612 
1613             const std::string* addr = nullptr;
1614             uint8_t prefix = 0;
1615 
1616             // Find the address and prefixLength values. Any values that are
1617             // not explicitly provided are assumed to be unmodified from the
1618             // current state of the interface. Merge existing state into the
1619             // current request.
1620             if (address)
1621             {
1622                 addr = &(*address);
1623             }
1624             else if (nicIpEntry != ipv6Data.end())
1625             {
1626                 addr = &(nicIpEntry->address);
1627             }
1628             else
1629             {
1630                 messages::propertyMissing(asyncResp->res,
1631                                           pathString + "/Address");
1632                 return;
1633             }
1634 
1635             if (prefixLength)
1636             {
1637                 prefix = *prefixLength;
1638             }
1639             else if (nicIpEntry != ipv6Data.end())
1640             {
1641                 prefix = nicIpEntry->prefixLength;
1642             }
1643             else
1644             {
1645                 messages::propertyMissing(asyncResp->res,
1646                                           pathString + "/PrefixLength");
1647                 return;
1648             }
1649 
1650             if (nicIpEntry != ipv6Data.end())
1651             {
1652                 deleteAndCreateIPv6(ifaceId, nicIpEntry->id, prefix, *addr,
1653                                     asyncResp);
1654                 nicIpEntry =
1655                     getNextStaticIpEntry(++nicIpEntry, ipv6Data.cend());
1656             }
1657             else
1658             {
1659                 createIPv6(ifaceId, *prefixLength, *addr, asyncResp);
1660             }
1661             entryIdx++;
1662         }
1663         else
1664         {
1665             if (nicIpEntry == ipv6Data.end())
1666             {
1667                 // Requesting a DELETE/DO NOT MODIFY action for an item
1668                 // that isn't present on the eth(n) interface. Input JSON is
1669                 // in error, so bail out.
1670                 if (thisJson.is_null())
1671                 {
1672                     messages::resourceCannotBeDeleted(asyncResp->res);
1673                     return;
1674                 }
1675                 messages::propertyValueFormatError(
1676                     asyncResp->res,
1677                     thisJson.dump(2, ' ', true,
1678                                   nlohmann::json::error_handler_t::replace),
1679                     pathString);
1680                 return;
1681             }
1682 
1683             if (thisJson.is_null())
1684             {
1685                 deleteIPv6(ifaceId, nicIpEntry->id, asyncResp);
1686             }
1687             if (nicIpEntry != ipv6Data.cend())
1688             {
1689                 nicIpEntry =
1690                     getNextStaticIpEntry(++nicIpEntry, ipv6Data.cend());
1691             }
1692             entryIdx++;
1693         }
1694     }
1695 }
1696 
1697 inline void parseInterfaceData(
1698     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1699     const std::string& ifaceId, const EthernetInterfaceData& ethData,
1700     const boost::container::flat_set<IPv4AddressData>& ipv4Data,
1701     const boost::container::flat_set<IPv6AddressData>& ipv6Data)
1702 {
1703     constexpr const std::array<const char*, 1> inventoryForEthernet = {
1704         "xyz.openbmc_project.Inventory.Item.Ethernet"};
1705 
1706     nlohmann::json& jsonResponse = asyncResp->res.jsonValue;
1707     jsonResponse["Id"] = ifaceId;
1708     jsonResponse["@odata.id"] =
1709         "/redfish/v1/Managers/bmc/EthernetInterfaces/" + ifaceId;
1710     jsonResponse["InterfaceEnabled"] = ethData.nicEnabled;
1711 
1712     auto health = std::make_shared<HealthPopulate>(asyncResp);
1713 
1714     crow::connections::systemBus->async_method_call(
1715         [health](const boost::system::error_code ec,
1716                  const std::vector<std::string>& resp) {
1717             if (ec)
1718             {
1719                 return;
1720             }
1721 
1722             health->inventory = resp;
1723         },
1724         "xyz.openbmc_project.ObjectMapper",
1725         "/xyz/openbmc_project/object_mapper",
1726         "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "/", int32_t(0),
1727         inventoryForEthernet);
1728 
1729     health->populate();
1730 
1731     if (ethData.nicEnabled)
1732     {
1733         jsonResponse["LinkStatus"] = "LinkUp";
1734         jsonResponse["Status"]["State"] = "Enabled";
1735     }
1736     else
1737     {
1738         jsonResponse["LinkStatus"] = "NoLink";
1739         jsonResponse["Status"]["State"] = "Disabled";
1740     }
1741 
1742     jsonResponse["LinkStatus"] = ethData.linkUp ? "LinkUp" : "LinkDown";
1743     jsonResponse["SpeedMbps"] = ethData.speed;
1744     jsonResponse["MTUSize"] = ethData.mtuSize;
1745     jsonResponse["MACAddress"] = ethData.mac_address;
1746     jsonResponse["DHCPv4"]["DHCPEnabled"] =
1747         translateDHCPEnabledToBool(ethData.DHCPEnabled, true);
1748     jsonResponse["DHCPv4"]["UseNTPServers"] = ethData.NTPEnabled;
1749     jsonResponse["DHCPv4"]["UseDNSServers"] = ethData.DNSEnabled;
1750     jsonResponse["DHCPv4"]["UseDomainName"] = ethData.HostNameEnabled;
1751 
1752     jsonResponse["DHCPv6"]["OperatingMode"] =
1753         translateDHCPEnabledToBool(ethData.DHCPEnabled, false) ? "Stateful"
1754                                                                : "Disabled";
1755     jsonResponse["DHCPv6"]["UseNTPServers"] = ethData.NTPEnabled;
1756     jsonResponse["DHCPv6"]["UseDNSServers"] = ethData.DNSEnabled;
1757     jsonResponse["DHCPv6"]["UseDomainName"] = ethData.HostNameEnabled;
1758 
1759     if (!ethData.hostname.empty())
1760     {
1761         jsonResponse["HostName"] = ethData.hostname;
1762 
1763         // When domain name is empty then it means, that it is a network
1764         // without domain names, and the host name itself must be treated as
1765         // FQDN
1766         std::string fqdn = ethData.hostname;
1767         if (!ethData.domainnames.empty())
1768         {
1769             fqdn += "." + ethData.domainnames[0];
1770         }
1771         jsonResponse["FQDN"] = fqdn;
1772     }
1773 
1774     jsonResponse["VLANs"] = {
1775         {"@odata.id",
1776          "/redfish/v1/Managers/bmc/EthernetInterfaces/" + ifaceId + "/VLANs"}};
1777 
1778     jsonResponse["NameServers"] = ethData.nameServers;
1779     jsonResponse["StaticNameServers"] = ethData.staticNameServers;
1780 
1781     nlohmann::json& ipv4Array = jsonResponse["IPv4Addresses"];
1782     nlohmann::json& ipv4StaticArray = jsonResponse["IPv4StaticAddresses"];
1783     ipv4Array = nlohmann::json::array();
1784     ipv4StaticArray = nlohmann::json::array();
1785     for (const auto& ipv4Config : ipv4Data)
1786     {
1787 
1788         std::string gatewayStr = ipv4Config.gateway;
1789         if (gatewayStr.empty())
1790         {
1791             gatewayStr = "0.0.0.0";
1792         }
1793 
1794         ipv4Array.push_back({{"AddressOrigin", ipv4Config.origin},
1795                              {"SubnetMask", ipv4Config.netmask},
1796                              {"Address", ipv4Config.address},
1797                              {"Gateway", gatewayStr}});
1798         if (ipv4Config.origin == "Static")
1799         {
1800             ipv4StaticArray.push_back({{"AddressOrigin", ipv4Config.origin},
1801                                        {"SubnetMask", ipv4Config.netmask},
1802                                        {"Address", ipv4Config.address},
1803                                        {"Gateway", gatewayStr}});
1804         }
1805     }
1806 
1807     std::string ipv6GatewayStr = ethData.ipv6_default_gateway;
1808     if (ipv6GatewayStr.empty())
1809     {
1810         ipv6GatewayStr = "0:0:0:0:0:0:0:0";
1811     }
1812 
1813     jsonResponse["IPv6DefaultGateway"] = ipv6GatewayStr;
1814 
1815     nlohmann::json& ipv6Array = jsonResponse["IPv6Addresses"];
1816     nlohmann::json& ipv6StaticArray = jsonResponse["IPv6StaticAddresses"];
1817     ipv6Array = nlohmann::json::array();
1818     ipv6StaticArray = nlohmann::json::array();
1819     nlohmann::json& ipv6AddrPolicyTable =
1820         jsonResponse["IPv6AddressPolicyTable"];
1821     ipv6AddrPolicyTable = nlohmann::json::array();
1822     for (const auto& ipv6Config : ipv6Data)
1823     {
1824         ipv6Array.push_back({{"Address", ipv6Config.address},
1825                              {"PrefixLength", ipv6Config.prefixLength},
1826                              {"AddressOrigin", ipv6Config.origin},
1827                              {"AddressState", nullptr}});
1828         if (ipv6Config.origin == "Static")
1829         {
1830             ipv6StaticArray.push_back(
1831                 {{"Address", ipv6Config.address},
1832                  {"PrefixLength", ipv6Config.prefixLength}});
1833         }
1834     }
1835 }
1836 
1837 inline void parseInterfaceData(nlohmann::json& jsonResponse,
1838                                const std::string& parentIfaceId,
1839                                const std::string& ifaceId,
1840                                const EthernetInterfaceData& ethData)
1841 {
1842     // Fill out obvious data...
1843     jsonResponse["Id"] = ifaceId;
1844     jsonResponse["@odata.id"] = "/redfish/v1/Managers/bmc/EthernetInterfaces/" +
1845                                 parentIfaceId + "/VLANs/" + ifaceId;
1846 
1847     jsonResponse["VLANEnable"] = true;
1848     if (!ethData.vlan_id.empty())
1849     {
1850         jsonResponse["VLANId"] = ethData.vlan_id.back();
1851     }
1852 }
1853 
1854 inline bool verifyNames(const std::string& parent, const std::string& iface)
1855 {
1856     return boost::starts_with(iface, parent + "_");
1857 }
1858 
1859 inline void requestEthernetInterfacesRoutes(App& app)
1860 {
1861     BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/EthernetInterfaces/")
1862         .privileges(redfish::privileges::getEthernetInterfaceCollection)
1863         .methods(
1864             boost::beast::http::verb::
1865                 get)([](const crow::Request&,
1866                         const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1867             asyncResp->res.jsonValue["@odata.type"] =
1868                 "#EthernetInterfaceCollection.EthernetInterfaceCollection";
1869             asyncResp->res.jsonValue["@odata.id"] =
1870                 "/redfish/v1/Managers/bmc/EthernetInterfaces";
1871             asyncResp->res.jsonValue["Name"] =
1872                 "Ethernet Network Interface Collection";
1873             asyncResp->res.jsonValue["Description"] =
1874                 "Collection of EthernetInterfaces for this Manager";
1875 
1876             // Get eth interface list, and call the below callback for JSON
1877             // preparation
1878             getEthernetIfaceList([asyncResp](const bool& success,
1879                                              const boost::container::flat_set<
1880                                                  std::string>& ifaceList) {
1881                 if (!success)
1882                 {
1883                     messages::internalError(asyncResp->res);
1884                     return;
1885                 }
1886 
1887                 nlohmann::json& ifaceArray =
1888                     asyncResp->res.jsonValue["Members"];
1889                 ifaceArray = nlohmann::json::array();
1890                 std::string tag = "_";
1891                 for (const std::string& ifaceItem : ifaceList)
1892                 {
1893                     std::size_t found = ifaceItem.find(tag);
1894                     if (found == std::string::npos)
1895                     {
1896                         ifaceArray.push_back(
1897                             {{"@odata.id",
1898                               "/redfish/v1/Managers/bmc/EthernetInterfaces/" +
1899                                   ifaceItem}});
1900                     }
1901                 }
1902 
1903                 asyncResp->res.jsonValue["Members@odata.count"] =
1904                     ifaceArray.size();
1905                 asyncResp->res.jsonValue["@odata.id"] =
1906                     "/redfish/v1/Managers/bmc/EthernetInterfaces";
1907             });
1908         });
1909 
1910     BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/EthernetInterfaces/<str>/")
1911         .privileges(redfish::privileges::getEthernetInterface)
1912         .methods(boost::beast::http::verb::get)(
1913             [](const crow::Request&,
1914                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1915                const std::string& ifaceId) {
1916                 getEthernetIfaceData(
1917                     ifaceId,
1918                     [asyncResp,
1919                      ifaceId](const bool& success,
1920                               const EthernetInterfaceData& ethData,
1921                               const boost::container::flat_set<IPv4AddressData>&
1922                                   ipv4Data,
1923                               const boost::container::flat_set<IPv6AddressData>&
1924                                   ipv6Data) {
1925                         if (!success)
1926                         {
1927                             // TODO(Pawel)consider distinguish between non
1928                             // existing object, and other errors
1929                             messages::resourceNotFound(
1930                                 asyncResp->res, "EthernetInterface", ifaceId);
1931                             return;
1932                         }
1933 
1934                         asyncResp->res.jsonValue["@odata.type"] =
1935                             "#EthernetInterface.v1_4_1.EthernetInterface";
1936                         asyncResp->res.jsonValue["Name"] =
1937                             "Manager Ethernet Interface";
1938                         asyncResp->res.jsonValue["Description"] =
1939                             "Management Network Interface";
1940 
1941                         parseInterfaceData(asyncResp, ifaceId, ethData,
1942                                            ipv4Data, ipv6Data);
1943                     });
1944             });
1945 
1946     BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/EthernetInterfaces/<str>/")
1947         .privileges(redfish::privileges::patchEthernetInterface)
1948 
1949         .methods(boost::beast::http::verb::patch)(
1950             [](const crow::Request& req,
1951                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1952                const std::string& ifaceId) {
1953                 std::optional<std::string> hostname;
1954                 std::optional<std::string> fqdn;
1955                 std::optional<std::string> macAddress;
1956                 std::optional<std::string> ipv6DefaultGateway;
1957                 std::optional<nlohmann::json> ipv4StaticAddresses;
1958                 std::optional<nlohmann::json> ipv6StaticAddresses;
1959                 std::optional<std::vector<std::string>> staticNameServers;
1960                 std::optional<nlohmann::json> dhcpv4;
1961                 std::optional<nlohmann::json> dhcpv6;
1962                 std::optional<bool> interfaceEnabled;
1963                 std::optional<size_t> mtuSize;
1964                 DHCPParameters v4dhcpParms;
1965                 DHCPParameters v6dhcpParms;
1966 
1967                 if (!json_util::readJson(
1968                         req, asyncResp->res, "HostName", hostname, "FQDN", fqdn,
1969                         "IPv4StaticAddresses", ipv4StaticAddresses,
1970                         "MACAddress", macAddress, "StaticNameServers",
1971                         staticNameServers, "IPv6DefaultGateway",
1972                         ipv6DefaultGateway, "IPv6StaticAddresses",
1973                         ipv6StaticAddresses, "DHCPv4", dhcpv4, "DHCPv6", dhcpv6,
1974                         "MTUSize", mtuSize, "InterfaceEnabled",
1975                         interfaceEnabled))
1976                 {
1977                     return;
1978                 }
1979                 if (dhcpv4)
1980                 {
1981                     if (!json_util::readJson(
1982                             *dhcpv4, asyncResp->res, "DHCPEnabled",
1983                             v4dhcpParms.dhcpv4Enabled, "UseDNSServers",
1984                             v4dhcpParms.useDNSServers, "UseNTPServers",
1985                             v4dhcpParms.useNTPServers, "UseDomainName",
1986                             v4dhcpParms.useUseDomainName))
1987                     {
1988                         return;
1989                     }
1990                 }
1991 
1992                 if (dhcpv6)
1993                 {
1994                     if (!json_util::readJson(
1995                             *dhcpv6, asyncResp->res, "OperatingMode",
1996                             v6dhcpParms.dhcpv6OperatingMode, "UseDNSServers",
1997                             v6dhcpParms.useDNSServers, "UseNTPServers",
1998                             v6dhcpParms.useNTPServers, "UseDomainName",
1999                             v6dhcpParms.useUseDomainName))
2000                     {
2001                         return;
2002                     }
2003                 }
2004 
2005                 // Get single eth interface data, and call the below callback
2006                 // for JSON preparation
2007                 getEthernetIfaceData(
2008                     ifaceId,
2009                     [asyncResp, ifaceId, hostname = std::move(hostname),
2010                      fqdn = std::move(fqdn), macAddress = std::move(macAddress),
2011                      ipv4StaticAddresses = std::move(ipv4StaticAddresses),
2012                      ipv6DefaultGateway = std::move(ipv6DefaultGateway),
2013                      ipv6StaticAddresses = std::move(ipv6StaticAddresses),
2014                      staticNameServers = std::move(staticNameServers),
2015                      dhcpv4 = std::move(dhcpv4), dhcpv6 = std::move(dhcpv6),
2016                      mtuSize = mtuSize, v4dhcpParms = std::move(v4dhcpParms),
2017                      v6dhcpParms = std::move(v6dhcpParms), interfaceEnabled](
2018                         const bool& success,
2019                         const EthernetInterfaceData& ethData,
2020                         const boost::container::flat_set<IPv4AddressData>&
2021                             ipv4Data,
2022                         const boost::container::flat_set<IPv6AddressData>&
2023                             ipv6Data) {
2024                         if (!success)
2025                         {
2026                             // ... otherwise return error
2027                             // TODO(Pawel)consider distinguish between non
2028                             // existing object, and other errors
2029                             messages::resourceNotFound(
2030                                 asyncResp->res, "Ethernet Interface", ifaceId);
2031                             return;
2032                         }
2033 
2034                         if (dhcpv4 || dhcpv6)
2035                         {
2036                             handleDHCPPatch(ifaceId, ethData, v4dhcpParms,
2037                                             v6dhcpParms, asyncResp);
2038                         }
2039 
2040                         if (hostname)
2041                         {
2042                             handleHostnamePatch(*hostname, asyncResp);
2043                         }
2044 
2045                         if (fqdn)
2046                         {
2047                             handleFqdnPatch(ifaceId, *fqdn, asyncResp);
2048                         }
2049 
2050                         if (macAddress)
2051                         {
2052                             handleMACAddressPatch(ifaceId, *macAddress,
2053                                                   asyncResp);
2054                         }
2055 
2056                         if (ipv4StaticAddresses)
2057                         {
2058                             // TODO(ed) for some reason the capture of
2059                             // ipv4Addresses above is returning a const value,
2060                             // not a non-const value. This doesn't really work
2061                             // for us, as we need to be able to efficiently move
2062                             // out the intermedia nlohmann::json objects. This
2063                             // makes a copy of the structure, and operates on
2064                             // that, but could be done more efficiently
2065                             nlohmann::json ipv4Static = *ipv4StaticAddresses;
2066                             handleIPv4StaticPatch(ifaceId, ipv4Static, ipv4Data,
2067                                                   asyncResp);
2068                         }
2069 
2070                         if (staticNameServers)
2071                         {
2072                             handleStaticNameServersPatch(
2073                                 ifaceId, *staticNameServers, asyncResp);
2074                         }
2075 
2076                         if (ipv6DefaultGateway)
2077                         {
2078                             messages::propertyNotWritable(asyncResp->res,
2079                                                           "IPv6DefaultGateway");
2080                         }
2081 
2082                         if (ipv6StaticAddresses)
2083                         {
2084                             nlohmann::json ipv6Static = *ipv6StaticAddresses;
2085                             handleIPv6StaticAddressesPatch(ifaceId, ipv6Static,
2086                                                            ipv6Data, asyncResp);
2087                         }
2088 
2089                         if (interfaceEnabled)
2090                         {
2091                             setEthernetInterfaceBoolProperty(
2092                                 ifaceId, "NICEnabled", *interfaceEnabled,
2093                                 asyncResp);
2094                         }
2095 
2096                         if (mtuSize)
2097                         {
2098                             handleMTUSizePatch(ifaceId, *mtuSize, asyncResp);
2099                         }
2100                     });
2101             });
2102 
2103     BMCWEB_ROUTE(
2104         app, "/redfish/v1/Managers/bmc/EthernetInterfaces/<str>/VLANs/<str>/")
2105         .privileges(redfish::privileges::getVLanNetworkInterface)
2106 
2107         .methods(boost::beast::http::verb::get)(
2108             [](const crow::Request& /* req */,
2109                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2110                const std::string& parentIfaceId, const std::string& ifaceId) {
2111                 asyncResp->res.jsonValue["@odata.type"] =
2112                     "#VLanNetworkInterface.v1_1_0.VLanNetworkInterface";
2113                 asyncResp->res.jsonValue["Name"] = "VLAN Network Interface";
2114 
2115                 if (!verifyNames(parentIfaceId, ifaceId))
2116                 {
2117                     return;
2118                 }
2119 
2120                 // Get single eth interface data, and call the below callback
2121                 // for JSON preparation
2122                 getEthernetIfaceData(
2123                     ifaceId,
2124                     [asyncResp, parentIfaceId, ifaceId](
2125                         const bool& success,
2126                         const EthernetInterfaceData& ethData,
2127                         const boost::container::flat_set<IPv4AddressData>&,
2128                         const boost::container::flat_set<IPv6AddressData>&) {
2129                         if (success && !ethData.vlan_id.empty())
2130                         {
2131                             parseInterfaceData(asyncResp->res.jsonValue,
2132                                                parentIfaceId, ifaceId, ethData);
2133                         }
2134                         else
2135                         {
2136                             // ... otherwise return error
2137                             // TODO(Pawel)consider distinguish between non
2138                             // existing object, and other errors
2139                             messages::resourceNotFound(asyncResp->res,
2140                                                        "VLAN Network Interface",
2141                                                        ifaceId);
2142                         }
2143                     });
2144             });
2145 
2146     BMCWEB_ROUTE(
2147         app, "/redfish/v1/Managers/bmc/EthernetInterfaces/<str>/VLANs/<str>/")
2148         // This privilege is incorrect, it should be ConfigureManager
2149         //.privileges(redfish::privileges::patchVLanNetworkInterface)
2150         .privileges({{"ConfigureComponents"}})
2151         .methods(boost::beast::http::verb::patch)(
2152             [](const crow::Request& req,
2153                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2154                const std::string& parentIfaceId, const std::string& ifaceId) {
2155                 if (!verifyNames(parentIfaceId, ifaceId))
2156                 {
2157                     messages::resourceNotFound(
2158                         asyncResp->res, "VLAN Network Interface", ifaceId);
2159                     return;
2160                 }
2161 
2162                 bool vlanEnable = false;
2163                 uint32_t vlanId = 0;
2164 
2165                 if (!json_util::readJson(req, asyncResp->res, "VLANEnable",
2166                                          vlanEnable, "VLANId", vlanId))
2167                 {
2168                     return;
2169                 }
2170 
2171                 // Get single eth interface data, and call the below callback
2172                 // for JSON preparation
2173                 getEthernetIfaceData(
2174                     ifaceId,
2175                     [asyncResp, parentIfaceId, ifaceId, &vlanEnable, &vlanId](
2176                         const bool& success,
2177                         const EthernetInterfaceData& ethData,
2178                         const boost::container::flat_set<IPv4AddressData>&,
2179                         const boost::container::flat_set<IPv6AddressData>&) {
2180                         if (success && !ethData.vlan_id.empty())
2181                         {
2182                             auto callback =
2183                                 [asyncResp](
2184                                     const boost::system::error_code ec) {
2185                                     if (ec)
2186                                     {
2187                                         messages::internalError(asyncResp->res);
2188                                     }
2189                                 };
2190 
2191                             if (vlanEnable)
2192                             {
2193                                 crow::connections::systemBus->async_method_call(
2194                                     std::move(callback),
2195                                     "xyz.openbmc_project.Network",
2196                                     "/xyz/openbmc_project/network/" + ifaceId,
2197                                     "org.freedesktop.DBus.Properties", "Set",
2198                                     "xyz.openbmc_project.Network.VLAN", "Id",
2199                                     dbus::utility::DbusVariantType(vlanId));
2200                             }
2201                             else
2202                             {
2203                                 BMCWEB_LOG_DEBUG
2204                                     << "vlanEnable is false. Deleting the "
2205                                        "vlan interface";
2206                                 crow::connections::systemBus->async_method_call(
2207                                     std::move(callback),
2208                                     "xyz.openbmc_project.Network",
2209                                     std::string(
2210                                         "/xyz/openbmc_project/network/") +
2211                                         ifaceId,
2212                                     "xyz.openbmc_project.Object.Delete",
2213                                     "Delete");
2214                             }
2215                         }
2216                         else
2217                         {
2218                             // TODO(Pawel)consider distinguish between non
2219                             // existing object, and other errors
2220                             messages::resourceNotFound(asyncResp->res,
2221                                                        "VLAN Network Interface",
2222                                                        ifaceId);
2223                             return;
2224                         }
2225                     });
2226             });
2227 
2228     BMCWEB_ROUTE(
2229         app, "/redfish/v1/Managers/bmc/EthernetInterfaces/<str>/VLANs/<str>/")
2230         // This privilege is incorrect, it should be ConfigureManager
2231         //.privileges(redfish::privileges::deleteVLanNetworkInterface)
2232         .privileges({{"ConfigureComponents"}})
2233         .methods(boost::beast::http::verb::delete_)(
2234             [](const crow::Request& /* req */,
2235                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2236                const std::string& parentIfaceId, const std::string& ifaceId) {
2237                 if (!verifyNames(parentIfaceId, ifaceId))
2238                 {
2239                     messages::resourceNotFound(
2240                         asyncResp->res, "VLAN Network Interface", ifaceId);
2241                     return;
2242                 }
2243 
2244                 // Get single eth interface data, and call the below callback
2245                 // for JSON preparation
2246                 getEthernetIfaceData(
2247                     ifaceId,
2248                     [asyncResp, parentIfaceId, ifaceId](
2249                         const bool& success,
2250                         const EthernetInterfaceData& ethData,
2251                         const boost::container::flat_set<IPv4AddressData>&,
2252                         const boost::container::flat_set<IPv6AddressData>&) {
2253                         if (success && !ethData.vlan_id.empty())
2254                         {
2255                             auto callback =
2256                                 [asyncResp](
2257                                     const boost::system::error_code ec) {
2258                                     if (ec)
2259                                     {
2260                                         messages::internalError(asyncResp->res);
2261                                     }
2262                                 };
2263                             crow::connections::systemBus->async_method_call(
2264                                 std::move(callback),
2265                                 "xyz.openbmc_project.Network",
2266                                 std::string("/xyz/openbmc_project/network/") +
2267                                     ifaceId,
2268                                 "xyz.openbmc_project.Object.Delete", "Delete");
2269                         }
2270                         else
2271                         {
2272                             // ... otherwise return error
2273                             // TODO(Pawel)consider distinguish between non
2274                             // existing object, and other errors
2275                             messages::resourceNotFound(asyncResp->res,
2276                                                        "VLAN Network Interface",
2277                                                        ifaceId);
2278                         }
2279                     });
2280             });
2281 
2282     BMCWEB_ROUTE(app,
2283                  "/redfish/v1/Managers/bmc/EthernetInterfaces/<str>/VLANs/")
2284 
2285         .privileges(redfish::privileges::getVLanNetworkInterfaceCollection)
2286         .methods(
2287             boost::beast::http::verb::
2288                 get)([](const crow::Request& /* req */,
2289                         const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2290                         const std::string& rootInterfaceName) {
2291             // Get eth interface list, and call the below callback for JSON
2292             // preparation
2293             getEthernetIfaceList([asyncResp, rootInterfaceName](
2294                                      const bool& success,
2295                                      const boost::container::flat_set<
2296                                          std::string>& ifaceList) {
2297                 if (!success)
2298                 {
2299                     messages::internalError(asyncResp->res);
2300                     return;
2301                 }
2302 
2303                 if (ifaceList.find(rootInterfaceName) == ifaceList.end())
2304                 {
2305                     messages::resourceNotFound(asyncResp->res,
2306                                                "VLanNetworkInterfaceCollection",
2307                                                rootInterfaceName);
2308                     return;
2309                 }
2310 
2311                 asyncResp->res.jsonValue["@odata.type"] =
2312                     "#VLanNetworkInterfaceCollection."
2313                     "VLanNetworkInterfaceCollection";
2314                 asyncResp->res.jsonValue["Name"] =
2315                     "VLAN Network Interface Collection";
2316 
2317                 nlohmann::json ifaceArray = nlohmann::json::array();
2318 
2319                 for (const std::string& ifaceItem : ifaceList)
2320                 {
2321                     if (boost::starts_with(ifaceItem, rootInterfaceName + "_"))
2322                     {
2323                         std::string path =
2324                             "/redfish/v1/Managers/bmc/EthernetInterfaces/";
2325                         path += rootInterfaceName;
2326                         path += "/VLANs/";
2327                         path += ifaceItem;
2328                         ifaceArray.push_back({{"@odata.id", std::move(path)}});
2329                     }
2330                 }
2331 
2332                 asyncResp->res.jsonValue["Members@odata.count"] =
2333                     ifaceArray.size();
2334                 asyncResp->res.jsonValue["Members"] = std::move(ifaceArray);
2335                 asyncResp->res.jsonValue["@odata.id"] =
2336                     "/redfish/v1/Managers/bmc/EthernetInterfaces/" +
2337                     rootInterfaceName + "/VLANs";
2338             });
2339         });
2340 
2341     BMCWEB_ROUTE(app,
2342                  "/redfish/v1/Managers/bmc/EthernetInterfaces/<str>/VLANs/")
2343         // This privilege is wrong, it should be ConfigureManager
2344         //.privileges(redfish::privileges::postVLanNetworkInterfaceCollection)
2345         .privileges({{"ConfigureComponents"}})
2346         .methods(boost::beast::http::verb::post)(
2347             [](const crow::Request& req,
2348                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2349                const std::string& rootInterfaceName) {
2350                 bool vlanEnable = false;
2351                 uint32_t vlanId = 0;
2352                 if (!json_util::readJson(req, asyncResp->res, "VLANId", vlanId,
2353                                          "VLANEnable", vlanEnable))
2354                 {
2355                     return;
2356                 }
2357                 // Need both vlanId and vlanEnable to service this request
2358                 if (vlanId == 0U)
2359                 {
2360                     messages::propertyMissing(asyncResp->res, "VLANId");
2361                 }
2362                 if (!vlanEnable)
2363                 {
2364                     messages::propertyMissing(asyncResp->res, "VLANEnable");
2365                 }
2366                 if (static_cast<bool>(vlanId) ^ vlanEnable)
2367                 {
2368                     return;
2369                 }
2370 
2371                 auto callback =
2372                     [asyncResp](const boost::system::error_code ec) {
2373                         if (ec)
2374                         {
2375                             // TODO(ed) make more consistent error messages
2376                             // based on phosphor-network responses
2377                             messages::internalError(asyncResp->res);
2378                             return;
2379                         }
2380                         messages::created(asyncResp->res);
2381                     };
2382                 crow::connections::systemBus->async_method_call(
2383                     std::move(callback), "xyz.openbmc_project.Network",
2384                     "/xyz/openbmc_project/network",
2385                     "xyz.openbmc_project.Network.VLAN.Create", "VLAN",
2386                     rootInterfaceName, vlanId);
2387             });
2388 }
2389 
2390 } // namespace redfish
2391