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