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