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