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