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