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