xref: /openbmc/bmcweb/redfish-core/lib/ethernet.hpp (revision 2c6ffdb08b2207ff7c31041f77cc3755508d45c4)
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     sdbusplus::asio::setProperty(
642         *crow::connections::systemBus, "xyz.openbmc_project.Network",
643         "/xyz/openbmc_project/network/" + ifaceId,
644         "xyz.openbmc_project.Network.EthernetInterface", "DefaultGateway",
645         gateway, [asyncResp](const boost::system::error_code& ec) {
646             if (ec)
647             {
648                 messages::internalError(asyncResp->res);
649                 return;
650             }
651             asyncResp->res.result(boost::beast::http::status::no_content);
652         });
653 }
654 /**
655  * @brief Creates a static IPv4 entry
656  *
657  * @param[in] ifaceId      Id of interface upon which to create the IPv4 entry
658  * @param[in] prefixLength IPv4 prefix syntax for the subnet mask
659  * @param[in] gateway      IPv4 address of this interfaces gateway
660  * @param[in] address      IPv4 address to assign to this interface
661  * @param[io] asyncResp    Response object that will be returned to client
662  *
663  * @return None
664  */
665 inline void createIPv4(const std::string& ifaceId, uint8_t prefixLength,
666                        const std::string& gateway, const std::string& address,
667                        const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
668 {
669     auto createIpHandler =
670         [asyncResp, ifaceId, gateway](const boost::system::error_code& ec) {
671         if (ec)
672         {
673             messages::internalError(asyncResp->res);
674             return;
675         }
676         updateIPv4DefaultGateway(ifaceId, gateway, asyncResp);
677     };
678 
679     crow::connections::systemBus->async_method_call(
680         std::move(createIpHandler), "xyz.openbmc_project.Network",
681         "/xyz/openbmc_project/network/" + ifaceId,
682         "xyz.openbmc_project.Network.IP.Create", "IP",
683         "xyz.openbmc_project.Network.IP.Protocol.IPv4", address, prefixLength,
684         gateway);
685 }
686 
687 /**
688  * @brief Deletes the IPv6 entry for this interface and creates a replacement
689  * static IPv6 entry
690  *
691  * @param[in] ifaceId      Id of interface upon which to create the IPv6 entry
692  * @param[in] id           The unique hash entry identifying the DBus entry
693  * @param[in] prefixLength IPv6 prefix syntax for the subnet mask
694  * @param[in] address      IPv6 address to assign to this interface
695  * @param[io] asyncResp    Response object that will be returned to client
696  *
697  * @return None
698  */
699 
700 enum class IpVersion
701 {
702     IpV4,
703     IpV6
704 };
705 
706 inline void deleteAndCreateIPAddress(
707     IpVersion version, const std::string& ifaceId, const std::string& id,
708     uint8_t prefixLength, const std::string& address,
709     const std::string& gateway,
710     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
711 {
712     crow::connections::systemBus->async_method_call(
713         [asyncResp, version, ifaceId, address, prefixLength,
714          gateway](const boost::system::error_code& ec) {
715         if (ec)
716         {
717             messages::internalError(asyncResp->res);
718         }
719         std::string protocol = "xyz.openbmc_project.Network.IP.Protocol.";
720         protocol += version == IpVersion::IpV4 ? "IPv4" : "IPv6";
721         crow::connections::systemBus->async_method_call(
722             [asyncResp](const boost::system::error_code& ec2) {
723             if (ec2)
724             {
725                 messages::internalError(asyncResp->res);
726             }
727             },
728             "xyz.openbmc_project.Network",
729             "/xyz/openbmc_project/network/" + ifaceId,
730             "xyz.openbmc_project.Network.IP.Create", "IP", protocol, address,
731             prefixLength, gateway);
732         },
733         "xyz.openbmc_project.Network",
734         "/xyz/openbmc_project/network/" + ifaceId + id,
735         "xyz.openbmc_project.Object.Delete", "Delete");
736 }
737 
738 /**
739  * @brief Creates IPv6 with given data
740  *
741  * @param[in] ifaceId      Id of interface whose IP should be added
742  * @param[in] prefixLength Prefix length that needs to be added
743  * @param[in] address      IP address that needs to be added
744  * @param[io] asyncResp    Response object that will be returned to client
745  *
746  * @return None
747  */
748 inline void createIPv6(const std::string& ifaceId, uint8_t prefixLength,
749                        const std::string& address,
750                        const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
751 {
752     auto createIpHandler =
753         [asyncResp, address](const boost::system::error_code& ec) {
754         if (ec)
755         {
756             if (ec == boost::system::errc::io_error)
757             {
758                 messages::propertyValueFormatError(asyncResp->res, address,
759                                                    "Address");
760             }
761             else
762             {
763                 messages::internalError(asyncResp->res);
764             }
765         }
766     };
767     // Passing null for gateway, as per redfish spec IPv6StaticAddresses object
768     // does not have associated gateway property
769     crow::connections::systemBus->async_method_call(
770         std::move(createIpHandler), "xyz.openbmc_project.Network",
771         "/xyz/openbmc_project/network/" + ifaceId,
772         "xyz.openbmc_project.Network.IP.Create", "IP",
773         "xyz.openbmc_project.Network.IP.Protocol.IPv6", address, prefixLength,
774         "");
775 }
776 
777 /**
778  * Function that retrieves all properties for given Ethernet Interface
779  * Object
780  * from EntityManager Network Manager
781  * @param ethiface_id a eth interface id to query on DBus
782  * @param callback a function that shall be called to convert Dbus output
783  * into JSON
784  */
785 template <typename CallbackFunc>
786 void getEthernetIfaceData(const std::string& ethifaceId,
787                           CallbackFunc&& callback)
788 {
789     sdbusplus::message::object_path path("/xyz/openbmc_project/network");
790     dbus::utility::getManagedObjects(
791         "xyz.openbmc_project.Network", path,
792         [ethifaceId{std::string{ethifaceId}},
793          callback{std::forward<CallbackFunc>(callback)}](
794             const boost::system::error_code& ec,
795             const dbus::utility::ManagedObjectType& resp) {
796         EthernetInterfaceData ethData{};
797         std::vector<IPv4AddressData> ipv4Data;
798         std::vector<IPv6AddressData> ipv6Data;
799 
800         if (ec)
801         {
802             callback(false, ethData, ipv4Data, ipv6Data);
803             return;
804         }
805 
806         bool found = extractEthernetInterfaceData(ethifaceId, resp, ethData);
807         if (!found)
808         {
809             callback(false, ethData, ipv4Data, ipv6Data);
810             return;
811         }
812 
813         extractIPData(ethifaceId, resp, ipv4Data);
814         // Fix global GW
815         for (IPv4AddressData& ipv4 : ipv4Data)
816         {
817             if (((ipv4.linktype == LinkType::Global) &&
818                  (ipv4.gateway == "0.0.0.0")) ||
819                 (ipv4.origin == "DHCP") || (ipv4.origin == "Static"))
820             {
821                 ipv4.gateway = ethData.defaultGateway;
822             }
823         }
824 
825         extractIPV6Data(ethifaceId, resp, ipv6Data);
826         // Finally make a callback with useful data
827         callback(true, ethData, ipv4Data, ipv6Data);
828         });
829 }
830 
831 /**
832  * Function that retrieves all Ethernet Interfaces available through Network
833  * Manager
834  * @param callback a function that shall be called to convert Dbus output
835  * into JSON.
836  */
837 template <typename CallbackFunc>
838 void getEthernetIfaceList(CallbackFunc&& callback)
839 {
840     sdbusplus::message::object_path path("/xyz/openbmc_project/network");
841     dbus::utility::getManagedObjects(
842         "xyz.openbmc_project.Network", path,
843         [callback{std::forward<CallbackFunc>(callback)}](
844             const boost::system::error_code& ec,
845             const dbus::utility::ManagedObjectType& resp) {
846         // Callback requires vector<string> to retrieve all available
847         // ethernet interfaces
848         std::vector<std::string> ifaceList;
849         ifaceList.reserve(resp.size());
850         if (ec)
851         {
852             callback(false, ifaceList);
853             return;
854         }
855 
856         // Iterate over all retrieved ObjectPaths.
857         for (const auto& objpath : resp)
858         {
859             // And all interfaces available for certain ObjectPath.
860             for (const auto& interface : objpath.second)
861             {
862                 // If interface is
863                 // xyz.openbmc_project.Network.EthernetInterface, this is
864                 // what we're looking for.
865                 if (interface.first ==
866                     "xyz.openbmc_project.Network.EthernetInterface")
867                 {
868                     std::string ifaceId = objpath.first.filename();
869                     if (ifaceId.empty())
870                     {
871                         continue;
872                     }
873                     // and put it into output vector.
874                     ifaceList.emplace_back(ifaceId);
875                 }
876             }
877         }
878 
879         std::sort(ifaceList.begin(), ifaceList.end(),
880                   AlphanumLess<std::string>());
881 
882         // Finally make a callback with useful data
883         callback(true, ifaceList);
884         });
885 }
886 
887 inline void
888     handleHostnamePatch(const std::string& hostname,
889                         const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
890 {
891     // SHOULD handle host names of up to 255 characters(RFC 1123)
892     if (hostname.length() > 255)
893     {
894         messages::propertyValueFormatError(asyncResp->res, hostname,
895                                            "HostName");
896         return;
897     }
898     sdbusplus::asio::setProperty(
899         *crow::connections::systemBus, "xyz.openbmc_project.Network",
900         "/xyz/openbmc_project/network/config",
901         "xyz.openbmc_project.Network.SystemConfiguration", "HostName", hostname,
902         [asyncResp](const boost::system::error_code& ec) {
903         if (ec)
904         {
905             messages::internalError(asyncResp->res);
906         }
907         });
908 }
909 
910 inline void
911     handleMTUSizePatch(const std::string& ifaceId, const size_t mtuSize,
912                        const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
913 {
914     sdbusplus::message::object_path objPath = "/xyz/openbmc_project/network/" +
915                                               ifaceId;
916     sdbusplus::asio::setProperty(
917         *crow::connections::systemBus, "xyz.openbmc_project.Network", objPath,
918         "xyz.openbmc_project.Network.EthernetInterface", "MTU", mtuSize,
919         [asyncResp](const boost::system::error_code& ec) {
920         if (ec)
921         {
922             messages::internalError(asyncResp->res);
923         }
924         });
925 }
926 
927 inline void
928     handleDomainnamePatch(const std::string& ifaceId,
929                           const std::string& domainname,
930                           const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
931 {
932     std::vector<std::string> vectorDomainname = {domainname};
933     sdbusplus::asio::setProperty(
934         *crow::connections::systemBus, "xyz.openbmc_project.Network",
935         "/xyz/openbmc_project/network/" + ifaceId,
936         "xyz.openbmc_project.Network.EthernetInterface", "DomainName",
937         vectorDomainname, [asyncResp](const boost::system::error_code& ec) {
938             if (ec)
939             {
940                 messages::internalError(asyncResp->res);
941             }
942         });
943 }
944 
945 inline bool isHostnameValid(const std::string& hostname)
946 {
947     // A valid host name can never have the dotted-decimal form (RFC 1123)
948     if (std::all_of(hostname.begin(), hostname.end(), ::isdigit))
949     {
950         return false;
951     }
952     // Each label(hostname/subdomains) within a valid FQDN
953     // MUST handle host names of up to 63 characters (RFC 1123)
954     // labels cannot start or end with hyphens (RFC 952)
955     // labels can start with numbers (RFC 1123)
956     const static std::regex pattern(
957         "^[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9]$");
958 
959     return std::regex_match(hostname, pattern);
960 }
961 
962 inline bool isDomainnameValid(const std::string& domainname)
963 {
964     // Can have multiple subdomains
965     // Top Level Domain's min length is 2 character
966     const static std::regex pattern(
967         "^([A-Za-z0-9][a-zA-Z0-9\\-]{1,61}|[a-zA-Z0-9]{1,30}\\.)*[a-zA-Z]{2,}$");
968 
969     return std::regex_match(domainname, pattern);
970 }
971 
972 inline void handleFqdnPatch(const std::string& ifaceId, const std::string& fqdn,
973                             const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
974 {
975     // Total length of FQDN must not exceed 255 characters(RFC 1035)
976     if (fqdn.length() > 255)
977     {
978         messages::propertyValueFormatError(asyncResp->res, fqdn, "FQDN");
979         return;
980     }
981 
982     size_t pos = fqdn.find('.');
983     if (pos == std::string::npos)
984     {
985         messages::propertyValueFormatError(asyncResp->res, fqdn, "FQDN");
986         return;
987     }
988 
989     std::string hostname;
990     std::string domainname;
991     domainname = (fqdn).substr(pos + 1);
992     hostname = (fqdn).substr(0, pos);
993 
994     if (!isHostnameValid(hostname) || !isDomainnameValid(domainname))
995     {
996         messages::propertyValueFormatError(asyncResp->res, fqdn, "FQDN");
997         return;
998     }
999 
1000     handleHostnamePatch(hostname, asyncResp);
1001     handleDomainnamePatch(ifaceId, domainname, asyncResp);
1002 }
1003 
1004 inline void
1005     handleMACAddressPatch(const std::string& ifaceId,
1006                           const std::string& macAddress,
1007                           const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1008 {
1009     static constexpr std::string_view dbusNotAllowedError =
1010         "xyz.openbmc_project.Common.Error.NotAllowed";
1011 
1012     sdbusplus::asio::setProperty(
1013         *crow::connections::systemBus, "xyz.openbmc_project.Network",
1014         "/xyz/openbmc_project/network/" + ifaceId,
1015         "xyz.openbmc_project.Network.MACAddress", "MACAddress", macAddress,
1016         [asyncResp](const boost::system::error_code& ec,
1017                     const sdbusplus::message_t& msg) {
1018         if (ec)
1019         {
1020             const sd_bus_error* err = msg.get_error();
1021             if (err == nullptr)
1022             {
1023                 messages::internalError(asyncResp->res);
1024                 return;
1025             }
1026             if (err->name == dbusNotAllowedError)
1027             {
1028                 messages::propertyNotWritable(asyncResp->res, "MACAddress");
1029                 return;
1030             }
1031             messages::internalError(asyncResp->res);
1032             return;
1033         }
1034         });
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     sdbusplus::asio::setProperty(
1044         *crow::connections::systemBus, "xyz.openbmc_project.Network",
1045         "/xyz/openbmc_project/network/" + ifaceId,
1046         "xyz.openbmc_project.Network.EthernetInterface", propertyName, dhcp,
1047         [asyncResp](const boost::system::error_code& ec) {
1048         if (ec)
1049         {
1050             BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
1051             messages::internalError(asyncResp->res);
1052             return;
1053         }
1054         messages::success(asyncResp->res);
1055         });
1056 }
1057 
1058 inline void setEthernetInterfaceBoolProperty(
1059     const std::string& ifaceId, const std::string& propertyName,
1060     const bool& value, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1061 {
1062     sdbusplus::asio::setProperty(
1063         *crow::connections::systemBus, "xyz.openbmc_project.Network",
1064         "/xyz/openbmc_project/network/" + ifaceId,
1065         "xyz.openbmc_project.Network.EthernetInterface", propertyName, value,
1066         [asyncResp](const boost::system::error_code& ec) {
1067         if (ec)
1068         {
1069             BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
1070             messages::internalError(asyncResp->res);
1071             return;
1072         }
1073         });
1074 }
1075 
1076 inline void setDHCPv4Config(const std::string& propertyName, const bool& value,
1077                             const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1078 {
1079     BMCWEB_LOG_DEBUG << propertyName << " = " << value;
1080     sdbusplus::asio::setProperty(
1081         *crow::connections::systemBus, "xyz.openbmc_project.Network",
1082         "/xyz/openbmc_project/network/dhcp",
1083         "xyz.openbmc_project.Network.DHCPConfiguration", propertyName, value,
1084         [asyncResp](const boost::system::error_code& ec) {
1085         if (ec)
1086         {
1087             BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
1088             messages::internalError(asyncResp->res);
1089             return;
1090         }
1091         });
1092 }
1093 
1094 inline void handleDHCPPatch(const std::string& ifaceId,
1095                             const EthernetInterfaceData& ethData,
1096                             const DHCPParameters& v4dhcpParms,
1097                             const DHCPParameters& v6dhcpParms,
1098                             const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1099 {
1100     bool ipv4Active = translateDhcpEnabledToBool(ethData.dhcpEnabled, true);
1101     bool ipv6Active = translateDhcpEnabledToBool(ethData.dhcpEnabled, false);
1102 
1103     bool nextv4DHCPState =
1104         v4dhcpParms.dhcpv4Enabled ? *v4dhcpParms.dhcpv4Enabled : ipv4Active;
1105 
1106     bool nextv6DHCPState{};
1107     if (v6dhcpParms.dhcpv6OperatingMode)
1108     {
1109         if ((*v6dhcpParms.dhcpv6OperatingMode != "Stateful") &&
1110             (*v6dhcpParms.dhcpv6OperatingMode != "Stateless") &&
1111             (*v6dhcpParms.dhcpv6OperatingMode != "Disabled"))
1112         {
1113             messages::propertyValueFormatError(asyncResp->res,
1114                                                *v6dhcpParms.dhcpv6OperatingMode,
1115                                                "OperatingMode");
1116             return;
1117         }
1118         nextv6DHCPState = (*v6dhcpParms.dhcpv6OperatingMode == "Stateful");
1119     }
1120     else
1121     {
1122         nextv6DHCPState = ipv6Active;
1123     }
1124 
1125     bool nextDNS{};
1126     if (v4dhcpParms.useDnsServers && v6dhcpParms.useDnsServers)
1127     {
1128         if (*v4dhcpParms.useDnsServers != *v6dhcpParms.useDnsServers)
1129         {
1130             messages::generalError(asyncResp->res);
1131             return;
1132         }
1133         nextDNS = *v4dhcpParms.useDnsServers;
1134     }
1135     else if (v4dhcpParms.useDnsServers)
1136     {
1137         nextDNS = *v4dhcpParms.useDnsServers;
1138     }
1139     else if (v6dhcpParms.useDnsServers)
1140     {
1141         nextDNS = *v6dhcpParms.useDnsServers;
1142     }
1143     else
1144     {
1145         nextDNS = ethData.dnsEnabled;
1146     }
1147 
1148     bool nextNTP{};
1149     if (v4dhcpParms.useNtpServers && v6dhcpParms.useNtpServers)
1150     {
1151         if (*v4dhcpParms.useNtpServers != *v6dhcpParms.useNtpServers)
1152         {
1153             messages::generalError(asyncResp->res);
1154             return;
1155         }
1156         nextNTP = *v4dhcpParms.useNtpServers;
1157     }
1158     else if (v4dhcpParms.useNtpServers)
1159     {
1160         nextNTP = *v4dhcpParms.useNtpServers;
1161     }
1162     else if (v6dhcpParms.useNtpServers)
1163     {
1164         nextNTP = *v6dhcpParms.useNtpServers;
1165     }
1166     else
1167     {
1168         nextNTP = ethData.ntpEnabled;
1169     }
1170 
1171     bool nextUseDomain{};
1172     if (v4dhcpParms.useDomainName && v6dhcpParms.useDomainName)
1173     {
1174         if (*v4dhcpParms.useDomainName != *v6dhcpParms.useDomainName)
1175         {
1176             messages::generalError(asyncResp->res);
1177             return;
1178         }
1179         nextUseDomain = *v4dhcpParms.useDomainName;
1180     }
1181     else if (v4dhcpParms.useDomainName)
1182     {
1183         nextUseDomain = *v4dhcpParms.useDomainName;
1184     }
1185     else if (v6dhcpParms.useDomainName)
1186     {
1187         nextUseDomain = *v6dhcpParms.useDomainName;
1188     }
1189     else
1190     {
1191         nextUseDomain = ethData.hostNameEnabled;
1192     }
1193 
1194     BMCWEB_LOG_DEBUG << "set DHCPEnabled...";
1195     setDHCPEnabled(ifaceId, "DHCPEnabled", nextv4DHCPState, nextv6DHCPState,
1196                    asyncResp);
1197     BMCWEB_LOG_DEBUG << "set DNSEnabled...";
1198     setDHCPv4Config("DNSEnabled", nextDNS, asyncResp);
1199     BMCWEB_LOG_DEBUG << "set NTPEnabled...";
1200     setDHCPv4Config("NTPEnabled", nextNTP, asyncResp);
1201     BMCWEB_LOG_DEBUG << "set HostNameEnabled...";
1202     setDHCPv4Config("HostNameEnabled", nextUseDomain, asyncResp);
1203 }
1204 
1205 inline std::vector<IPv4AddressData>::const_iterator getNextStaticIpEntry(
1206     const std::vector<IPv4AddressData>::const_iterator& head,
1207     const std::vector<IPv4AddressData>::const_iterator& end)
1208 {
1209     return std::find_if(head, end, [](const IPv4AddressData& value) {
1210         return value.origin == "Static";
1211     });
1212 }
1213 
1214 inline std::vector<IPv6AddressData>::const_iterator getNextStaticIpEntry(
1215     const std::vector<IPv6AddressData>::const_iterator& head,
1216     const std::vector<IPv6AddressData>::const_iterator& end)
1217 {
1218     return std::find_if(head, end, [](const IPv6AddressData& value) {
1219         return value.origin == "Static";
1220     });
1221 }
1222 
1223 inline void
1224     handleIPv4StaticPatch(const std::string& ifaceId,
1225                           nlohmann::json::array_t& input,
1226                           const std::vector<IPv4AddressData>& ipv4Data,
1227                           const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1228 {
1229     if (input.empty())
1230     {
1231         messages::propertyValueTypeError(asyncResp->res, input,
1232                                          "IPv4StaticAddresses");
1233         return;
1234     }
1235 
1236     unsigned entryIdx = 1;
1237     // Find the first static IP address currently active on the NIC and
1238     // match it to the first JSON element in the IPv4StaticAddresses array.
1239     // Match each subsequent JSON element to the next static IP programmed
1240     // into the NIC.
1241     std::vector<IPv4AddressData>::const_iterator nicIpEntry =
1242         getNextStaticIpEntry(ipv4Data.cbegin(), ipv4Data.cend());
1243 
1244     for (nlohmann::json& thisJson : input)
1245     {
1246         std::string pathString = "IPv4StaticAddresses/" +
1247                                  std::to_string(entryIdx);
1248 
1249         if (!thisJson.is_null() && !thisJson.empty())
1250         {
1251             std::optional<std::string> address;
1252             std::optional<std::string> subnetMask;
1253             std::optional<std::string> gateway;
1254 
1255             if (!json_util::readJson(thisJson, asyncResp->res, "Address",
1256                                      address, "SubnetMask", subnetMask,
1257                                      "Gateway", gateway))
1258             {
1259                 messages::propertyValueFormatError(asyncResp->res, thisJson,
1260                                                    pathString);
1261                 return;
1262             }
1263 
1264             // Find the address/subnet/gateway values. Any values that are
1265             // not explicitly provided are assumed to be unmodified from the
1266             // current state of the interface. Merge existing state into the
1267             // current request.
1268             const std::string* addr = nullptr;
1269             const std::string* gw = nullptr;
1270             uint8_t prefixLength = 0;
1271             bool errorInEntry = false;
1272             if (address)
1273             {
1274                 if (ip_util::ipv4VerifyIpAndGetBitcount(*address))
1275                 {
1276                     addr = &(*address);
1277                 }
1278                 else
1279                 {
1280                     messages::propertyValueFormatError(asyncResp->res, *address,
1281                                                        pathString + "/Address");
1282                     errorInEntry = true;
1283                 }
1284             }
1285             else if (nicIpEntry != ipv4Data.cend())
1286             {
1287                 addr = &(nicIpEntry->address);
1288             }
1289             else
1290             {
1291                 messages::propertyMissing(asyncResp->res,
1292                                           pathString + "/Address");
1293                 errorInEntry = true;
1294             }
1295 
1296             if (subnetMask)
1297             {
1298                 if (!ip_util::ipv4VerifyIpAndGetBitcount(*subnetMask,
1299                                                          &prefixLength))
1300                 {
1301                     messages::propertyValueFormatError(
1302                         asyncResp->res, *subnetMask,
1303                         pathString + "/SubnetMask");
1304                     errorInEntry = true;
1305                 }
1306             }
1307             else if (nicIpEntry != ipv4Data.cend())
1308             {
1309                 if (!ip_util::ipv4VerifyIpAndGetBitcount(nicIpEntry->netmask,
1310                                                          &prefixLength))
1311                 {
1312                     messages::propertyValueFormatError(
1313                         asyncResp->res, nicIpEntry->netmask,
1314                         pathString + "/SubnetMask");
1315                     errorInEntry = true;
1316                 }
1317             }
1318             else
1319             {
1320                 messages::propertyMissing(asyncResp->res,
1321                                           pathString + "/SubnetMask");
1322                 errorInEntry = true;
1323             }
1324 
1325             if (gateway)
1326             {
1327                 if (ip_util::ipv4VerifyIpAndGetBitcount(*gateway))
1328                 {
1329                     gw = &(*gateway);
1330                 }
1331                 else
1332                 {
1333                     messages::propertyValueFormatError(asyncResp->res, *gateway,
1334                                                        pathString + "/Gateway");
1335                     errorInEntry = true;
1336                 }
1337             }
1338             else if (nicIpEntry != ipv4Data.cend())
1339             {
1340                 gw = &nicIpEntry->gateway;
1341             }
1342             else
1343             {
1344                 messages::propertyMissing(asyncResp->res,
1345                                           pathString + "/Gateway");
1346                 errorInEntry = true;
1347             }
1348 
1349             if (errorInEntry)
1350             {
1351                 return;
1352             }
1353 
1354             if (nicIpEntry != ipv4Data.cend())
1355             {
1356                 deleteAndCreateIPAddress(IpVersion::IpV4, ifaceId,
1357                                          nicIpEntry->id, prefixLength, *gw,
1358                                          *addr, asyncResp);
1359                 nicIpEntry = getNextStaticIpEntry(++nicIpEntry,
1360                                                   ipv4Data.cend());
1361             }
1362             else
1363             {
1364                 createIPv4(ifaceId, prefixLength, *gateway, *address,
1365                            asyncResp);
1366             }
1367             entryIdx++;
1368         }
1369         else
1370         {
1371             if (nicIpEntry == ipv4Data.cend())
1372             {
1373                 // Requesting a DELETE/DO NOT MODIFY action for an item
1374                 // that isn't present on the eth(n) interface. Input JSON is
1375                 // in error, so bail out.
1376                 if (thisJson.is_null())
1377                 {
1378                     messages::resourceCannotBeDeleted(asyncResp->res);
1379                     return;
1380                 }
1381                 messages::propertyValueFormatError(asyncResp->res, thisJson,
1382                                                    pathString);
1383                 return;
1384             }
1385 
1386             if (thisJson.is_null())
1387             {
1388                 deleteIPAddress(ifaceId, nicIpEntry->id, asyncResp);
1389             }
1390             if (nicIpEntry != ipv4Data.cend())
1391             {
1392                 nicIpEntry = getNextStaticIpEntry(++nicIpEntry,
1393                                                   ipv4Data.cend());
1394             }
1395             entryIdx++;
1396         }
1397     }
1398 }
1399 
1400 inline void handleStaticNameServersPatch(
1401     const std::string& ifaceId,
1402     const std::vector<std::string>& updatedStaticNameServers,
1403     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1404 {
1405     sdbusplus::asio::setProperty(
1406         *crow::connections::systemBus, "xyz.openbmc_project.Network",
1407         "/xyz/openbmc_project/network/" + ifaceId,
1408         "xyz.openbmc_project.Network.EthernetInterface", "StaticNameServers",
1409         updatedStaticNameServers,
1410         [asyncResp](const boost::system::error_code& ec) {
1411         if (ec)
1412         {
1413             messages::internalError(asyncResp->res);
1414             return;
1415         }
1416         });
1417 }
1418 
1419 inline void handleIPv6StaticAddressesPatch(
1420     const std::string& ifaceId, const nlohmann::json::array_t& input,
1421     const std::vector<IPv6AddressData>& ipv6Data,
1422     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1423 {
1424     if (input.empty())
1425     {
1426         messages::propertyValueTypeError(asyncResp->res, input,
1427                                          "IPv6StaticAddresses");
1428         return;
1429     }
1430     size_t entryIdx = 1;
1431     std::vector<IPv6AddressData>::const_iterator nicIpEntry =
1432         getNextStaticIpEntry(ipv6Data.cbegin(), ipv6Data.cend());
1433     for (const nlohmann::json& thisJson : input)
1434     {
1435         std::string pathString = "IPv6StaticAddresses/" +
1436                                  std::to_string(entryIdx);
1437 
1438         if (!thisJson.is_null() && !thisJson.empty())
1439         {
1440             std::optional<std::string> address;
1441             std::optional<uint8_t> prefixLength;
1442             nlohmann::json thisJsonCopy = thisJson;
1443             if (!json_util::readJson(thisJsonCopy, asyncResp->res, "Address",
1444                                      address, "PrefixLength", prefixLength))
1445             {
1446                 messages::propertyValueFormatError(asyncResp->res, thisJson,
1447                                                    pathString);
1448                 return;
1449             }
1450 
1451             const std::string* addr = nullptr;
1452             uint8_t prefix = 0;
1453 
1454             // Find the address and prefixLength values. Any values that are
1455             // not explicitly provided are assumed to be unmodified from the
1456             // current state of the interface. Merge existing state into the
1457             // current request.
1458             if (address)
1459             {
1460                 addr = &(*address);
1461             }
1462             else if (nicIpEntry != ipv6Data.end())
1463             {
1464                 addr = &(nicIpEntry->address);
1465             }
1466             else
1467             {
1468                 messages::propertyMissing(asyncResp->res,
1469                                           pathString + "/Address");
1470                 return;
1471             }
1472 
1473             if (prefixLength)
1474             {
1475                 prefix = *prefixLength;
1476             }
1477             else if (nicIpEntry != ipv6Data.end())
1478             {
1479                 prefix = nicIpEntry->prefixLength;
1480             }
1481             else
1482             {
1483                 messages::propertyMissing(asyncResp->res,
1484                                           pathString + "/PrefixLength");
1485                 return;
1486             }
1487 
1488             if (nicIpEntry != ipv6Data.end())
1489             {
1490                 deleteAndCreateIPAddress(IpVersion::IpV6, ifaceId,
1491                                          nicIpEntry->id, prefix, "", *addr,
1492                                          asyncResp);
1493                 nicIpEntry = getNextStaticIpEntry(++nicIpEntry,
1494                                                   ipv6Data.cend());
1495             }
1496             else
1497             {
1498                 createIPv6(ifaceId, *prefixLength, *addr, asyncResp);
1499             }
1500             entryIdx++;
1501         }
1502         else
1503         {
1504             if (nicIpEntry == ipv6Data.end())
1505             {
1506                 // Requesting a DELETE/DO NOT MODIFY action for an item
1507                 // that isn't present on the eth(n) interface. Input JSON is
1508                 // in error, so bail out.
1509                 if (thisJson.is_null())
1510                 {
1511                     messages::resourceCannotBeDeleted(asyncResp->res);
1512                     return;
1513                 }
1514                 messages::propertyValueFormatError(asyncResp->res, thisJson,
1515                                                    pathString);
1516                 return;
1517             }
1518 
1519             if (thisJson.is_null())
1520             {
1521                 deleteIPAddress(ifaceId, nicIpEntry->id, asyncResp);
1522             }
1523             if (nicIpEntry != ipv6Data.cend())
1524             {
1525                 nicIpEntry = getNextStaticIpEntry(++nicIpEntry,
1526                                                   ipv6Data.cend());
1527             }
1528             entryIdx++;
1529         }
1530     }
1531 }
1532 
1533 inline std::string extractParentInterfaceName(const std::string& ifaceId)
1534 {
1535     std::size_t pos = ifaceId.find('_');
1536     return ifaceId.substr(0, pos);
1537 }
1538 
1539 inline void
1540     parseInterfaceData(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1541                        const std::string& ifaceId,
1542                        const EthernetInterfaceData& ethData,
1543                        const std::vector<IPv4AddressData>& ipv4Data,
1544                        const std::vector<IPv6AddressData>& ipv6Data)
1545 {
1546     nlohmann::json& jsonResponse = asyncResp->res.jsonValue;
1547     jsonResponse["Id"] = ifaceId;
1548     jsonResponse["@odata.id"] = boost::urls::format(
1549         "/redfish/v1/Managers/bmc/EthernetInterfaces/{}", ifaceId);
1550     jsonResponse["InterfaceEnabled"] = ethData.nicEnabled;
1551 
1552     if constexpr (bmcwebEnableHealthPopulate)
1553     {
1554         constexpr std::array<std::string_view, 1> inventoryForEthernet = {
1555             "xyz.openbmc_project.Inventory.Item.Ethernet"};
1556         auto health = std::make_shared<HealthPopulate>(asyncResp);
1557         dbus::utility::getSubTreePaths(
1558             "/", 0, inventoryForEthernet,
1559             [health](const boost::system::error_code& ec,
1560                      const dbus::utility::MapperGetSubTreePathsResponse& resp) {
1561             if (ec)
1562             {
1563                 return;
1564             }
1565 
1566             health->inventory = resp;
1567             });
1568 
1569         health->populate();
1570     }
1571 
1572     if (ethData.nicEnabled)
1573     {
1574         jsonResponse["LinkStatus"] = ethData.linkUp ? "LinkUp" : "LinkDown";
1575         jsonResponse["Status"]["State"] = "Enabled";
1576     }
1577     else
1578     {
1579         jsonResponse["LinkStatus"] = "NoLink";
1580         jsonResponse["Status"]["State"] = "Disabled";
1581     }
1582 
1583     jsonResponse["SpeedMbps"] = ethData.speed;
1584     jsonResponse["MTUSize"] = ethData.mtuSize;
1585     jsonResponse["MACAddress"] = ethData.macAddress;
1586     jsonResponse["DHCPv4"]["DHCPEnabled"] =
1587         translateDhcpEnabledToBool(ethData.dhcpEnabled, true);
1588     jsonResponse["DHCPv4"]["UseNTPServers"] = ethData.ntpEnabled;
1589     jsonResponse["DHCPv4"]["UseDNSServers"] = ethData.dnsEnabled;
1590     jsonResponse["DHCPv4"]["UseDomainName"] = ethData.hostNameEnabled;
1591 
1592     jsonResponse["DHCPv6"]["OperatingMode"] =
1593         translateDhcpEnabledToBool(ethData.dhcpEnabled, false) ? "Stateful"
1594                                                                : "Disabled";
1595     jsonResponse["DHCPv6"]["UseNTPServers"] = ethData.ntpEnabled;
1596     jsonResponse["DHCPv6"]["UseDNSServers"] = ethData.dnsEnabled;
1597     jsonResponse["DHCPv6"]["UseDomainName"] = ethData.hostNameEnabled;
1598 
1599     if (!ethData.hostName.empty())
1600     {
1601         jsonResponse["HostName"] = ethData.hostName;
1602 
1603         // When domain name is empty then it means, that it is a network
1604         // without domain names, and the host name itself must be treated as
1605         // FQDN
1606         std::string fqdn = ethData.hostName;
1607         if (!ethData.domainnames.empty())
1608         {
1609             fqdn += "." + ethData.domainnames[0];
1610         }
1611         jsonResponse["FQDN"] = fqdn;
1612     }
1613 
1614     if (ethData.vlanId)
1615     {
1616         jsonResponse["EthernetInterfaceType"] = "Virtual";
1617         jsonResponse["VLAN"]["VLANEnable"] = true;
1618         jsonResponse["VLAN"]["VLANId"] = *ethData.vlanId;
1619         jsonResponse["VLAN"]["Tagged"] = true;
1620 
1621         nlohmann::json::array_t relatedInterfaces;
1622         nlohmann::json& parentInterface = relatedInterfaces.emplace_back();
1623         parentInterface["@odata.id"] =
1624             boost::urls::format("/redfish/v1/Managers/bmc/EthernetInterfaces",
1625                                 extractParentInterfaceName(ifaceId));
1626         jsonResponse["Links"]["RelatedInterfaces"] =
1627             std::move(relatedInterfaces);
1628     }
1629     else
1630     {
1631         jsonResponse["EthernetInterfaceType"] = "Physical";
1632     }
1633 
1634     jsonResponse["NameServers"] = ethData.nameServers;
1635     jsonResponse["StaticNameServers"] = ethData.staticNameServers;
1636 
1637     nlohmann::json& ipv4Array = jsonResponse["IPv4Addresses"];
1638     nlohmann::json& ipv4StaticArray = jsonResponse["IPv4StaticAddresses"];
1639     ipv4Array = nlohmann::json::array();
1640     ipv4StaticArray = nlohmann::json::array();
1641     for (const auto& ipv4Config : ipv4Data)
1642     {
1643         std::string gatewayStr = ipv4Config.gateway;
1644         if (gatewayStr.empty())
1645         {
1646             gatewayStr = "0.0.0.0";
1647         }
1648         nlohmann::json::object_t ipv4;
1649         ipv4["AddressOrigin"] = ipv4Config.origin;
1650         ipv4["SubnetMask"] = ipv4Config.netmask;
1651         ipv4["Address"] = ipv4Config.address;
1652         ipv4["Gateway"] = gatewayStr;
1653 
1654         if (ipv4Config.origin == "Static")
1655         {
1656             ipv4StaticArray.push_back(ipv4);
1657         }
1658 
1659         ipv4Array.emplace_back(std::move(ipv4));
1660     }
1661 
1662     std::string ipv6GatewayStr = ethData.ipv6DefaultGateway;
1663     if (ipv6GatewayStr.empty())
1664     {
1665         ipv6GatewayStr = "0:0:0:0:0:0:0:0";
1666     }
1667 
1668     jsonResponse["IPv6DefaultGateway"] = ipv6GatewayStr;
1669 
1670     nlohmann::json& ipv6Array = jsonResponse["IPv6Addresses"];
1671     nlohmann::json& ipv6StaticArray = jsonResponse["IPv6StaticAddresses"];
1672     ipv6Array = nlohmann::json::array();
1673     ipv6StaticArray = nlohmann::json::array();
1674     nlohmann::json& ipv6AddrPolicyTable =
1675         jsonResponse["IPv6AddressPolicyTable"];
1676     ipv6AddrPolicyTable = nlohmann::json::array();
1677     for (const auto& ipv6Config : ipv6Data)
1678     {
1679         nlohmann::json::object_t ipv6;
1680         ipv6["Address"] = ipv6Config.address;
1681         ipv6["PrefixLength"] = ipv6Config.prefixLength;
1682         ipv6["AddressOrigin"] = ipv6Config.origin;
1683 
1684         ipv6Array.emplace_back(std::move(ipv6));
1685         if (ipv6Config.origin == "Static")
1686         {
1687             nlohmann::json::object_t ipv6Static;
1688             ipv6Static["Address"] = ipv6Config.address;
1689             ipv6Static["PrefixLength"] = ipv6Config.prefixLength;
1690             ipv6StaticArray.emplace_back(std::move(ipv6Static));
1691         }
1692     }
1693 }
1694 
1695 inline void afterDelete(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1696                         const std::string& ifaceId,
1697                         const boost::system::error_code& ec,
1698                         const sdbusplus::message_t& m)
1699 {
1700     if (!ec)
1701     {
1702         return;
1703     }
1704     const sd_bus_error* dbusError = m.get_error();
1705     if (dbusError == nullptr)
1706     {
1707         messages::internalError(asyncResp->res);
1708         return;
1709     }
1710     BMCWEB_LOG_DEBUG << "DBus error: " << dbusError->name;
1711 
1712     if (std::string_view("org.freedesktop.DBus.Error.UnknownObject") ==
1713         dbusError->name)
1714     {
1715         messages::resourceNotFound(asyncResp->res, "EthernetInterface",
1716                                    ifaceId);
1717         return;
1718     }
1719     if (std::string_view("org.freedesktop.DBus.Error.UnknownMethod") ==
1720         dbusError->name)
1721     {
1722         messages::resourceCannotBeDeleted(asyncResp->res);
1723         return;
1724     }
1725     messages::internalError(asyncResp->res);
1726 }
1727 
1728 inline void afterVlanCreate(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1729                             const std::string& parentInterfaceUri,
1730                             const std::string& vlanInterface,
1731                             const boost::system::error_code& ec,
1732                             const sdbusplus::message_t& m
1733 
1734 )
1735 {
1736     if (ec)
1737     {
1738         const sd_bus_error* dbusError = m.get_error();
1739         if (dbusError == nullptr)
1740         {
1741             messages::internalError(asyncResp->res);
1742             return;
1743         }
1744         BMCWEB_LOG_DEBUG << "DBus error: " << dbusError->name;
1745 
1746         if (std::string_view(
1747                 "xyz.openbmc_project.Common.Error.ResourceNotFound") ==
1748             dbusError->name)
1749         {
1750             messages::propertyValueNotInList(
1751                 asyncResp->res, parentInterfaceUri,
1752                 "Links/RelatedInterfaces/0/@odata.id");
1753             return;
1754         }
1755         if (std::string_view(
1756                 "xyz.openbmc_project.Common.Error.InvalidArgument") ==
1757             dbusError->name)
1758         {
1759             messages::resourceAlreadyExists(asyncResp->res, "EthernetInterface",
1760                                             "Id", vlanInterface);
1761             return;
1762         }
1763         messages::internalError(asyncResp->res);
1764         return;
1765     }
1766 
1767     const boost::urls::url vlanInterfaceUri = boost::urls::format(
1768         "/redfish/v1/Managers/bmc/EthernetInterfaces/{}", vlanInterface);
1769     asyncResp->res.addHeader("Location", vlanInterfaceUri.buffer());
1770 }
1771 
1772 inline void requestEthernetInterfacesRoutes(App& app)
1773 {
1774     BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/EthernetInterfaces/")
1775         .privileges(redfish::privileges::getEthernetInterfaceCollection)
1776         .methods(boost::beast::http::verb::get)(
1777             [&app](const crow::Request& req,
1778                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1779         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1780         {
1781             return;
1782         }
1783 
1784         asyncResp->res.jsonValue["@odata.type"] =
1785             "#EthernetInterfaceCollection.EthernetInterfaceCollection";
1786         asyncResp->res.jsonValue["@odata.id"] =
1787             "/redfish/v1/Managers/bmc/EthernetInterfaces";
1788         asyncResp->res.jsonValue["Name"] =
1789             "Ethernet Network Interface Collection";
1790         asyncResp->res.jsonValue["Description"] =
1791             "Collection of EthernetInterfaces for this Manager";
1792 
1793         // Get eth interface list, and call the below callback for JSON
1794         // preparation
1795         getEthernetIfaceList(
1796             [asyncResp](const bool& success,
1797                         const std::vector<std::string>& ifaceList) {
1798             if (!success)
1799             {
1800                 messages::internalError(asyncResp->res);
1801                 return;
1802             }
1803 
1804             nlohmann::json& ifaceArray = asyncResp->res.jsonValue["Members"];
1805             ifaceArray = nlohmann::json::array();
1806             for (const std::string& ifaceItem : ifaceList)
1807             {
1808                 nlohmann::json::object_t iface;
1809                 iface["@odata.id"] = boost::urls::format(
1810                     "/redfish/v1/Managers/bmc/EthernetInterfaces/{}",
1811                     ifaceItem);
1812                 ifaceArray.push_back(std::move(iface));
1813             }
1814 
1815             asyncResp->res.jsonValue["Members@odata.count"] = ifaceArray.size();
1816             asyncResp->res.jsonValue["@odata.id"] =
1817                 "/redfish/v1/Managers/bmc/EthernetInterfaces";
1818         });
1819         });
1820 
1821     BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/EthernetInterfaces/")
1822         .privileges(redfish::privileges::postEthernetInterfaceCollection)
1823         .methods(boost::beast::http::verb::post)(
1824             [&app](const crow::Request& req,
1825                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1826         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1827         {
1828             return;
1829         }
1830 
1831         bool vlanEnable = false;
1832         uint32_t vlanId = 0;
1833         nlohmann::json::array_t relatedInterfaces;
1834 
1835         if (!json_util::readJsonPatch(req, asyncResp->res, "VLAN/VLANEnable",
1836                                       vlanEnable, "VLAN/VLANId", vlanId,
1837                                       "Links/RelatedInterfaces",
1838                                       relatedInterfaces))
1839         {
1840             return;
1841         }
1842 
1843         if (relatedInterfaces.size() != 1)
1844         {
1845             messages::arraySizeTooLong(asyncResp->res,
1846                                        "Links/RelatedInterfaces",
1847                                        relatedInterfaces.size());
1848             return;
1849         }
1850 
1851         std::string parentInterfaceUri;
1852         if (!json_util::readJson(relatedInterfaces[0], asyncResp->res,
1853                                  "@odata.id", parentInterfaceUri))
1854         {
1855             messages::propertyMissing(asyncResp->res,
1856                                       "Links/RelatedInterfaces/0/@odata.id");
1857             return;
1858         }
1859         BMCWEB_LOG_INFO << "Parent Interface URI: " << parentInterfaceUri;
1860 
1861         boost::urls::result<boost::urls::url_view> parsedUri =
1862             boost::urls::parse_relative_ref(parentInterfaceUri);
1863         if (!parsedUri)
1864         {
1865             messages::propertyValueFormatError(
1866                 asyncResp->res, parentInterfaceUri,
1867                 "Links/RelatedInterfaces/0/@odata.id");
1868             return;
1869         }
1870 
1871         std::string parentInterface;
1872         if (!crow::utility::readUrlSegments(
1873                 *parsedUri, "redfish", "v1", "Managers", "bmc",
1874                 "EthernetInterfaces", std::ref(parentInterface)))
1875         {
1876             messages::propertyValueNotInList(
1877                 asyncResp->res, parentInterfaceUri,
1878                 "Links/RelatedInterfaces/0/@odata.id");
1879             return;
1880         }
1881 
1882         if (!vlanEnable)
1883         {
1884             // In OpenBMC implementation, VLANEnable cannot be false on
1885             // create
1886             messages::propertyValueIncorrect(asyncResp->res, "VLAN/VLANEnable",
1887                                              "false");
1888             return;
1889         }
1890 
1891         std::string vlanInterface = parentInterface + "_" +
1892                                     std::to_string(vlanId);
1893         crow::connections::systemBus->async_method_call(
1894             [asyncResp, parentInterfaceUri,
1895              vlanInterface](const boost::system::error_code& ec,
1896                             const sdbusplus::message_t& m) {
1897             afterVlanCreate(asyncResp, parentInterfaceUri, vlanInterface, ec,
1898                             m);
1899             },
1900             "xyz.openbmc_project.Network", "/xyz/openbmc_project/network",
1901             "xyz.openbmc_project.Network.VLAN.Create", "VLAN", parentInterface,
1902             vlanId);
1903         });
1904 
1905     BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/EthernetInterfaces/<str>/")
1906         .privileges(redfish::privileges::getEthernetInterface)
1907         .methods(boost::beast::http::verb::get)(
1908             [&app](const crow::Request& req,
1909                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1910                    const std::string& ifaceId) {
1911         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1912         {
1913             return;
1914         }
1915         getEthernetIfaceData(
1916             ifaceId,
1917             [asyncResp, ifaceId](const bool& success,
1918                                  const EthernetInterfaceData& ethData,
1919                                  const std::vector<IPv4AddressData>& ipv4Data,
1920                                  const std::vector<IPv6AddressData>& ipv6Data) {
1921             if (!success)
1922             {
1923                 // TODO(Pawel)consider distinguish between non
1924                 // existing object, and other errors
1925                 messages::resourceNotFound(asyncResp->res, "EthernetInterface",
1926                                            ifaceId);
1927                 return;
1928             }
1929 
1930             asyncResp->res.jsonValue["@odata.type"] =
1931                 "#EthernetInterface.v1_9_0.EthernetInterface";
1932             asyncResp->res.jsonValue["Name"] = "Manager Ethernet Interface";
1933             asyncResp->res.jsonValue["Description"] =
1934                 "Management Network Interface";
1935 
1936             parseInterfaceData(asyncResp, ifaceId, ethData, ipv4Data, ipv6Data);
1937             });
1938         });
1939 
1940     BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/EthernetInterfaces/<str>/")
1941         .privileges(redfish::privileges::patchEthernetInterface)
1942         .methods(boost::beast::http::verb::patch)(
1943             [&app](const crow::Request& req,
1944                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1945                    const std::string& ifaceId) {
1946         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1947         {
1948             return;
1949         }
1950         std::optional<std::string> hostname;
1951         std::optional<std::string> fqdn;
1952         std::optional<std::string> macAddress;
1953         std::optional<std::string> ipv6DefaultGateway;
1954         std::optional<nlohmann::json::array_t> ipv4StaticAddresses;
1955         std::optional<nlohmann::json::array_t> ipv6StaticAddresses;
1956         std::optional<std::vector<std::string>> staticNameServers;
1957         std::optional<nlohmann::json> dhcpv4;
1958         std::optional<nlohmann::json> dhcpv6;
1959         std::optional<bool> interfaceEnabled;
1960         std::optional<size_t> mtuSize;
1961         DHCPParameters v4dhcpParms;
1962         DHCPParameters v6dhcpParms;
1963 
1964         if (!json_util::readJsonPatch(
1965                 req, asyncResp->res, "HostName", hostname, "FQDN", fqdn,
1966                 "IPv4StaticAddresses", ipv4StaticAddresses, "MACAddress",
1967                 macAddress, "StaticNameServers", staticNameServers,
1968                 "IPv6DefaultGateway", ipv6DefaultGateway, "IPv6StaticAddresses",
1969                 ipv6StaticAddresses, "DHCPv4", dhcpv4, "DHCPv6", dhcpv6,
1970                 "MTUSize", mtuSize, "InterfaceEnabled", interfaceEnabled))
1971         {
1972             return;
1973         }
1974         if (dhcpv4)
1975         {
1976             if (!json_util::readJson(*dhcpv4, asyncResp->res, "DHCPEnabled",
1977                                      v4dhcpParms.dhcpv4Enabled, "UseDNSServers",
1978                                      v4dhcpParms.useDnsServers, "UseNTPServers",
1979                                      v4dhcpParms.useNtpServers, "UseDomainName",
1980                                      v4dhcpParms.useDomainName))
1981             {
1982                 return;
1983             }
1984         }
1985 
1986         if (dhcpv6)
1987         {
1988             if (!json_util::readJson(*dhcpv6, asyncResp->res, "OperatingMode",
1989                                      v6dhcpParms.dhcpv6OperatingMode,
1990                                      "UseDNSServers", v6dhcpParms.useDnsServers,
1991                                      "UseNTPServers", v6dhcpParms.useNtpServers,
1992                                      "UseDomainName",
1993                                      v6dhcpParms.useDomainName))
1994             {
1995                 return;
1996             }
1997         }
1998 
1999         // Get single eth interface data, and call the below callback
2000         // for JSON preparation
2001         getEthernetIfaceData(
2002             ifaceId,
2003             [asyncResp, ifaceId, hostname = std::move(hostname),
2004              fqdn = std::move(fqdn), macAddress = std::move(macAddress),
2005              ipv4StaticAddresses = std::move(ipv4StaticAddresses),
2006              ipv6DefaultGateway = std::move(ipv6DefaultGateway),
2007              ipv6StaticAddresses = std::move(ipv6StaticAddresses),
2008              staticNameServers = std::move(staticNameServers),
2009              dhcpv4 = std::move(dhcpv4), dhcpv6 = std::move(dhcpv6), mtuSize,
2010              v4dhcpParms = std::move(v4dhcpParms),
2011              v6dhcpParms = std::move(v6dhcpParms), interfaceEnabled](
2012                 const bool& success, const EthernetInterfaceData& ethData,
2013                 const std::vector<IPv4AddressData>& ipv4Data,
2014                 const std::vector<IPv6AddressData>& ipv6Data) {
2015             if (!success)
2016             {
2017                 // ... otherwise return error
2018                 // TODO(Pawel)consider distinguish between non
2019                 // existing object, and other errors
2020                 messages::resourceNotFound(asyncResp->res, "EthernetInterface",
2021                                            ifaceId);
2022                 return;
2023             }
2024 
2025             if (dhcpv4 || dhcpv6)
2026             {
2027                 handleDHCPPatch(ifaceId, ethData, v4dhcpParms, v6dhcpParms,
2028                                 asyncResp);
2029             }
2030 
2031             if (hostname)
2032             {
2033                 handleHostnamePatch(*hostname, asyncResp);
2034             }
2035 
2036             if (fqdn)
2037             {
2038                 handleFqdnPatch(ifaceId, *fqdn, asyncResp);
2039             }
2040 
2041             if (macAddress)
2042             {
2043                 handleMACAddressPatch(ifaceId, *macAddress, asyncResp);
2044             }
2045 
2046             if (ipv4StaticAddresses)
2047             {
2048                 // TODO(ed) for some reason the capture of
2049                 // ipv4Addresses above is returning a const value,
2050                 // not a non-const value. This doesn't really work
2051                 // for us, as we need to be able to efficiently move
2052                 // out the intermedia nlohmann::json objects. This
2053                 // makes a copy of the structure, and operates on
2054                 // that, but could be done more efficiently
2055                 nlohmann::json::array_t ipv4Static = *ipv4StaticAddresses;
2056                 handleIPv4StaticPatch(ifaceId, ipv4Static, ipv4Data, asyncResp);
2057             }
2058 
2059             if (staticNameServers)
2060             {
2061                 handleStaticNameServersPatch(ifaceId, *staticNameServers,
2062                                              asyncResp);
2063             }
2064 
2065             if (ipv6DefaultGateway)
2066             {
2067                 messages::propertyNotWritable(asyncResp->res,
2068                                               "IPv6DefaultGateway");
2069             }
2070 
2071             if (ipv6StaticAddresses)
2072             {
2073                 handleIPv6StaticAddressesPatch(ifaceId, *ipv6StaticAddresses,
2074                                                ipv6Data, asyncResp);
2075             }
2076 
2077             if (interfaceEnabled)
2078             {
2079                 setEthernetInterfaceBoolProperty(ifaceId, "NICEnabled",
2080                                                  *interfaceEnabled, asyncResp);
2081             }
2082 
2083             if (mtuSize)
2084             {
2085                 handleMTUSizePatch(ifaceId, *mtuSize, asyncResp);
2086             }
2087             });
2088         });
2089 
2090     BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/EthernetInterfaces/<str>/")
2091         .privileges(redfish::privileges::deleteEthernetInterface)
2092         .methods(boost::beast::http::verb::delete_)(
2093             [&app](const crow::Request& req,
2094                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2095                    const std::string& ifaceId) {
2096         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2097         {
2098             return;
2099         }
2100 
2101         crow::connections::systemBus->async_method_call(
2102             [asyncResp, ifaceId](const boost::system::error_code& ec,
2103                                  const sdbusplus::message_t& m) {
2104             afterDelete(asyncResp, ifaceId, ec, m);
2105             },
2106             "xyz.openbmc_project.Network",
2107             std::string("/xyz/openbmc_project/network/") + ifaceId,
2108             "xyz.openbmc_project.Object.Delete", "Delete");
2109         });
2110 }
2111 
2112 } // namespace redfish
2113