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