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