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