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