xref: /openbmc/bmcweb/redfish-core/lib/ethernet.hpp (revision 177612aaa0633cf9d5aef0b763a43135cf552d9b)
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         nicIpEntry++;
1665         nicIpEntry = getNextStaticIpEntry(nicIpEntry, ipv4Data.cend());
1666         entryIdx++;
1667     }
1668 
1669     // Delete the remaining IPs
1670     while (nicIpEntry != ipv4Data.cend())
1671     {
1672         AddressPatch& thisAddress = addressesOut.emplace_back();
1673         thisAddress.operation = AddrChange::Delete;
1674         thisAddress.existingDbusId = nicIpEntry->id;
1675         nicIpEntry++;
1676         nicIpEntry = getNextStaticIpEntry(nicIpEntry, ipv4Data.cend());
1677     }
1678 
1679     return true;
1680 }
1681 
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)1682 inline void handleIPv4StaticPatch(
1683     const std::string& ifaceId,
1684     std::vector<std::variant<nlohmann::json::object_t, std::nullptr_t>>& input,
1685     const EthernetInterfaceData& ethData,
1686     const std::vector<IPv4AddressData>& ipv4Data,
1687     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1688 {
1689     std::vector<AddressPatch> addresses;
1690     std::string gatewayOut;
1691     if (!parseAddresses(input, ipv4Data, asyncResp->res, addresses, gatewayOut))
1692     {
1693         return;
1694     }
1695 
1696     // If we're setting the gateway to something new, delete the
1697     // existing so we won't conflict
1698     if (!ethData.defaultGateway.empty() && ethData.defaultGateway != gatewayOut)
1699     {
1700         updateIPv4DefaultGateway(ifaceId, "", asyncResp);
1701     }
1702 
1703     for (const AddressPatch& address : addresses)
1704     {
1705         switch (address.operation)
1706         {
1707             case AddrChange::Delete:
1708             {
1709                 BMCWEB_LOG_ERROR("Deleting id {} on interface {}",
1710                                  address.existingDbusId, ifaceId);
1711                 deleteIPAddress(ifaceId, address.existingDbusId, asyncResp);
1712             }
1713             break;
1714             case AddrChange::Update:
1715             {
1716                 // Update is a delete then a recreate
1717                 // Only need to update if there is an existing ip at this index
1718                 if (!address.existingDbusId.empty())
1719                 {
1720                     BMCWEB_LOG_ERROR("Deleting id {} on interface {}",
1721                                      address.existingDbusId, ifaceId);
1722                     deleteAndCreateIPAddress(
1723                         IpVersion::IpV4, ifaceId, address.existingDbusId,
1724                         address.prefixLength, address.address, address.gateway,
1725                         asyncResp);
1726                 }
1727                 else
1728                 {
1729                     // Otherwise, just create a new one
1730                     BMCWEB_LOG_ERROR(
1731                         "creating ip {} prefix {} gateway {} on interface {}",
1732                         address.address, address.prefixLength, address.gateway,
1733                         ifaceId);
1734                     createIPv4(ifaceId, address.prefixLength, address.gateway,
1735                                address.address, asyncResp);
1736                 }
1737             }
1738             break;
1739             default:
1740             {
1741                 // Leave alone
1742             }
1743             break;
1744         }
1745     }
1746 
1747     // now update to the new gateway.
1748     // Default gateway is already empty, so no need to update if we're clearing
1749     if (!gatewayOut.empty() && ethData.defaultGateway != gatewayOut)
1750     {
1751         updateIPv4DefaultGateway(ifaceId, gatewayOut, asyncResp);
1752     }
1753 }
1754 
handleStaticNameServersPatch(const std::string & ifaceId,const std::vector<std::string> & updatedStaticNameServers,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)1755 inline void handleStaticNameServersPatch(
1756     const std::string& ifaceId,
1757     const std::vector<std::string>& updatedStaticNameServers,
1758     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1759 {
1760     setDbusProperty(
1761         asyncResp, "StaticNameServers", "xyz.openbmc_project.Network",
1762         sdbusplus::message::object_path("/xyz/openbmc_project/network") /
1763             ifaceId,
1764         "xyz.openbmc_project.Network.EthernetInterface", "StaticNameServers",
1765         updatedStaticNameServers);
1766 }
1767 
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)1768 inline void handleIPv6StaticAddressesPatch(
1769     const std::string& ifaceId,
1770     std::vector<std::variant<nlohmann::json::object_t, std::nullptr_t>>& input,
1771     const std::vector<IPv6AddressData>& ipv6Data,
1772     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1773 {
1774     size_t entryIdx = 1;
1775     std::vector<IPv6AddressData>::const_iterator nicIpEntry =
1776         getNextStaticIpEntry(ipv6Data.cbegin(), ipv6Data.cend());
1777     for (std::variant<nlohmann::json::object_t, std::nullptr_t>& thisJson :
1778          input)
1779     {
1780         std::string pathString =
1781             "IPv6StaticAddresses/" + std::to_string(entryIdx);
1782         nlohmann::json::object_t* obj =
1783             std::get_if<nlohmann::json::object_t>(&thisJson);
1784         if (obj != nullptr && !obj->empty())
1785         {
1786             std::optional<std::string> address;
1787             std::optional<uint8_t> prefixLength;
1788             nlohmann::json::object_t thisJsonCopy = *obj;
1789             if (!json_util::readJsonObject(       //
1790                     thisJsonCopy, asyncResp->res, //
1791                     "Address", address,           //
1792                     "PrefixLength", prefixLength  //
1793                     ))
1794             {
1795                 messages::propertyValueFormatError(asyncResp->res, thisJsonCopy,
1796                                                    pathString);
1797                 return;
1798             }
1799 
1800             // Find the address and prefixLength values. Any values that are
1801             // not explicitly provided are assumed to be unmodified from the
1802             // current state of the interface. Merge existing state into the
1803             // current request.
1804             if (!address)
1805             {
1806                 if (nicIpEntry == ipv6Data.end())
1807                 {
1808                     messages::propertyMissing(asyncResp->res,
1809                                               pathString + "/Address");
1810                     return;
1811                 }
1812                 address = nicIpEntry->address;
1813             }
1814 
1815             if (!prefixLength)
1816             {
1817                 if (nicIpEntry == ipv6Data.end())
1818                 {
1819                     messages::propertyMissing(asyncResp->res,
1820                                               pathString + "/PrefixLength");
1821                     return;
1822                 }
1823                 prefixLength = nicIpEntry->prefixLength;
1824             }
1825 
1826             if (nicIpEntry != ipv6Data.end())
1827             {
1828                 deleteAndCreateIPAddress(IpVersion::IpV6, ifaceId,
1829                                          nicIpEntry->id, *prefixLength,
1830                                          *address, "", asyncResp);
1831                 nicIpEntry =
1832                     getNextStaticIpEntry(++nicIpEntry, ipv6Data.cend());
1833             }
1834             else
1835             {
1836                 createIPv6(ifaceId, *prefixLength, *address, asyncResp);
1837             }
1838             entryIdx++;
1839         }
1840         else
1841         {
1842             if (nicIpEntry == ipv6Data.end())
1843             {
1844                 // Requesting a DELETE/DO NOT MODIFY action for an item
1845                 // that isn't present on the eth(n) interface. Input JSON is
1846                 // in error, so bail out.
1847                 if (obj == nullptr)
1848                 {
1849                     messages::resourceCannotBeDeleted(asyncResp->res);
1850                     return;
1851                 }
1852                 messages::propertyValueFormatError(asyncResp->res, *obj,
1853                                                    pathString);
1854                 return;
1855             }
1856 
1857             if (obj == nullptr)
1858             {
1859                 deleteIPAddress(ifaceId, nicIpEntry->id, asyncResp);
1860             }
1861             if (nicIpEntry != ipv6Data.cend())
1862             {
1863                 nicIpEntry =
1864                     getNextStaticIpEntry(++nicIpEntry, ipv6Data.cend());
1865             }
1866             entryIdx++;
1867         }
1868     }
1869 }
1870 
extractParentInterfaceName(const std::string & ifaceId)1871 inline std::string extractParentInterfaceName(const std::string& ifaceId)
1872 {
1873     std::size_t pos = ifaceId.find('_');
1874     return ifaceId.substr(0, pos);
1875 }
1876 
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)1877 inline void parseInterfaceData(
1878     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1879     const std::string& ifaceId, const EthernetInterfaceData& ethData,
1880     const std::vector<IPv4AddressData>& ipv4Data,
1881     const std::vector<IPv6AddressData>& ipv6Data,
1882     const std::vector<StaticGatewayData>& ipv6GatewayData)
1883 {
1884     nlohmann::json& jsonResponse = asyncResp->res.jsonValue;
1885     jsonResponse["Id"] = ifaceId;
1886     jsonResponse["@odata.id"] =
1887         boost::urls::format("/redfish/v1/Managers/{}/EthernetInterfaces/{}",
1888                             BMCWEB_REDFISH_MANAGER_URI_NAME, ifaceId);
1889     jsonResponse["InterfaceEnabled"] = ethData.nicEnabled;
1890 
1891     if (ethData.nicEnabled)
1892     {
1893         jsonResponse["LinkStatus"] =
1894             ethData.linkUp ? ethernet_interface::LinkStatus::LinkUp
1895                            : ethernet_interface::LinkStatus::LinkDown;
1896         jsonResponse["Status"]["State"] = resource::State::Enabled;
1897     }
1898     else
1899     {
1900         jsonResponse["LinkStatus"] = ethernet_interface::LinkStatus::NoLink;
1901         jsonResponse["Status"]["State"] = resource::State::Disabled;
1902     }
1903 
1904     jsonResponse["SpeedMbps"] = ethData.speed;
1905     jsonResponse["MTUSize"] = ethData.mtuSize;
1906     if (ethData.macAddress)
1907     {
1908         jsonResponse["MACAddress"] = *ethData.macAddress;
1909     }
1910     jsonResponse["DHCPv4"]["DHCPEnabled"] =
1911         translateDhcpEnabledToBool(ethData.dhcpEnabled, true);
1912     jsonResponse["DHCPv4"]["UseNTPServers"] = ethData.ntpv4Enabled;
1913     jsonResponse["DHCPv4"]["UseDNSServers"] = ethData.dnsv4Enabled;
1914     jsonResponse["DHCPv4"]["UseDomainName"] = ethData.domainv4Enabled;
1915     jsonResponse["DHCPv6"]["OperatingMode"] =
1916         translateDhcpEnabledToBool(ethData.dhcpEnabled, false)
1917             ? "Enabled"
1918             : "Disabled";
1919     jsonResponse["DHCPv6"]["UseNTPServers"] = ethData.ntpv6Enabled;
1920     jsonResponse["DHCPv6"]["UseDNSServers"] = ethData.dnsv6Enabled;
1921     jsonResponse["DHCPv6"]["UseDomainName"] = ethData.domainv6Enabled;
1922     jsonResponse["StatelessAddressAutoConfig"]["IPv6AutoConfigEnabled"] =
1923         ethData.ipv6AcceptRa;
1924 
1925     if (!ethData.hostName.empty())
1926     {
1927         jsonResponse["HostName"] = ethData.hostName;
1928 
1929         // When domain name is empty then it means, that it is a network
1930         // without domain names, and the host name itself must be treated as
1931         // FQDN
1932         std::string fqdn = ethData.hostName;
1933         if (!ethData.domainnames.empty())
1934         {
1935             fqdn += "." + ethData.domainnames[0];
1936         }
1937         jsonResponse["FQDN"] = fqdn;
1938     }
1939 
1940     if (ethData.vlanId)
1941     {
1942         jsonResponse["EthernetInterfaceType"] =
1943             ethernet_interface::EthernetDeviceType::Virtual;
1944         jsonResponse["VLAN"]["VLANEnable"] = true;
1945         jsonResponse["VLAN"]["VLANId"] = *ethData.vlanId;
1946         jsonResponse["VLAN"]["Tagged"] = true;
1947 
1948         nlohmann::json::array_t relatedInterfaces;
1949         nlohmann::json& parentInterface = relatedInterfaces.emplace_back();
1950         parentInterface["@odata.id"] =
1951             boost::urls::format("/redfish/v1/Managers/{}/EthernetInterfaces",
1952                                 BMCWEB_REDFISH_MANAGER_URI_NAME,
1953                                 extractParentInterfaceName(ifaceId));
1954         jsonResponse["Links"]["RelatedInterfaces"] =
1955             std::move(relatedInterfaces);
1956     }
1957     else
1958     {
1959         jsonResponse["EthernetInterfaceType"] =
1960             ethernet_interface::EthernetDeviceType::Physical;
1961     }
1962 
1963     jsonResponse["NameServers"] = ethData.nameServers;
1964     jsonResponse["StaticNameServers"] = ethData.staticNameServers;
1965 
1966     nlohmann::json& ipv4Array = jsonResponse["IPv4Addresses"];
1967     nlohmann::json& ipv4StaticArray = jsonResponse["IPv4StaticAddresses"];
1968     ipv4Array = nlohmann::json::array();
1969     ipv4StaticArray = nlohmann::json::array();
1970     for (const auto& ipv4Config : ipv4Data)
1971     {
1972         std::string gatewayStr = ipv4Config.gateway;
1973         if (gatewayStr.empty())
1974         {
1975             gatewayStr = "0.0.0.0";
1976         }
1977         nlohmann::json::object_t ipv4;
1978         ipv4["AddressOrigin"] = ipv4Config.origin;
1979         ipv4["SubnetMask"] = ipv4Config.netmask;
1980         ipv4["Address"] = ipv4Config.address;
1981         ipv4["Gateway"] = gatewayStr;
1982 
1983         if (ipv4Config.origin == "Static")
1984         {
1985             ipv4StaticArray.push_back(ipv4);
1986         }
1987 
1988         ipv4Array.emplace_back(std::move(ipv4));
1989     }
1990 
1991     std::string ipv6GatewayStr = ethData.ipv6DefaultGateway;
1992     if (ipv6GatewayStr.empty())
1993     {
1994         ipv6GatewayStr = "0:0:0:0:0:0:0:0";
1995     }
1996 
1997     jsonResponse["IPv6DefaultGateway"] = ipv6GatewayStr;
1998 
1999     nlohmann::json::array_t ipv6StaticGatewayArray;
2000     for (const auto& ipv6GatewayConfig : ipv6GatewayData)
2001     {
2002         nlohmann::json::object_t ipv6Gateway;
2003         ipv6Gateway["Address"] = ipv6GatewayConfig.gateway;
2004         ipv6StaticGatewayArray.emplace_back(std::move(ipv6Gateway));
2005     }
2006     jsonResponse["IPv6StaticDefaultGateways"] =
2007         std::move(ipv6StaticGatewayArray);
2008 
2009     nlohmann::json& ipv6Array = jsonResponse["IPv6Addresses"];
2010     nlohmann::json& ipv6StaticArray = jsonResponse["IPv6StaticAddresses"];
2011     ipv6Array = nlohmann::json::array();
2012     ipv6StaticArray = nlohmann::json::array();
2013     nlohmann::json& ipv6AddrPolicyTable =
2014         jsonResponse["IPv6AddressPolicyTable"];
2015     ipv6AddrPolicyTable = nlohmann::json::array();
2016     for (const auto& ipv6Config : ipv6Data)
2017     {
2018         nlohmann::json::object_t ipv6;
2019         ipv6["Address"] = ipv6Config.address;
2020         ipv6["PrefixLength"] = ipv6Config.prefixLength;
2021         ipv6["AddressOrigin"] = ipv6Config.origin;
2022 
2023         ipv6Array.emplace_back(std::move(ipv6));
2024         if (ipv6Config.origin == "Static")
2025         {
2026             nlohmann::json::object_t ipv6Static;
2027             ipv6Static["Address"] = ipv6Config.address;
2028             ipv6Static["PrefixLength"] = ipv6Config.prefixLength;
2029             ipv6StaticArray.emplace_back(std::move(ipv6Static));
2030         }
2031     }
2032 }
2033 
afterDelete(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & ifaceId,const boost::system::error_code & ec,const sdbusplus::message_t & m)2034 inline void afterDelete(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2035                         const std::string& ifaceId,
2036                         const boost::system::error_code& ec,
2037                         const sdbusplus::message_t& m)
2038 {
2039     if (!ec)
2040     {
2041         return;
2042     }
2043     const sd_bus_error* dbusError = m.get_error();
2044     if (dbusError == nullptr)
2045     {
2046         messages::internalError(asyncResp->res);
2047         return;
2048     }
2049     BMCWEB_LOG_DEBUG("DBus error: {}", dbusError->name);
2050 
2051     if (std::string_view("org.freedesktop.DBus.Error.UnknownObject") ==
2052         dbusError->name)
2053     {
2054         messages::resourceNotFound(asyncResp->res, "EthernetInterface",
2055                                    ifaceId);
2056         return;
2057     }
2058     if (std::string_view("org.freedesktop.DBus.Error.UnknownMethod") ==
2059         dbusError->name)
2060     {
2061         messages::resourceCannotBeDeleted(asyncResp->res);
2062         return;
2063     }
2064     messages::internalError(asyncResp->res);
2065 }
2066 
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)2067 inline void afterVlanCreate(
2068     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2069     const std::string& parentInterfaceUri, const std::string& vlanInterface,
2070     const boost::system::error_code& ec, const sdbusplus::message_t& m
2071 
2072 )
2073 {
2074     if (ec)
2075     {
2076         const sd_bus_error* dbusError = m.get_error();
2077         if (dbusError == nullptr)
2078         {
2079             messages::internalError(asyncResp->res);
2080             return;
2081         }
2082         BMCWEB_LOG_DEBUG("DBus error: {}", dbusError->name);
2083 
2084         if (std::string_view(
2085                 "xyz.openbmc_project.Common.Error.ResourceNotFound") ==
2086             dbusError->name)
2087         {
2088             messages::propertyValueNotInList(
2089                 asyncResp->res, parentInterfaceUri,
2090                 "Links/RelatedInterfaces/0/@odata.id");
2091             return;
2092         }
2093         if (std::string_view(
2094                 "xyz.openbmc_project.Common.Error.InvalidArgument") ==
2095             dbusError->name)
2096         {
2097             messages::resourceAlreadyExists(asyncResp->res, "EthernetInterface",
2098                                             "Id", vlanInterface);
2099             return;
2100         }
2101         messages::internalError(asyncResp->res);
2102         return;
2103     }
2104 
2105     const boost::urls::url vlanInterfaceUri =
2106         boost::urls::format("/redfish/v1/Managers/{}/EthernetInterfaces/{}",
2107                             BMCWEB_REDFISH_MANAGER_URI_NAME, vlanInterface);
2108     asyncResp->res.addHeader("Location", vlanInterfaceUri.buffer());
2109 }
2110 
requestEthernetInterfacesRoutes(App & app)2111 inline void requestEthernetInterfacesRoutes(App& app)
2112 {
2113     BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/EthernetInterfaces/")
2114         .privileges(redfish::privileges::getEthernetInterfaceCollection)
2115         .methods(boost::beast::http::verb::get)(
2116             [&app](const crow::Request& req,
2117                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2118                    const std::string& managerId) {
2119                 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2120                 {
2121                     return;
2122                 }
2123 
2124                 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
2125                 {
2126                     messages::resourceNotFound(asyncResp->res, "Manager",
2127                                                managerId);
2128                     return;
2129                 }
2130 
2131                 asyncResp->res.jsonValue["@odata.type"] =
2132                     "#EthernetInterfaceCollection.EthernetInterfaceCollection";
2133                 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
2134                     "/redfish/v1/Managers/{}/EthernetInterfaces",
2135                     BMCWEB_REDFISH_MANAGER_URI_NAME);
2136                 asyncResp->res.jsonValue["Name"] =
2137                     "Ethernet Network Interface Collection";
2138                 asyncResp->res.jsonValue["Description"] =
2139                     "Collection of EthernetInterfaces for this Manager";
2140 
2141                 // Get eth interface list, and call the below callback for JSON
2142                 // preparation
2143                 getEthernetIfaceList(
2144                     [asyncResp](const bool& success,
2145                                 const std::vector<std::string>& ifaceList) {
2146                         if (!success)
2147                         {
2148                             messages::internalError(asyncResp->res);
2149                             return;
2150                         }
2151 
2152                         nlohmann::json& ifaceArray =
2153                             asyncResp->res.jsonValue["Members"];
2154                         ifaceArray = nlohmann::json::array();
2155                         for (const std::string& ifaceItem : ifaceList)
2156                         {
2157                             nlohmann::json::object_t iface;
2158                             iface["@odata.id"] = boost::urls::format(
2159                                 "/redfish/v1/Managers/{}/EthernetInterfaces/{}",
2160                                 BMCWEB_REDFISH_MANAGER_URI_NAME, ifaceItem);
2161                             ifaceArray.push_back(std::move(iface));
2162                         }
2163 
2164                         asyncResp->res.jsonValue["Members@odata.count"] =
2165                             ifaceArray.size();
2166                         asyncResp->res.jsonValue["@odata.id"] =
2167                             boost::urls::format(
2168                                 "/redfish/v1/Managers/{}/EthernetInterfaces",
2169                                 BMCWEB_REDFISH_MANAGER_URI_NAME);
2170                     });
2171             });
2172 
2173     BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/EthernetInterfaces/")
2174         .privileges(redfish::privileges::postEthernetInterfaceCollection)
2175         .methods(boost::beast::http::verb::post)(
2176             [&app](const crow::Request& req,
2177                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2178                    const std::string& managerId) {
2179                 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2180                 {
2181                     return;
2182                 }
2183 
2184                 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
2185                 {
2186                     messages::resourceNotFound(asyncResp->res, "Manager",
2187                                                managerId);
2188                     return;
2189                 }
2190 
2191                 bool vlanEnable = false;
2192                 uint32_t vlanId = 0;
2193                 std::vector<nlohmann::json::object_t> relatedInterfaces;
2194 
2195                 if (!json_util::readJsonPatch(                        //
2196                         req, asyncResp->res,                          //
2197                         "Links/RelatedInterfaces", relatedInterfaces, //
2198                         "VLAN/VLANEnable", vlanEnable,                //
2199                         "VLAN/VLANId", vlanId                         //
2200                         ))
2201                 {
2202                     return;
2203                 }
2204 
2205                 if (relatedInterfaces.size() != 1)
2206                 {
2207                     messages::arraySizeTooLong(asyncResp->res,
2208                                                "Links/RelatedInterfaces",
2209                                                relatedInterfaces.size());
2210                     return;
2211                 }
2212 
2213                 std::string parentInterfaceUri;
2214                 if (!json_util::readJsonObject(relatedInterfaces[0],
2215                                                asyncResp->res, "@odata.id",
2216                                                parentInterfaceUri))
2217                 {
2218                     messages::propertyMissing(
2219                         asyncResp->res, "Links/RelatedInterfaces/0/@odata.id");
2220                     return;
2221                 }
2222                 BMCWEB_LOG_INFO("Parent Interface URI: {}", parentInterfaceUri);
2223 
2224                 boost::system::result<boost::urls::url_view> parsedUri =
2225                     boost::urls::parse_relative_ref(parentInterfaceUri);
2226                 if (!parsedUri)
2227                 {
2228                     messages::propertyValueFormatError(
2229                         asyncResp->res, parentInterfaceUri,
2230                         "Links/RelatedInterfaces/0/@odata.id");
2231                     return;
2232                 }
2233 
2234                 std::string parentInterface;
2235                 if (!crow::utility::readUrlSegments(
2236                         *parsedUri, "redfish", "v1", "Managers", "bmc",
2237                         "EthernetInterfaces", std::ref(parentInterface)))
2238                 {
2239                     messages::propertyValueNotInList(
2240                         asyncResp->res, parentInterfaceUri,
2241                         "Links/RelatedInterfaces/0/@odata.id");
2242                     return;
2243                 }
2244 
2245                 if (!vlanEnable)
2246                 {
2247                     // In OpenBMC implementation, VLANEnable cannot be false on
2248                     // create
2249                     messages::propertyValueIncorrect(
2250                         asyncResp->res, "VLAN/VLANEnable", "false");
2251                     return;
2252                 }
2253 
2254                 std::string vlanInterface =
2255                     parentInterface + "_" + std::to_string(vlanId);
2256                 dbus::utility::async_method_call(
2257                     asyncResp,
2258                     [asyncResp, parentInterfaceUri,
2259                      vlanInterface](const boost::system::error_code& ec,
2260                                     const sdbusplus::message_t& m) {
2261                         afterVlanCreate(asyncResp, parentInterfaceUri,
2262                                         vlanInterface, ec, m);
2263                     },
2264                     "xyz.openbmc_project.Network",
2265                     "/xyz/openbmc_project/network",
2266                     "xyz.openbmc_project.Network.VLAN.Create", "VLAN",
2267                     parentInterface, vlanId);
2268             });
2269 
2270     BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/EthernetInterfaces/<str>/")
2271         .privileges(redfish::privileges::getEthernetInterface)
2272         .methods(boost::beast::http::verb::get)(
2273             [&app](const crow::Request& req,
2274                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2275                    const std::string& managerId, const std::string& ifaceId) {
2276                 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2277                 {
2278                     return;
2279                 }
2280 
2281                 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
2282                 {
2283                     messages::resourceNotFound(asyncResp->res, "Manager",
2284                                                managerId);
2285                     return;
2286                 }
2287 
2288                 getEthernetIfaceData(
2289                     ifaceId,
2290                     [asyncResp, ifaceId](
2291                         const bool& success,
2292                         const EthernetInterfaceData& ethData,
2293                         const std::vector<IPv4AddressData>& ipv4Data,
2294                         const std::vector<IPv6AddressData>& ipv6Data,
2295                         const std::vector<StaticGatewayData>& ipv6GatewayData) {
2296                         if (!success)
2297                         {
2298                             // TODO(Pawel)consider distinguish between non
2299                             // existing object, and other errors
2300                             messages::resourceNotFound(
2301                                 asyncResp->res, "EthernetInterface", ifaceId);
2302                             return;
2303                         }
2304 
2305                         asyncResp->res.jsonValue["@odata.type"] =
2306                             "#EthernetInterface.v1_9_0.EthernetInterface";
2307                         asyncResp->res.jsonValue["Name"] =
2308                             "Manager Ethernet Interface";
2309                         asyncResp->res.jsonValue["Description"] =
2310                             "Management Network Interface";
2311 
2312                         parseInterfaceData(asyncResp, ifaceId, ethData,
2313                                            ipv4Data, ipv6Data, ipv6GatewayData);
2314                     });
2315             });
2316 
2317     BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/EthernetInterfaces/<str>/")
2318         .privileges(redfish::privileges::patchEthernetInterface)
2319         .methods(boost::beast::http::verb::patch)(
2320             [&app](const crow::Request& req,
2321                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2322                    const std::string& managerId, const std::string& ifaceId) {
2323                 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2324                 {
2325                     return;
2326                 }
2327 
2328                 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
2329                 {
2330                     messages::resourceNotFound(asyncResp->res, "Manager",
2331                                                managerId);
2332                     return;
2333                 }
2334 
2335                 std::optional<std::string> hostname;
2336                 std::optional<std::string> fqdn;
2337                 std::optional<std::string> macAddress;
2338                 std::optional<std::string> ipv6DefaultGateway;
2339                 std::optional<std::vector<
2340                     std::variant<nlohmann::json::object_t, std::nullptr_t>>>
2341                     ipv4StaticAddresses;
2342                 std::optional<std::vector<
2343                     std::variant<nlohmann::json::object_t, std::nullptr_t>>>
2344                     ipv6StaticAddresses;
2345                 std::optional<std::vector<
2346                     std::variant<nlohmann::json::object_t, std::nullptr_t>>>
2347                     ipv6StaticDefaultGateways;
2348                 std::optional<std::vector<std::string>> staticNameServers;
2349                 std::optional<bool> ipv6AutoConfigEnabled;
2350                 std::optional<bool> interfaceEnabled;
2351                 std::optional<size_t> mtuSize;
2352                 DHCPParameters v4dhcpParms;
2353                 DHCPParameters v6dhcpParms;
2354 
2355                 if (!json_util::readJsonPatch(                             //
2356                         req, asyncResp->res,                               //
2357                         "DHCPv4/DHCPEnabled", v4dhcpParms.dhcpv4Enabled,   //
2358                         "DHCPv4/UseDNSServers", v4dhcpParms.useDnsServers, //
2359                         "DHCPv4/UseDomainName", v4dhcpParms.useDomainName, //
2360                         "DHCPv4/UseNTPServers", v4dhcpParms.useNtpServers, //
2361                         "DHCPv6/OperatingMode",
2362                         v6dhcpParms.dhcpv6OperatingMode,                   //
2363                         "DHCPv6/UseDNSServers", v6dhcpParms.useDnsServers, //
2364                         "DHCPv6/UseDomainName", v6dhcpParms.useDomainName, //
2365                         "DHCPv6/UseNTPServers", v6dhcpParms.useNtpServers, //
2366                         "FQDN", fqdn,                                      //
2367                         "HostName", hostname,                              //
2368                         "InterfaceEnabled", interfaceEnabled,              //
2369                         "IPv4StaticAddresses", ipv4StaticAddresses,        //
2370                         "IPv6DefaultGateway", ipv6DefaultGateway,          //
2371                         "IPv6StaticAddresses", ipv6StaticAddresses,        //
2372                         "IPv6StaticDefaultGateways",
2373                         ipv6StaticDefaultGateways,                         //
2374                         "InterfaceEnabled", interfaceEnabled,              //
2375                         "MACAddress", macAddress,                          //
2376                         "MTUSize", mtuSize,                                //
2377                         "StatelessAddressAutoConfig/IPv6AutoConfigEnabled",
2378                         ipv6AutoConfigEnabled,                             //
2379                         "StaticNameServers", staticNameServers             //
2380                         ))
2381                 {
2382                     return;
2383                 }
2384 
2385                 // Get single eth interface data, and call the below callback
2386                 // for JSON preparation
2387                 getEthernetIfaceData(
2388                     ifaceId,
2389                     [asyncResp, ifaceId, hostname = std::move(hostname),
2390                      fqdn = std::move(fqdn), macAddress = std::move(macAddress),
2391                      ipv4StaticAddresses = std::move(ipv4StaticAddresses),
2392                      ipv6DefaultGateway = std::move(ipv6DefaultGateway),
2393                      ipv6StaticAddresses = std::move(ipv6StaticAddresses),
2394                      ipv6StaticDefaultGateway =
2395                          std::move(ipv6StaticDefaultGateways),
2396                      staticNameServers = std::move(staticNameServers), mtuSize,
2397                      ipv6AutoConfigEnabled,
2398                      v4dhcpParms = std::move(v4dhcpParms),
2399                      v6dhcpParms = std::move(v6dhcpParms), interfaceEnabled](
2400                         const bool success,
2401                         const EthernetInterfaceData& ethData,
2402                         const std::vector<IPv4AddressData>& ipv4Data,
2403                         const std::vector<IPv6AddressData>& ipv6Data,
2404                         const std::vector<StaticGatewayData>&
2405                             ipv6GatewayData) mutable {
2406                         if (!success)
2407                         {
2408                             // ... otherwise return error
2409                             // TODO(Pawel)consider distinguish between non
2410                             // existing object, and other errors
2411                             messages::resourceNotFound(
2412                                 asyncResp->res, "EthernetInterface", ifaceId);
2413                             return;
2414                         }
2415 
2416                         handleDHCPPatch(ifaceId, ethData, v4dhcpParms,
2417                                         v6dhcpParms, asyncResp);
2418 
2419                         if (hostname)
2420                         {
2421                             handleHostnamePatch(*hostname, asyncResp);
2422                         }
2423 
2424                         if (ipv6AutoConfigEnabled)
2425                         {
2426                             handleSLAACAutoConfigPatch(
2427                                 ifaceId, *ipv6AutoConfigEnabled, asyncResp);
2428                         }
2429 
2430                         if (fqdn)
2431                         {
2432                             handleFqdnPatch(ifaceId, *fqdn, asyncResp);
2433                         }
2434 
2435                         if (macAddress)
2436                         {
2437                             handleMACAddressPatch(ifaceId, *macAddress,
2438                                                   asyncResp);
2439                         }
2440 
2441                         if (ipv4StaticAddresses)
2442                         {
2443                             handleIPv4StaticPatch(ifaceId, *ipv4StaticAddresses,
2444                                                   ethData, ipv4Data, asyncResp);
2445                         }
2446 
2447                         if (staticNameServers)
2448                         {
2449                             handleStaticNameServersPatch(
2450                                 ifaceId, *staticNameServers, asyncResp);
2451                         }
2452 
2453                         if (ipv6DefaultGateway)
2454                         {
2455                             messages::propertyNotWritable(asyncResp->res,
2456                                                           "IPv6DefaultGateway");
2457                         }
2458 
2459                         if (ipv6StaticAddresses)
2460                         {
2461                             handleIPv6StaticAddressesPatch(ifaceId,
2462                                                            *ipv6StaticAddresses,
2463                                                            ipv6Data, asyncResp);
2464                         }
2465 
2466                         if (ipv6StaticDefaultGateway)
2467                         {
2468                             handleIPv6DefaultGateway(
2469                                 ifaceId, *ipv6StaticDefaultGateway,
2470                                 ipv6GatewayData, asyncResp);
2471                         }
2472 
2473                         if (interfaceEnabled)
2474                         {
2475                             setDbusProperty(
2476                                 asyncResp, "InterfaceEnabled",
2477                                 "xyz.openbmc_project.Network",
2478                                 sdbusplus::message::object_path(
2479                                     "/xyz/openbmc_project/network") /
2480                                     ifaceId,
2481                                 "xyz.openbmc_project.Network.EthernetInterface",
2482                                 "NICEnabled", *interfaceEnabled);
2483                         }
2484 
2485                         if (mtuSize)
2486                         {
2487                             handleMTUSizePatch(ifaceId, *mtuSize, asyncResp);
2488                         }
2489                     });
2490             });
2491 
2492     BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/EthernetInterfaces/<str>/")
2493         .privileges(redfish::privileges::deleteEthernetInterface)
2494         .methods(boost::beast::http::verb::delete_)(
2495             [&app](const crow::Request& req,
2496                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2497                    const std::string& managerId, const std::string& ifaceId) {
2498                 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2499                 {
2500                     return;
2501                 }
2502 
2503                 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
2504                 {
2505                     messages::resourceNotFound(asyncResp->res, "Manager",
2506                                                managerId);
2507                     return;
2508                 }
2509 
2510                 dbus::utility::async_method_call(
2511                     asyncResp,
2512                     [asyncResp, ifaceId](const boost::system::error_code& ec,
2513                                          const sdbusplus::message_t& m) {
2514                         afterDelete(asyncResp, ifaceId, ec, m);
2515                     },
2516                     "xyz.openbmc_project.Network",
2517                     std::string("/xyz/openbmc_project/network/") + ifaceId,
2518                     "xyz.openbmc_project.Object.Delete", "Delete");
2519             });
2520 }
2521 
2522 } // namespace redfish
2523