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