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