xref: /openbmc/bmcweb/features/redfish/lib/ethernet.hpp (revision 2e8c4bda9c4b2809ca76bb227f818592515a3e4a)
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(
1265                     asyncResp->res,
1266                     thisJson.dump(2, ' ', true,
1267                                   nlohmann::json::error_handler_t::replace),
1268                     pathString);
1269                 return;
1270             }
1271 
1272             // Find the address/subnet/gateway values. Any values that are
1273             // not explicitly provided are assumed to be unmodified from the
1274             // current state of the interface. Merge existing state into the
1275             // current request.
1276             const std::string* addr = nullptr;
1277             const std::string* gw = nullptr;
1278             uint8_t prefixLength = 0;
1279             bool errorInEntry = false;
1280             if (address)
1281             {
1282                 if (ip_util::ipv4VerifyIpAndGetBitcount(*address))
1283                 {
1284                     addr = &(*address);
1285                 }
1286                 else
1287                 {
1288                     messages::propertyValueFormatError(asyncResp->res, *address,
1289                                                        pathString + "/Address");
1290                     errorInEntry = true;
1291                 }
1292             }
1293             else if (nicIpEntry != ipv4Data.cend())
1294             {
1295                 addr = &(nicIpEntry->address);
1296             }
1297             else
1298             {
1299                 messages::propertyMissing(asyncResp->res,
1300                                           pathString + "/Address");
1301                 errorInEntry = true;
1302             }
1303 
1304             if (subnetMask)
1305             {
1306                 if (!ip_util::ipv4VerifyIpAndGetBitcount(*subnetMask,
1307                                                          &prefixLength))
1308                 {
1309                     messages::propertyValueFormatError(
1310                         asyncResp->res, *subnetMask,
1311                         pathString + "/SubnetMask");
1312                     errorInEntry = true;
1313                 }
1314             }
1315             else if (nicIpEntry != ipv4Data.cend())
1316             {
1317                 if (!ip_util::ipv4VerifyIpAndGetBitcount(nicIpEntry->netmask,
1318                                                          &prefixLength))
1319                 {
1320                     messages::propertyValueFormatError(
1321                         asyncResp->res, nicIpEntry->netmask,
1322                         pathString + "/SubnetMask");
1323                     errorInEntry = true;
1324                 }
1325             }
1326             else
1327             {
1328                 messages::propertyMissing(asyncResp->res,
1329                                           pathString + "/SubnetMask");
1330                 errorInEntry = true;
1331             }
1332 
1333             if (gateway)
1334             {
1335                 if (ip_util::ipv4VerifyIpAndGetBitcount(*gateway))
1336                 {
1337                     gw = &(*gateway);
1338                 }
1339                 else
1340                 {
1341                     messages::propertyValueFormatError(asyncResp->res, *gateway,
1342                                                        pathString + "/Gateway");
1343                     errorInEntry = true;
1344                 }
1345             }
1346             else if (nicIpEntry != ipv4Data.cend())
1347             {
1348                 gw = &nicIpEntry->gateway;
1349             }
1350             else
1351             {
1352                 messages::propertyMissing(asyncResp->res,
1353                                           pathString + "/Gateway");
1354                 errorInEntry = true;
1355             }
1356 
1357             if (errorInEntry)
1358             {
1359                 return;
1360             }
1361 
1362             if (nicIpEntry != ipv4Data.cend())
1363             {
1364                 deleteAndCreateIPAddress(IpVersion::IpV4, ifaceId,
1365                                          nicIpEntry->id, prefixLength, *gw,
1366                                          *addr, asyncResp);
1367                 nicIpEntry = getNextStaticIpEntry(++nicIpEntry,
1368                                                   ipv4Data.cend());
1369             }
1370             else
1371             {
1372                 createIPv4(ifaceId, prefixLength, *gateway, *address,
1373                            asyncResp);
1374             }
1375             entryIdx++;
1376         }
1377         else
1378         {
1379             if (nicIpEntry == ipv4Data.cend())
1380             {
1381                 // Requesting a DELETE/DO NOT MODIFY action for an item
1382                 // that isn't present on the eth(n) interface. Input JSON is
1383                 // in error, so bail out.
1384                 if (thisJson.is_null())
1385                 {
1386                     messages::resourceCannotBeDeleted(asyncResp->res);
1387                     return;
1388                 }
1389                 messages::propertyValueFormatError(
1390                     asyncResp->res,
1391                     thisJson.dump(2, ' ', true,
1392                                   nlohmann::json::error_handler_t::replace),
1393                     pathString);
1394                 return;
1395             }
1396 
1397             if (thisJson.is_null())
1398             {
1399                 deleteIPAddress(ifaceId, nicIpEntry->id, asyncResp);
1400             }
1401             if (nicIpEntry != ipv4Data.cend())
1402             {
1403                 nicIpEntry = getNextStaticIpEntry(++nicIpEntry,
1404                                                   ipv4Data.cend());
1405             }
1406             entryIdx++;
1407         }
1408     }
1409 }
1410 
1411 inline void handleStaticNameServersPatch(
1412     const std::string& ifaceId,
1413     const std::vector<std::string>& updatedStaticNameServers,
1414     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1415 {
1416     crow::connections::systemBus->async_method_call(
1417         [asyncResp](const boost::system::error_code& ec) {
1418         if (ec)
1419         {
1420             messages::internalError(asyncResp->res);
1421             return;
1422         }
1423         },
1424         "xyz.openbmc_project.Network",
1425         "/xyz/openbmc_project/network/" + ifaceId,
1426         "org.freedesktop.DBus.Properties", "Set",
1427         "xyz.openbmc_project.Network.EthernetInterface", "StaticNameServers",
1428         dbus::utility::DbusVariantType{updatedStaticNameServers});
1429 }
1430 
1431 inline void handleIPv6StaticAddressesPatch(
1432     const std::string& ifaceId, const nlohmann::json::array_t& input,
1433     const std::vector<IPv6AddressData>& ipv6Data,
1434     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1435 {
1436     if (input.empty())
1437     {
1438         messages::propertyValueTypeError(asyncResp->res, input,
1439                                          "IPv6StaticAddresses");
1440         return;
1441     }
1442     size_t entryIdx = 1;
1443     std::vector<IPv6AddressData>::const_iterator nicIpEntry =
1444         getNextStaticIpEntry(ipv6Data.cbegin(), ipv6Data.cend());
1445     for (const nlohmann::json& thisJson : input)
1446     {
1447         std::string pathString = "IPv6StaticAddresses/" +
1448                                  std::to_string(entryIdx);
1449 
1450         if (!thisJson.is_null() && !thisJson.empty())
1451         {
1452             std::optional<std::string> address;
1453             std::optional<uint8_t> prefixLength;
1454             nlohmann::json thisJsonCopy = thisJson;
1455             if (!json_util::readJson(thisJsonCopy, asyncResp->res, "Address",
1456                                      address, "PrefixLength", prefixLength))
1457             {
1458                 messages::propertyValueFormatError(
1459                     asyncResp->res,
1460                     thisJson.dump(2, ' ', true,
1461                                   nlohmann::json::error_handler_t::replace),
1462                     pathString);
1463                 return;
1464             }
1465 
1466             const std::string* addr = nullptr;
1467             uint8_t prefix = 0;
1468 
1469             // Find the address and prefixLength values. Any values that are
1470             // not explicitly provided are assumed to be unmodified from the
1471             // current state of the interface. Merge existing state into the
1472             // current request.
1473             if (address)
1474             {
1475                 addr = &(*address);
1476             }
1477             else if (nicIpEntry != ipv6Data.end())
1478             {
1479                 addr = &(nicIpEntry->address);
1480             }
1481             else
1482             {
1483                 messages::propertyMissing(asyncResp->res,
1484                                           pathString + "/Address");
1485                 return;
1486             }
1487 
1488             if (prefixLength)
1489             {
1490                 prefix = *prefixLength;
1491             }
1492             else if (nicIpEntry != ipv6Data.end())
1493             {
1494                 prefix = nicIpEntry->prefixLength;
1495             }
1496             else
1497             {
1498                 messages::propertyMissing(asyncResp->res,
1499                                           pathString + "/PrefixLength");
1500                 return;
1501             }
1502 
1503             if (nicIpEntry != ipv6Data.end())
1504             {
1505                 deleteAndCreateIPAddress(IpVersion::IpV6, ifaceId,
1506                                          nicIpEntry->id, prefix, "", *addr,
1507                                          asyncResp);
1508                 nicIpEntry = getNextStaticIpEntry(++nicIpEntry,
1509                                                   ipv6Data.cend());
1510             }
1511             else
1512             {
1513                 createIPv6(ifaceId, *prefixLength, *addr, asyncResp);
1514             }
1515             entryIdx++;
1516         }
1517         else
1518         {
1519             if (nicIpEntry == ipv6Data.end())
1520             {
1521                 // Requesting a DELETE/DO NOT MODIFY action for an item
1522                 // that isn't present on the eth(n) interface. Input JSON is
1523                 // in error, so bail out.
1524                 if (thisJson.is_null())
1525                 {
1526                     messages::resourceCannotBeDeleted(asyncResp->res);
1527                     return;
1528                 }
1529                 messages::propertyValueFormatError(
1530                     asyncResp->res,
1531                     thisJson.dump(2, ' ', true,
1532                                   nlohmann::json::error_handler_t::replace),
1533                     pathString);
1534                 return;
1535             }
1536 
1537             if (thisJson.is_null())
1538             {
1539                 deleteIPAddress(ifaceId, nicIpEntry->id, asyncResp);
1540             }
1541             if (nicIpEntry != ipv6Data.cend())
1542             {
1543                 nicIpEntry = getNextStaticIpEntry(++nicIpEntry,
1544                                                   ipv6Data.cend());
1545             }
1546             entryIdx++;
1547         }
1548     }
1549 }
1550 
1551 inline std::string extractParentInterfaceName(const std::string& ifaceId)
1552 {
1553     std::size_t pos = ifaceId.find('_');
1554     return ifaceId.substr(0, pos);
1555 }
1556 
1557 inline void
1558     parseInterfaceData(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1559                        const std::string& ifaceId,
1560                        const EthernetInterfaceData& ethData,
1561                        const std::vector<IPv4AddressData>& ipv4Data,
1562                        const std::vector<IPv6AddressData>& ipv6Data)
1563 {
1564     nlohmann::json& jsonResponse = asyncResp->res.jsonValue;
1565     jsonResponse["Id"] = ifaceId;
1566     jsonResponse["@odata.id"] = boost::urls::format(
1567         "/redfish/v1/Managers/bmc/EthernetInterfaces/{}", ifaceId);
1568     jsonResponse["InterfaceEnabled"] = ethData.nicEnabled;
1569 
1570     if constexpr (bmcwebEnableHealthPopulate)
1571     {
1572         constexpr std::array<std::string_view, 1> inventoryForEthernet = {
1573             "xyz.openbmc_project.Inventory.Item.Ethernet"};
1574         auto health = std::make_shared<HealthPopulate>(asyncResp);
1575         dbus::utility::getSubTreePaths(
1576             "/", 0, inventoryForEthernet,
1577             [health](const boost::system::error_code& ec,
1578                      const dbus::utility::MapperGetSubTreePathsResponse& resp) {
1579             if (ec)
1580             {
1581                 return;
1582             }
1583 
1584             health->inventory = resp;
1585             });
1586 
1587         health->populate();
1588     }
1589 
1590     if (ethData.nicEnabled)
1591     {
1592         jsonResponse["LinkStatus"] = ethData.linkUp ? "LinkUp" : "LinkDown";
1593         jsonResponse["Status"]["State"] = "Enabled";
1594     }
1595     else
1596     {
1597         jsonResponse["LinkStatus"] = "NoLink";
1598         jsonResponse["Status"]["State"] = "Disabled";
1599     }
1600 
1601     jsonResponse["SpeedMbps"] = ethData.speed;
1602     jsonResponse["MTUSize"] = ethData.mtuSize;
1603     jsonResponse["MACAddress"] = ethData.macAddress;
1604     jsonResponse["DHCPv4"]["DHCPEnabled"] =
1605         translateDhcpEnabledToBool(ethData.dhcpEnabled, true);
1606     jsonResponse["DHCPv4"]["UseNTPServers"] = ethData.ntpEnabled;
1607     jsonResponse["DHCPv4"]["UseDNSServers"] = ethData.dnsEnabled;
1608     jsonResponse["DHCPv4"]["UseDomainName"] = ethData.hostNameEnabled;
1609 
1610     jsonResponse["DHCPv6"]["OperatingMode"] =
1611         translateDhcpEnabledToBool(ethData.dhcpEnabled, false) ? "Stateful"
1612                                                                : "Disabled";
1613     jsonResponse["DHCPv6"]["UseNTPServers"] = ethData.ntpEnabled;
1614     jsonResponse["DHCPv6"]["UseDNSServers"] = ethData.dnsEnabled;
1615     jsonResponse["DHCPv6"]["UseDomainName"] = ethData.hostNameEnabled;
1616 
1617     if (!ethData.hostName.empty())
1618     {
1619         jsonResponse["HostName"] = ethData.hostName;
1620 
1621         // When domain name is empty then it means, that it is a network
1622         // without domain names, and the host name itself must be treated as
1623         // FQDN
1624         std::string fqdn = ethData.hostName;
1625         if (!ethData.domainnames.empty())
1626         {
1627             fqdn += "." + ethData.domainnames[0];
1628         }
1629         jsonResponse["FQDN"] = fqdn;
1630     }
1631 
1632     if (ethData.vlanId)
1633     {
1634         jsonResponse["EthernetInterfaceType"] = "Virtual";
1635         jsonResponse["VLAN"]["VLANEnable"] = true;
1636         jsonResponse["VLAN"]["VLANId"] = *ethData.vlanId;
1637         jsonResponse["VLAN"]["Tagged"] = true;
1638 
1639         nlohmann::json::array_t relatedInterfaces;
1640         nlohmann::json& parentInterface = relatedInterfaces.emplace_back();
1641         parentInterface["@odata.id"] =
1642             boost::urls::format("/redfish/v1/Managers/bmc/EthernetInterfaces",
1643                                 extractParentInterfaceName(ifaceId));
1644         jsonResponse["Links"]["RelatedInterfaces"] =
1645             std::move(relatedInterfaces);
1646     }
1647     else
1648     {
1649         jsonResponse["EthernetInterfaceType"] = "Physical";
1650     }
1651 
1652     jsonResponse["NameServers"] = ethData.nameServers;
1653     jsonResponse["StaticNameServers"] = ethData.staticNameServers;
1654 
1655     nlohmann::json& ipv4Array = jsonResponse["IPv4Addresses"];
1656     nlohmann::json& ipv4StaticArray = jsonResponse["IPv4StaticAddresses"];
1657     ipv4Array = nlohmann::json::array();
1658     ipv4StaticArray = nlohmann::json::array();
1659     for (const auto& ipv4Config : ipv4Data)
1660     {
1661         std::string gatewayStr = ipv4Config.gateway;
1662         if (gatewayStr.empty())
1663         {
1664             gatewayStr = "0.0.0.0";
1665         }
1666         nlohmann::json::object_t ipv4;
1667         ipv4["AddressOrigin"] = ipv4Config.origin;
1668         ipv4["SubnetMask"] = ipv4Config.netmask;
1669         ipv4["Address"] = ipv4Config.address;
1670         ipv4["Gateway"] = gatewayStr;
1671 
1672         if (ipv4Config.origin == "Static")
1673         {
1674             ipv4StaticArray.push_back(ipv4);
1675         }
1676 
1677         ipv4Array.emplace_back(std::move(ipv4));
1678     }
1679 
1680     std::string ipv6GatewayStr = ethData.ipv6DefaultGateway;
1681     if (ipv6GatewayStr.empty())
1682     {
1683         ipv6GatewayStr = "0:0:0:0:0:0:0:0";
1684     }
1685 
1686     jsonResponse["IPv6DefaultGateway"] = ipv6GatewayStr;
1687 
1688     nlohmann::json& ipv6Array = jsonResponse["IPv6Addresses"];
1689     nlohmann::json& ipv6StaticArray = jsonResponse["IPv6StaticAddresses"];
1690     ipv6Array = nlohmann::json::array();
1691     ipv6StaticArray = nlohmann::json::array();
1692     nlohmann::json& ipv6AddrPolicyTable =
1693         jsonResponse["IPv6AddressPolicyTable"];
1694     ipv6AddrPolicyTable = nlohmann::json::array();
1695     for (const auto& ipv6Config : ipv6Data)
1696     {
1697         nlohmann::json::object_t ipv6;
1698         ipv6["Address"] = ipv6Config.address;
1699         ipv6["PrefixLength"] = ipv6Config.prefixLength;
1700         ipv6["AddressOrigin"] = ipv6Config.origin;
1701 
1702         ipv6Array.emplace_back(std::move(ipv6));
1703         if (ipv6Config.origin == "Static")
1704         {
1705             nlohmann::json::object_t ipv6Static;
1706             ipv6Static["Address"] = ipv6Config.address;
1707             ipv6Static["PrefixLength"] = ipv6Config.prefixLength;
1708             ipv6StaticArray.emplace_back(std::move(ipv6Static));
1709         }
1710     }
1711 }
1712 
1713 inline void afterDelete(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1714                         const std::string& ifaceId,
1715                         const boost::system::error_code& ec,
1716                         const sdbusplus::message_t& m)
1717 {
1718     if (!ec)
1719     {
1720         return;
1721     }
1722     const sd_bus_error* dbusError = m.get_error();
1723     if (dbusError == nullptr)
1724     {
1725         messages::internalError(asyncResp->res);
1726         return;
1727     }
1728     BMCWEB_LOG_DEBUG << "DBus error: " << dbusError->name;
1729 
1730     if (std::string_view("org.freedesktop.DBus.Error.UnknownObject") ==
1731         dbusError->name)
1732     {
1733         messages::resourceNotFound(asyncResp->res, "EthernetInterface",
1734                                    ifaceId);
1735         return;
1736     }
1737     if (std::string_view("org.freedesktop.DBus.Error.UnknownMethod") ==
1738         dbusError->name)
1739     {
1740         messages::resourceCannotBeDeleted(asyncResp->res);
1741         return;
1742     }
1743     messages::internalError(asyncResp->res);
1744 }
1745 
1746 inline void afterVlanCreate(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1747                             const std::string& parentInterfaceUri,
1748                             const std::string& vlanInterface,
1749                             const boost::system::error_code& ec,
1750                             const sdbusplus::message_t& m
1751 
1752 )
1753 {
1754     if (ec)
1755     {
1756         const sd_bus_error* dbusError = m.get_error();
1757         if (dbusError == nullptr)
1758         {
1759             messages::internalError(asyncResp->res);
1760             return;
1761         }
1762         BMCWEB_LOG_DEBUG << "DBus error: " << dbusError->name;
1763 
1764         if (std::string_view(
1765                 "xyz.openbmc_project.Common.Error.ResourceNotFound") ==
1766             dbusError->name)
1767         {
1768             messages::propertyValueNotInList(
1769                 asyncResp->res, parentInterfaceUri,
1770                 "Links/RelatedInterfaces/0/@odata.id");
1771             return;
1772         }
1773         if (std::string_view(
1774                 "xyz.openbmc_project.Common.Error.InvalidArgument") ==
1775             dbusError->name)
1776         {
1777             messages::resourceAlreadyExists(asyncResp->res, "EthernetInterface",
1778                                             "Id", vlanInterface);
1779             return;
1780         }
1781         messages::internalError(asyncResp->res);
1782         return;
1783     }
1784 
1785     const boost::urls::url vlanInterfaceUri = boost::urls::format(
1786         "/redfish/v1/Managers/bmc/EthernetInterfaces/{}", vlanInterface);
1787     asyncResp->res.addHeader("Location", vlanInterfaceUri.buffer());
1788 }
1789 
1790 inline void requestEthernetInterfacesRoutes(App& app)
1791 {
1792     BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/EthernetInterfaces/")
1793         .privileges(redfish::privileges::getEthernetInterfaceCollection)
1794         .methods(boost::beast::http::verb::get)(
1795             [&app](const crow::Request& req,
1796                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1797         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1798         {
1799             return;
1800         }
1801 
1802         asyncResp->res.jsonValue["@odata.type"] =
1803             "#EthernetInterfaceCollection.EthernetInterfaceCollection";
1804         asyncResp->res.jsonValue["@odata.id"] =
1805             "/redfish/v1/Managers/bmc/EthernetInterfaces";
1806         asyncResp->res.jsonValue["Name"] =
1807             "Ethernet Network Interface Collection";
1808         asyncResp->res.jsonValue["Description"] =
1809             "Collection of EthernetInterfaces for this Manager";
1810 
1811         // Get eth interface list, and call the below callback for JSON
1812         // preparation
1813         getEthernetIfaceList(
1814             [asyncResp](const bool& success,
1815                         const std::vector<std::string>& ifaceList) {
1816             if (!success)
1817             {
1818                 messages::internalError(asyncResp->res);
1819                 return;
1820             }
1821 
1822             nlohmann::json& ifaceArray = asyncResp->res.jsonValue["Members"];
1823             ifaceArray = nlohmann::json::array();
1824             for (const std::string& ifaceItem : ifaceList)
1825             {
1826                 nlohmann::json::object_t iface;
1827                 iface["@odata.id"] = boost::urls::format(
1828                     "/redfish/v1/Managers/bmc/EthernetInterfaces/{}",
1829                     ifaceItem);
1830                 ifaceArray.push_back(std::move(iface));
1831             }
1832 
1833             asyncResp->res.jsonValue["Members@odata.count"] = ifaceArray.size();
1834             asyncResp->res.jsonValue["@odata.id"] =
1835                 "/redfish/v1/Managers/bmc/EthernetInterfaces";
1836         });
1837         });
1838 
1839     BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/EthernetInterfaces/")
1840         .privileges(redfish::privileges::postEthernetInterfaceCollection)
1841         .methods(boost::beast::http::verb::post)(
1842             [&app](const crow::Request& req,
1843                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1844         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1845         {
1846             return;
1847         }
1848 
1849         bool vlanEnable = false;
1850         uint32_t vlanId = 0;
1851         nlohmann::json::array_t relatedInterfaces;
1852 
1853         if (!json_util::readJsonPatch(req, asyncResp->res, "VLAN/VLANEnable",
1854                                       vlanEnable, "VLAN/VLANId", vlanId,
1855                                       "Links/RelatedInterfaces",
1856                                       relatedInterfaces))
1857         {
1858             return;
1859         }
1860 
1861         if (relatedInterfaces.size() != 1)
1862         {
1863             messages::arraySizeTooLong(asyncResp->res,
1864                                        "Links/RelatedInterfaces",
1865                                        relatedInterfaces.size());
1866             return;
1867         }
1868 
1869         std::string parentInterfaceUri;
1870         if (!json_util::readJson(relatedInterfaces[0], asyncResp->res,
1871                                  "@odata.id", parentInterfaceUri))
1872         {
1873             messages::propertyMissing(asyncResp->res,
1874                                       "Links/RelatedInterfaces/0/@odata.id");
1875             return;
1876         }
1877         BMCWEB_LOG_INFO << "Parent Interface URI: " << parentInterfaceUri;
1878 
1879         boost::urls::result<boost::urls::url_view> parsedUri =
1880             boost::urls::parse_relative_ref(parentInterfaceUri);
1881         if (!parsedUri)
1882         {
1883             messages::propertyValueFormatError(
1884                 asyncResp->res, parentInterfaceUri,
1885                 "Links/RelatedInterfaces/0/@odata.id");
1886             return;
1887         }
1888 
1889         std::string parentInterface;
1890         if (!crow::utility::readUrlSegments(
1891                 *parsedUri, "redfish", "v1", "Managers", "bmc",
1892                 "EthernetInterfaces", std::ref(parentInterface)))
1893         {
1894             messages::propertyValueNotInList(
1895                 asyncResp->res, parentInterfaceUri,
1896                 "Links/RelatedInterfaces/0/@odata.id");
1897             return;
1898         }
1899 
1900         if (!vlanEnable)
1901         {
1902             // In OpenBMC implementation, VLANEnable cannot be false on
1903             // create
1904             messages::propertyValueIncorrect(asyncResp->res, "VLAN/VLANEnable",
1905                                              "false");
1906             return;
1907         }
1908 
1909         std::string vlanInterface = parentInterface + "_" +
1910                                     std::to_string(vlanId);
1911         crow::connections::systemBus->async_method_call(
1912             [asyncResp, parentInterfaceUri,
1913              vlanInterface](const boost::system::error_code& ec,
1914                             const sdbusplus::message_t& m) {
1915             afterVlanCreate(asyncResp, parentInterfaceUri, vlanInterface, ec,
1916                             m);
1917             },
1918             "xyz.openbmc_project.Network", "/xyz/openbmc_project/network",
1919             "xyz.openbmc_project.Network.VLAN.Create", "VLAN", parentInterface,
1920             vlanId);
1921         });
1922 
1923     BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/EthernetInterfaces/<str>/")
1924         .privileges(redfish::privileges::getEthernetInterface)
1925         .methods(boost::beast::http::verb::get)(
1926             [&app](const crow::Request& req,
1927                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1928                    const std::string& ifaceId) {
1929         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1930         {
1931             return;
1932         }
1933         getEthernetIfaceData(
1934             ifaceId,
1935             [asyncResp, ifaceId](const bool& success,
1936                                  const EthernetInterfaceData& ethData,
1937                                  const std::vector<IPv4AddressData>& ipv4Data,
1938                                  const std::vector<IPv6AddressData>& ipv6Data) {
1939             if (!success)
1940             {
1941                 // TODO(Pawel)consider distinguish between non
1942                 // existing object, and other errors
1943                 messages::resourceNotFound(asyncResp->res, "EthernetInterface",
1944                                            ifaceId);
1945                 return;
1946             }
1947 
1948             asyncResp->res.jsonValue["@odata.type"] =
1949                 "#EthernetInterface.v1_9_0.EthernetInterface";
1950             asyncResp->res.jsonValue["Name"] = "Manager Ethernet Interface";
1951             asyncResp->res.jsonValue["Description"] =
1952                 "Management Network Interface";
1953 
1954             parseInterfaceData(asyncResp, ifaceId, ethData, ipv4Data, ipv6Data);
1955             });
1956         });
1957 
1958     BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/EthernetInterfaces/<str>/")
1959         .privileges(redfish::privileges::patchEthernetInterface)
1960         .methods(boost::beast::http::verb::patch)(
1961             [&app](const crow::Request& req,
1962                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1963                    const std::string& ifaceId) {
1964         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1965         {
1966             return;
1967         }
1968         std::optional<std::string> hostname;
1969         std::optional<std::string> fqdn;
1970         std::optional<std::string> macAddress;
1971         std::optional<std::string> ipv6DefaultGateway;
1972         std::optional<nlohmann::json::array_t> ipv4StaticAddresses;
1973         std::optional<nlohmann::json::array_t> ipv6StaticAddresses;
1974         std::optional<std::vector<std::string>> staticNameServers;
1975         std::optional<nlohmann::json> dhcpv4;
1976         std::optional<nlohmann::json> dhcpv6;
1977         std::optional<bool> interfaceEnabled;
1978         std::optional<size_t> mtuSize;
1979         DHCPParameters v4dhcpParms;
1980         DHCPParameters v6dhcpParms;
1981 
1982         if (!json_util::readJsonPatch(
1983                 req, asyncResp->res, "HostName", hostname, "FQDN", fqdn,
1984                 "IPv4StaticAddresses", ipv4StaticAddresses, "MACAddress",
1985                 macAddress, "StaticNameServers", staticNameServers,
1986                 "IPv6DefaultGateway", ipv6DefaultGateway, "IPv6StaticAddresses",
1987                 ipv6StaticAddresses, "DHCPv4", dhcpv4, "DHCPv6", dhcpv6,
1988                 "MTUSize", mtuSize, "InterfaceEnabled", interfaceEnabled))
1989         {
1990             return;
1991         }
1992         if (dhcpv4)
1993         {
1994             if (!json_util::readJson(*dhcpv4, asyncResp->res, "DHCPEnabled",
1995                                      v4dhcpParms.dhcpv4Enabled, "UseDNSServers",
1996                                      v4dhcpParms.useDnsServers, "UseNTPServers",
1997                                      v4dhcpParms.useNtpServers, "UseDomainName",
1998                                      v4dhcpParms.useDomainName))
1999             {
2000                 return;
2001             }
2002         }
2003 
2004         if (dhcpv6)
2005         {
2006             if (!json_util::readJson(*dhcpv6, asyncResp->res, "OperatingMode",
2007                                      v6dhcpParms.dhcpv6OperatingMode,
2008                                      "UseDNSServers", v6dhcpParms.useDnsServers,
2009                                      "UseNTPServers", v6dhcpParms.useNtpServers,
2010                                      "UseDomainName",
2011                                      v6dhcpParms.useDomainName))
2012             {
2013                 return;
2014             }
2015         }
2016 
2017         // Get single eth interface data, and call the below callback
2018         // for JSON preparation
2019         getEthernetIfaceData(
2020             ifaceId,
2021             [asyncResp, ifaceId, hostname = std::move(hostname),
2022              fqdn = std::move(fqdn), macAddress = std::move(macAddress),
2023              ipv4StaticAddresses = std::move(ipv4StaticAddresses),
2024              ipv6DefaultGateway = std::move(ipv6DefaultGateway),
2025              ipv6StaticAddresses = std::move(ipv6StaticAddresses),
2026              staticNameServers = std::move(staticNameServers),
2027              dhcpv4 = std::move(dhcpv4), dhcpv6 = std::move(dhcpv6), mtuSize,
2028              v4dhcpParms = std::move(v4dhcpParms),
2029              v6dhcpParms = std::move(v6dhcpParms), interfaceEnabled](
2030                 const bool& success, const EthernetInterfaceData& ethData,
2031                 const std::vector<IPv4AddressData>& ipv4Data,
2032                 const std::vector<IPv6AddressData>& ipv6Data) {
2033             if (!success)
2034             {
2035                 // ... otherwise return error
2036                 // TODO(Pawel)consider distinguish between non
2037                 // existing object, and other errors
2038                 messages::resourceNotFound(asyncResp->res, "EthernetInterface",
2039                                            ifaceId);
2040                 return;
2041             }
2042 
2043             if (dhcpv4 || dhcpv6)
2044             {
2045                 handleDHCPPatch(ifaceId, ethData, v4dhcpParms, v6dhcpParms,
2046                                 asyncResp);
2047             }
2048 
2049             if (hostname)
2050             {
2051                 handleHostnamePatch(*hostname, asyncResp);
2052             }
2053 
2054             if (fqdn)
2055             {
2056                 handleFqdnPatch(ifaceId, *fqdn, asyncResp);
2057             }
2058 
2059             if (macAddress)
2060             {
2061                 handleMACAddressPatch(ifaceId, *macAddress, asyncResp);
2062             }
2063 
2064             if (ipv4StaticAddresses)
2065             {
2066                 // TODO(ed) for some reason the capture of
2067                 // ipv4Addresses above is returning a const value,
2068                 // not a non-const value. This doesn't really work
2069                 // for us, as we need to be able to efficiently move
2070                 // out the intermedia nlohmann::json objects. This
2071                 // makes a copy of the structure, and operates on
2072                 // that, but could be done more efficiently
2073                 nlohmann::json::array_t ipv4Static = *ipv4StaticAddresses;
2074                 handleIPv4StaticPatch(ifaceId, ipv4Static, ipv4Data, asyncResp);
2075             }
2076 
2077             if (staticNameServers)
2078             {
2079                 handleStaticNameServersPatch(ifaceId, *staticNameServers,
2080                                              asyncResp);
2081             }
2082 
2083             if (ipv6DefaultGateway)
2084             {
2085                 messages::propertyNotWritable(asyncResp->res,
2086                                               "IPv6DefaultGateway");
2087             }
2088 
2089             if (ipv6StaticAddresses)
2090             {
2091                 handleIPv6StaticAddressesPatch(ifaceId, *ipv6StaticAddresses,
2092                                                ipv6Data, asyncResp);
2093             }
2094 
2095             if (interfaceEnabled)
2096             {
2097                 setEthernetInterfaceBoolProperty(ifaceId, "NICEnabled",
2098                                                  *interfaceEnabled, asyncResp);
2099             }
2100 
2101             if (mtuSize)
2102             {
2103                 handleMTUSizePatch(ifaceId, *mtuSize, asyncResp);
2104             }
2105             });
2106         });
2107 
2108     BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/EthernetInterfaces/<str>/")
2109         .privileges(redfish::privileges::deleteEthernetInterface)
2110         .methods(boost::beast::http::verb::delete_)(
2111             [&app](const crow::Request& req,
2112                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2113                    const std::string& ifaceId) {
2114         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2115         {
2116             return;
2117         }
2118 
2119         crow::connections::systemBus->async_method_call(
2120             [asyncResp, ifaceId](const boost::system::error_code& ec,
2121                                  const sdbusplus::message_t& m) {
2122             afterDelete(asyncResp, ifaceId, ec, m);
2123             },
2124             "xyz.openbmc_project.Network",
2125             std::string("/xyz/openbmc_project/network/") + ifaceId,
2126             "xyz.openbmc_project.Object.Delete", "Delete");
2127         });
2128 }
2129 
2130 } // namespace redfish
2131