1 #include "transporthandler.hpp"
2 
3 #include "app/channel.hpp"
4 #include "user_channel/cipher_mgmt.hpp"
5 
6 #include <arpa/inet.h>
7 #include <netinet/ether.h>
8 
9 #include <array>
10 #include <bitset>
11 #include <cinttypes>
12 #include <cstdint>
13 #include <cstring>
14 #include <fstream>
15 #include <functional>
16 #include <ipmid/api.hpp>
17 #include <ipmid/message.hpp>
18 #include <ipmid/message/types.hpp>
19 #include <ipmid/types.hpp>
20 #include <ipmid/utils.hpp>
21 #include <optional>
22 #include <phosphor-logging/elog-errors.hpp>
23 #include <phosphor-logging/elog.hpp>
24 #include <phosphor-logging/log.hpp>
25 #include <sdbusplus/bus.hpp>
26 #include <sdbusplus/exception.hpp>
27 #include <string>
28 #include <string_view>
29 #include <type_traits>
30 #include <unordered_map>
31 #include <unordered_set>
32 #include <user_channel/channel_layer.hpp>
33 #include <utility>
34 #include <vector>
35 #include <xyz/openbmc_project/Common/error.hpp>
36 #include <xyz/openbmc_project/Network/EthernetInterface/server.hpp>
37 #include <xyz/openbmc_project/Network/IP/server.hpp>
38 #include <xyz/openbmc_project/Network/Neighbor/server.hpp>
39 
40 using phosphor::logging::commit;
41 using phosphor::logging::elog;
42 using phosphor::logging::entry;
43 using phosphor::logging::level;
44 using phosphor::logging::log;
45 using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
46 using sdbusplus::xyz::openbmc_project::Network::server::EthernetInterface;
47 using sdbusplus::xyz::openbmc_project::Network::server::IP;
48 using sdbusplus::xyz::openbmc_project::Network::server::Neighbor;
49 
50 namespace cipher
51 {
52 
53 std::vector<uint8_t> getCipherList()
54 {
55     std::vector<uint8_t> cipherList;
56 
57     std::ifstream jsonFile(cipher::configFile);
58     if (!jsonFile.is_open())
59     {
60         log<level::ERR>("Channel Cipher suites file not found");
61         elog<InternalFailure>();
62     }
63 
64     auto data = Json::parse(jsonFile, nullptr, false);
65     if (data.is_discarded())
66     {
67         log<level::ERR>("Parsing channel cipher suites JSON failed");
68         elog<InternalFailure>();
69     }
70 
71     // Byte 1 is reserved
72     cipherList.push_back(0x00);
73 
74     for (const auto& record : data)
75     {
76         cipherList.push_back(record.value(cipher, 0));
77     }
78 
79     return cipherList;
80 }
81 } // namespace cipher
82 
83 namespace ipmi
84 {
85 namespace transport
86 {
87 
88 // D-Bus Network Daemon definitions
89 constexpr auto PATH_ROOT = "/xyz/openbmc_project/network";
90 constexpr auto PATH_SYSTEMCONFIG = "/xyz/openbmc_project/network/config";
91 
92 constexpr auto INTF_SYSTEMCONFIG =
93     "xyz.openbmc_project.Network.SystemConfiguration";
94 constexpr auto INTF_ETHERNET = "xyz.openbmc_project.Network.EthernetInterface";
95 constexpr auto INTF_IP = "xyz.openbmc_project.Network.IP";
96 constexpr auto INTF_IP_CREATE = "xyz.openbmc_project.Network.IP.Create";
97 constexpr auto INTF_MAC = "xyz.openbmc_project.Network.MACAddress";
98 constexpr auto INTF_NEIGHBOR = "xyz.openbmc_project.Network.Neighbor";
99 constexpr auto INTF_NEIGHBOR_CREATE_STATIC =
100     "xyz.openbmc_project.Network.Neighbor.CreateStatic";
101 constexpr auto INTF_VLAN = "xyz.openbmc_project.Network.VLAN";
102 constexpr auto INTF_VLAN_CREATE = "xyz.openbmc_project.Network.VLAN.Create";
103 
104 /** @brief Generic paramters for different address families */
105 template <int family>
106 struct AddrFamily
107 {
108 };
109 
110 /** @brief Parameter specialization for IPv4 */
111 template <>
112 struct AddrFamily<AF_INET>
113 {
114     using addr = in_addr;
115     static constexpr auto protocol = IP::Protocol::IPv4;
116     static constexpr size_t maxStrLen = INET6_ADDRSTRLEN;
117     static constexpr uint8_t defaultPrefix = 32;
118     static constexpr char propertyGateway[] = "DefaultGateway";
119 };
120 
121 /** @brief Parameter specialization for IPv6 */
122 template <>
123 struct AddrFamily<AF_INET6>
124 {
125     using addr = in6_addr;
126     static constexpr auto protocol = IP::Protocol::IPv6;
127     static constexpr size_t maxStrLen = INET6_ADDRSTRLEN;
128     static constexpr uint8_t defaultPrefix = 128;
129     static constexpr char propertyGateway[] = "DefaultGateway6";
130 };
131 
132 /** @brief Valid address origins for IPv4 */
133 const std::unordered_set<IP::AddressOrigin> originsV4 = {
134     IP::AddressOrigin::Static,
135     IP::AddressOrigin::DHCP,
136 };
137 
138 /** @brief Valid address origins for IPv6 */
139 const std::unordered_set<IP::AddressOrigin> originsV6Static = {
140     IP::AddressOrigin::Static};
141 const std::unordered_set<IP::AddressOrigin> originsV6Dynamic = {
142     IP::AddressOrigin::DHCP,
143     IP::AddressOrigin::SLAAC,
144 };
145 
146 /** @brief Interface IP Address configuration parameters */
147 template <int family>
148 struct IfAddr
149 {
150     std::string path;
151     typename AddrFamily<family>::addr address;
152     IP::AddressOrigin origin;
153     uint8_t prefix;
154 };
155 
156 /** @brief Interface Neighbor configuration parameters */
157 template <int family>
158 struct IfNeigh
159 {
160     std::string path;
161     typename AddrFamily<family>::addr ip;
162     ether_addr mac;
163 };
164 
165 static constexpr uint8_t oemCmdStart = 192;
166 static constexpr uint8_t oemCmdEnd = 255;
167 
168 /** @brief A trivial helper used to determine if two PODs are equal
169  *
170  *  @params[in] a - The first object to compare
171  *  @params[in] b - The second object to compare
172  *  @return True if the objects are the same bytewise
173  */
174 template <typename T>
175 bool equal(const T& a, const T& b)
176 {
177     static_assert(std::is_trivially_copyable_v<T>);
178     return std::memcmp(&a, &b, sizeof(T)) == 0;
179 }
180 
181 /** @brief Copies bytes from an array into a trivially copyable container
182  *
183  *  @params[out] t     - The container receiving the data
184  *  @params[in]  bytes - The data to copy
185  */
186 template <size_t N, typename T>
187 void copyInto(T& t, const std::array<uint8_t, N>& bytes)
188 {
189     static_assert(std::is_trivially_copyable_v<T>);
190     static_assert(N == sizeof(T));
191     std::memcpy(&t, bytes.data(), bytes.size());
192 }
193 
194 /** @brief Gets a generic view of the bytes in the input container
195  *
196  *  @params[in] t - The data to reference
197  *  @return A string_view referencing the bytes in the container
198  */
199 template <typename T>
200 std::string_view dataRef(const T& t)
201 {
202     static_assert(std::is_trivially_copyable_v<T>);
203     return {reinterpret_cast<const char*>(&t), sizeof(T)};
204 }
205 
206 /** @brief The dbus parameters for the interface corresponding to a channel
207  *         This helps reduce the number of mapper lookups we need for each
208  *         query and simplifies finding the VLAN interface if needed.
209  */
210 struct ChannelParams
211 {
212     /** @brief The channel ID */
213     int id;
214     /** @brief channel name for the interface */
215     std::string ifname;
216     /** @brief Name of the service on the bus */
217     std::string service;
218     /** @brief Lower level adapter path that is guaranteed to not be a VLAN */
219     std::string ifPath;
220     /** @brief Logical adapter path used for address assignment */
221     std::string logicalPath;
222 };
223 
224 /** @brief Determines the ethernet interface name corresponding to a channel
225  *         Tries to map a VLAN object first so that the address information
226  *         is accurate. Otherwise it gets the standard ethernet interface.
227  *
228  *  @param[in] bus     - The bus object used for lookups
229  *  @param[in] channel - The channel id corresponding to an ethernet interface
230  *  @return Ethernet interface service and object path if it exists
231  */
232 std::optional<ChannelParams> maybeGetChannelParams(sdbusplus::bus::bus& bus,
233                                                    uint8_t channel)
234 {
235     auto ifname = getChannelName(channel);
236     if (ifname.empty())
237     {
238         return std::nullopt;
239     }
240 
241     // Enumerate all VLAN + ETHERNET interfaces
242     auto req = bus.new_method_call(MAPPER_BUS_NAME, MAPPER_OBJ, MAPPER_INTF,
243                                    "GetSubTree");
244     req.append(PATH_ROOT, 0,
245                std::vector<std::string>{INTF_VLAN, INTF_ETHERNET});
246     auto reply = bus.call(req);
247     ObjectTree objs;
248     reply.read(objs);
249 
250     ChannelParams params;
251     for (const auto& [path, impls] : objs)
252     {
253         if (path.find(ifname) == path.npos)
254         {
255             continue;
256         }
257         for (const auto& [service, intfs] : impls)
258         {
259             bool vlan = false;
260             bool ethernet = false;
261             for (const auto& intf : intfs)
262             {
263                 if (intf == INTF_VLAN)
264                 {
265                     vlan = true;
266                 }
267                 else if (intf == INTF_ETHERNET)
268                 {
269                     ethernet = true;
270                 }
271             }
272             if (params.service.empty() && (vlan || ethernet))
273             {
274                 params.service = service;
275             }
276             if (params.ifPath.empty() && !vlan && ethernet)
277             {
278                 params.ifPath = path;
279             }
280             if (params.logicalPath.empty() && vlan)
281             {
282                 params.logicalPath = path;
283             }
284         }
285     }
286 
287     // We must have a path for the underlying interface
288     if (params.ifPath.empty())
289     {
290         return std::nullopt;
291     }
292     // We don't have a VLAN so the logical path is the same
293     if (params.logicalPath.empty())
294     {
295         params.logicalPath = params.ifPath;
296     }
297 
298     params.id = channel;
299     params.ifname = std::move(ifname);
300     return std::move(params);
301 }
302 
303 /** @brief A trivial helper around maybeGetChannelParams() that throws an
304  *         exception when it is unable to acquire parameters for the channel.
305  *
306  *  @param[in] bus     - The bus object used for lookups
307  *  @param[in] channel - The channel id corresponding to an ethernet interface
308  *  @return Ethernet interface service and object path
309  */
310 ChannelParams getChannelParams(sdbusplus::bus::bus& bus, uint8_t channel)
311 {
312     auto params = maybeGetChannelParams(bus, channel);
313     if (!params)
314     {
315         log<level::ERR>("Failed to get channel params",
316                         entry("CHANNEL=%" PRIu8, channel));
317         elog<InternalFailure>();
318     }
319     return std::move(*params);
320 }
321 
322 /** @brief Wraps the phosphor logging method to insert some additional metadata
323  *
324  *  @param[in] params - The parameters for the channel
325  *  ...
326  */
327 template <auto level, typename... Args>
328 auto logWithChannel(const ChannelParams& params, Args&&... args)
329 {
330     return log<level>(std::forward<Args>(args)...,
331                       entry("CHANNEL=%d", params.id),
332                       entry("IFNAME=%s", params.ifname.c_str()));
333 }
334 template <auto level, typename... Args>
335 auto logWithChannel(const std::optional<ChannelParams>& params, Args&&... args)
336 {
337     if (params)
338     {
339         return logWithChannel<level>(*params, std::forward<Args>(args)...);
340     }
341     return log<level>(std::forward<Args>(args)...);
342 }
343 
344 /** @brief Trivializes using parameter getter functions by providing a bus
345  *         and channel parameters automatically.
346  *
347  *  @param[in] channel - The channel id corresponding to an ethernet interface
348  *  ...
349  */
350 template <auto func, typename... Args>
351 auto channelCall(uint8_t channel, Args&&... args)
352 {
353     sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection());
354     auto params = getChannelParams(bus, channel);
355     return std::invoke(func, bus, params, std::forward<Args>(args)...);
356 }
357 
358 /** @brief Determines if the ethernet interface is using DHCP
359  *
360  *  @param[in] bus    - The bus object used for lookups
361  *  @param[in] params - The parameters for the channel
362  *  @return DHCPConf enumeration
363  */
364 EthernetInterface::DHCPConf getDHCPProperty(sdbusplus::bus::bus& bus,
365                                             const ChannelParams& params)
366 {
367     std::string dhcpstr = std::get<std::string>(getDbusProperty(
368         bus, params.service, params.logicalPath, INTF_ETHERNET, "DHCPEnabled"));
369     return EthernetInterface::convertDHCPConfFromString(dhcpstr);
370 }
371 
372 /** @brief Sets the DHCP v4 state on the given interface
373  *
374  *  @param[in] bus           - The bus object used for lookups
375  *  @param[in] params        - The parameters for the channel
376  *  @param[in] requestedDhcp - DHCP state to assign
377  *                             (EthernetInterface::DHCPConf::none,
378  *                              EthernetInterface::DHCPConf::v4,
379  *                              EthernetInterface::DHCPConf::v6,
380  *                              EthernetInterface::DHCPConf::both)
381  */
382 void setDHCPv4Property(sdbusplus::bus::bus& bus, const ChannelParams& params,
383                        const EthernetInterface::DHCPConf requestedDhcp)
384 {
385     EthernetInterface::DHCPConf currentDhcp = getDHCPProperty(bus, params);
386     EthernetInterface::DHCPConf nextDhcp = EthernetInterface::DHCPConf::none;
387 
388     if ((currentDhcp == EthernetInterface::DHCPConf::v6) &&
389         (requestedDhcp == EthernetInterface::DHCPConf::v4))
390     {
391         nextDhcp = EthernetInterface::DHCPConf::both;
392     }
393     else if ((currentDhcp == EthernetInterface::DHCPConf::none) &&
394              (requestedDhcp == EthernetInterface::DHCPConf::v4))
395 
396     {
397         nextDhcp = requestedDhcp;
398     }
399     else if (requestedDhcp == EthernetInterface::DHCPConf::none)
400     {
401         if (currentDhcp == EthernetInterface::DHCPConf::both)
402         {
403             nextDhcp = EthernetInterface::DHCPConf::v6;
404         }
405         else if (currentDhcp == EthernetInterface::DHCPConf::v4)
406         {
407             nextDhcp = EthernetInterface::DHCPConf::none;
408         }
409     }
410     else
411     {
412         nextDhcp = currentDhcp;
413     }
414     std::string newDhcp =
415         sdbusplus::xyz::openbmc_project::Network::server::convertForMessage(
416             nextDhcp);
417     setDbusProperty(bus, params.service, params.logicalPath, INTF_ETHERNET,
418                     "DHCPEnabled", newDhcp);
419 }
420 
421 /** @brief Sets the DHCP v6 state on the given interface
422  *
423  *  @param[in] bus           - The bus object used for lookups
424  *  @param[in] params        - The parameters for the channel
425  *  @param[in] requestedDhcp - DHCP state to assign (none, v6, both)
426  *  @param[in] defaultMode   - True: Use algorithmic assignment
427  *                             False: requestedDhcp assigned unconditionally
428  */
429 void setDHCPv6Property(sdbusplus::bus::bus& bus, const ChannelParams& params,
430                        const EthernetInterface::DHCPConf requestedDhcp,
431                        const bool defaultMode = true)
432 {
433     EthernetInterface::DHCPConf currentDhcp = getDHCPProperty(bus, params);
434     EthernetInterface::DHCPConf nextDhcp = EthernetInterface::DHCPConf::none;
435 
436     if (defaultMode)
437     {
438         if ((currentDhcp == EthernetInterface::DHCPConf::v4) &&
439             (requestedDhcp == EthernetInterface::DHCPConf::v6))
440         {
441             nextDhcp = EthernetInterface::DHCPConf::both;
442         }
443         else if ((currentDhcp == EthernetInterface::DHCPConf::none) &&
444                  (requestedDhcp == EthernetInterface::DHCPConf::v6))
445 
446         {
447             nextDhcp = requestedDhcp;
448         }
449         else if (requestedDhcp == EthernetInterface::DHCPConf::none)
450         {
451             if (currentDhcp == EthernetInterface::DHCPConf::both)
452             {
453                 nextDhcp = EthernetInterface::DHCPConf::v4;
454             }
455             else if (currentDhcp == EthernetInterface::DHCPConf::v6)
456             {
457                 nextDhcp = EthernetInterface::DHCPConf::none;
458             }
459         }
460         else
461         {
462             nextDhcp = currentDhcp;
463         }
464     }
465     else
466     {
467         // allow the v6 call to set any value
468         nextDhcp = requestedDhcp;
469     }
470 
471     std::string newDhcp =
472         sdbusplus::xyz::openbmc_project::Network::server::convertForMessage(
473             nextDhcp);
474     setDbusProperty(bus, params.service, params.logicalPath, INTF_ETHERNET,
475                     "DHCPEnabled", newDhcp);
476 }
477 
478 /** @brief Converts a human readable MAC string into MAC bytes
479  *
480  *  @param[in] mac - The MAC string
481  *  @return MAC in bytes
482  */
483 ether_addr stringToMAC(const char* mac)
484 {
485     const ether_addr* ret = ether_aton(mac);
486     if (ret == nullptr)
487     {
488         log<level::ERR>("Invalid MAC Address", entry("MAC=%s", mac));
489         elog<InternalFailure>();
490     }
491     return *ret;
492 }
493 
494 /** @brief Determines the MAC of the ethernet interface
495  *
496  *  @param[in] bus    - The bus object used for lookups
497  *  @param[in] params - The parameters for the channel
498  *  @return The configured mac address
499  */
500 ether_addr getMACProperty(sdbusplus::bus::bus& bus, const ChannelParams& params)
501 {
502     auto macStr = std::get<std::string>(getDbusProperty(
503         bus, params.service, params.ifPath, INTF_MAC, "MACAddress"));
504     return stringToMAC(macStr.c_str());
505 }
506 
507 /** @brief Sets the system value for MAC address on the given interface
508  *
509  *  @param[in] bus    - The bus object used for lookups
510  *  @param[in] params - The parameters for the channel
511  *  @param[in] mac    - MAC address to apply
512  */
513 void setMACProperty(sdbusplus::bus::bus& bus, const ChannelParams& params,
514                     const ether_addr& mac)
515 {
516     std::string macStr = ether_ntoa(&mac);
517     setDbusProperty(bus, params.service, params.ifPath, INTF_MAC, "MACAddress",
518                     macStr);
519 }
520 
521 /** @brief Turns an IP address string into the network byte order form
522  *         NOTE: This version strictly validates family matches
523  *
524  *  @param[in] address - The string form of the address
525  *  @return A network byte order address or none if conversion failed
526  */
527 template <int family>
528 std::optional<typename AddrFamily<family>::addr>
529     maybeStringToAddr(const char* address)
530 {
531     typename AddrFamily<family>::addr ret;
532     if (inet_pton(family, address, &ret) == 1)
533     {
534         return ret;
535     }
536     return std::nullopt;
537 }
538 
539 /** @brief Turns an IP address string into the network byte order form
540  *         NOTE: This version strictly validates family matches
541  *
542  *  @param[in] address - The string form of the address
543  *  @return A network byte order address
544  */
545 template <int family>
546 typename AddrFamily<family>::addr stringToAddr(const char* address)
547 {
548     auto ret = maybeStringToAddr<family>(address);
549     if (!ret)
550     {
551         log<level::ERR>("Failed to convert IP Address",
552                         entry("FAMILY=%d", family),
553                         entry("ADDRESS=%s", address));
554         elog<InternalFailure>();
555     }
556     return *ret;
557 }
558 
559 /** @brief Turns an IP address in network byte order into a string
560  *
561  *  @param[in] address - The string form of the address
562  *  @return A network byte order address
563  */
564 template <int family>
565 std::string addrToString(const typename AddrFamily<family>::addr& address)
566 {
567     std::string ret(AddrFamily<family>::maxStrLen, '\0');
568     inet_ntop(family, &address, ret.data(), ret.size());
569     ret.resize(strlen(ret.c_str()));
570     return ret;
571 }
572 
573 /** @brief Retrieves the current gateway for the address family on the system
574  *         NOTE: The gateway is currently system wide and not per channel
575  *
576  *  @param[in] bus    - The bus object used for lookups
577  *  @param[in] params - The parameters for the channel
578  *  @return An address representing the gateway address if it exists
579  */
580 template <int family>
581 std::optional<typename AddrFamily<family>::addr>
582     getGatewayProperty(sdbusplus::bus::bus& bus, const ChannelParams& params)
583 {
584     auto gatewayStr = std::get<std::string>(getDbusProperty(
585         bus, params.service, PATH_SYSTEMCONFIG, INTF_SYSTEMCONFIG,
586         AddrFamily<family>::propertyGateway));
587     if (gatewayStr.empty())
588     {
589         return std::nullopt;
590     }
591     return stringToAddr<family>(gatewayStr.c_str());
592 }
593 
594 /** @brief A lazy lookup mechanism for iterating over object properties stored
595  *         in DBus. This will only perform the object lookup when needed, and
596  *         retains a cache of previous lookups to speed up future iterations.
597  */
598 class ObjectLookupCache
599 {
600   public:
601     using PropertiesCache = std::unordered_map<std::string, PropertyMap>;
602 
603     /** @brief Creates a new ObjectLookupCache for the interface on the bus
604      *         NOTE: The inputs to this object must outlive the object since
605      *         they are only referenced by it.
606      *
607      *  @param[in] bus    - The bus object used for lookups
608      *  @param[in] params - The parameters for the channel
609      *  @param[in] intf   - The interface we are looking up
610      */
611     ObjectLookupCache(sdbusplus::bus::bus& bus, const ChannelParams& params,
612                       const char* intf) :
613         bus(bus),
614         params(params), intf(intf),
615         objs(getAllDbusObjects(bus, params.logicalPath, intf, ""))
616     {
617     }
618 
619     class iterator : public ObjectTree::const_iterator
620     {
621       public:
622         using value_type = PropertiesCache::value_type;
623 
624         iterator(ObjectTree::const_iterator it, ObjectLookupCache& container) :
625             ObjectTree::const_iterator(it), container(container),
626             ret(container.cache.end())
627         {
628         }
629         value_type& operator*()
630         {
631             ret = container.get(ObjectTree::const_iterator::operator*().first);
632             return *ret;
633         }
634         value_type* operator->()
635         {
636             return &operator*();
637         }
638 
639       private:
640         ObjectLookupCache& container;
641         PropertiesCache::iterator ret;
642     };
643 
644     iterator begin() noexcept
645     {
646         return iterator(objs.begin(), *this);
647     }
648 
649     iterator end() noexcept
650     {
651         return iterator(objs.end(), *this);
652     }
653 
654   private:
655     sdbusplus::bus::bus& bus;
656     const ChannelParams& params;
657     const char* const intf;
658     const ObjectTree objs;
659     PropertiesCache cache;
660 
661     /** @brief Gets a cached copy of the object properties if possible
662      *         Otherwise performs a query on DBus to look them up
663      *
664      *  @param[in] path - The object path to lookup
665      *  @return An iterator for the specified object path + properties
666      */
667     PropertiesCache::iterator get(const std::string& path)
668     {
669         auto it = cache.find(path);
670         if (it != cache.end())
671         {
672             return it;
673         }
674         auto properties = getAllDbusProperties(bus, params.service, path, intf);
675         return cache.insert({path, std::move(properties)}).first;
676     }
677 };
678 
679 /** @brief Searches the ip object lookup cache for an address matching
680  *         the input parameters. NOTE: The index lacks stability across address
681  *         changes since the network daemon has no notion of stable indicies.
682  *
683  *  @param[in] bus     - The bus object used for lookups
684  *  @param[in] params  - The parameters for the channel
685  *  @param[in] idx     - The index of the desired address on the interface
686  *  @param[in] origins - The allowed origins for the address objects
687  *  @param[in] ips     - The object lookup cache holding all of the address info
688  *  @return The address and prefix if it was found
689  */
690 template <int family>
691 std::optional<IfAddr<family>>
692     findIfAddr(sdbusplus::bus::bus& bus, const ChannelParams& params,
693                uint8_t idx,
694                const std::unordered_set<IP::AddressOrigin>& origins,
695                ObjectLookupCache& ips)
696 {
697     for (const auto& [path, properties] : ips)
698     {
699         const auto& addrStr = std::get<std::string>(properties.at("Address"));
700         auto addr = maybeStringToAddr<family>(addrStr.c_str());
701         if (!addr)
702         {
703             continue;
704         }
705 
706         IP::AddressOrigin origin = IP::convertAddressOriginFromString(
707             std::get<std::string>(properties.at("Origin")));
708         if (origins.find(origin) == origins.end())
709         {
710             continue;
711         }
712 
713         if (idx > 0)
714         {
715             idx--;
716             continue;
717         }
718 
719         IfAddr<family> ifaddr;
720         ifaddr.path = path;
721         ifaddr.address = *addr;
722         ifaddr.prefix = std::get<uint8_t>(properties.at("PrefixLength"));
723         ifaddr.origin = origin;
724         return std::move(ifaddr);
725     }
726 
727     return std::nullopt;
728 }
729 
730 /** @brief Trivial helper around findIfAddr that simplifies calls
731  *         for one off lookups. Don't use this if you intend to do multiple
732  *         lookups at a time.
733  *
734  *  @param[in] bus     - The bus object used for lookups
735  *  @param[in] params  - The parameters for the channel
736  *  @param[in] idx     - The index of the desired address on the interface
737  *  @param[in] origins - The allowed origins for the address objects
738  *  @return The address and prefix if it was found
739  */
740 template <int family>
741 auto getIfAddr(sdbusplus::bus::bus& bus, const ChannelParams& params,
742                uint8_t idx,
743                const std::unordered_set<IP::AddressOrigin>& origins)
744 {
745     ObjectLookupCache ips(bus, params, INTF_IP);
746     return findIfAddr<family>(bus, params, idx, origins, ips);
747 }
748 
749 /** @brief Deletes the dbus object. Ignores empty objects or objects that are
750  *         missing from the bus.
751  *
752  *  @param[in] bus     - The bus object used for lookups
753  *  @param[in] service - The name of the service
754  *  @param[in] path    - The path of the object to delete
755  */
756 void deleteObjectIfExists(sdbusplus::bus::bus& bus, const std::string& service,
757                           const std::string& path)
758 {
759     if (path.empty())
760     {
761         return;
762     }
763     try
764     {
765         auto req = bus.new_method_call(service.c_str(), path.c_str(),
766                                        ipmi::DELETE_INTERFACE, "Delete");
767         bus.call_noreply(req);
768     }
769     catch (const sdbusplus::exception::SdBusError& e)
770     {
771         if (strcmp(e.name(),
772                    "xyz.openbmc_project.Common.Error.InternalFailure") != 0 &&
773             strcmp(e.name(), "org.freedesktop.DBus.Error.UnknownObject") != 0)
774         {
775             // We want to rethrow real errors
776             throw;
777         }
778     }
779 }
780 
781 /** @brief Sets the address info configured for the interface
782  *         If a previous address path exists then it will be removed
783  *         before the new address is added.
784  *
785  *  @param[in] bus     - The bus object used for lookups
786  *  @param[in] params  - The parameters for the channel
787  *  @param[in] address - The address of the new IP
788  *  @param[in] prefix  - The prefix of the new IP
789  */
790 template <int family>
791 void createIfAddr(sdbusplus::bus::bus& bus, const ChannelParams& params,
792                   const typename AddrFamily<family>::addr& address,
793                   uint8_t prefix)
794 {
795     auto newreq =
796         bus.new_method_call(params.service.c_str(), params.logicalPath.c_str(),
797                             INTF_IP_CREATE, "IP");
798     std::string protocol =
799         sdbusplus::xyz::openbmc_project::Network::server::convertForMessage(
800             AddrFamily<family>::protocol);
801     newreq.append(protocol, addrToString<family>(address), prefix, "");
802     bus.call_noreply(newreq);
803 }
804 
805 /** @brief Trivial helper for getting the IPv4 address from getIfAddrs()
806  *
807  *  @param[in] bus    - The bus object used for lookups
808  *  @param[in] params - The parameters for the channel
809  *  @return The address and prefix if found
810  */
811 auto getIfAddr4(sdbusplus::bus::bus& bus, const ChannelParams& params)
812 {
813     return getIfAddr<AF_INET>(bus, params, 0, originsV4);
814 }
815 
816 /** @brief Reconfigures the IPv4 address info configured for the interface
817  *
818  *  @param[in] bus     - The bus object used for lookups
819  *  @param[in] params  - The parameters for the channel
820  *  @param[in] address - The new address if specified
821  *  @param[in] prefix  - The new address prefix if specified
822  */
823 void reconfigureIfAddr4(sdbusplus::bus::bus& bus, const ChannelParams& params,
824                         const std::optional<in_addr>& address,
825                         std::optional<uint8_t> prefix)
826 {
827     auto ifaddr = getIfAddr4(bus, params);
828     if (!ifaddr && !address)
829     {
830         log<level::ERR>("Missing address for IPv4 assignment");
831         elog<InternalFailure>();
832     }
833     uint8_t fallbackPrefix = AddrFamily<AF_INET>::defaultPrefix;
834     if (ifaddr)
835     {
836         fallbackPrefix = ifaddr->prefix;
837         deleteObjectIfExists(bus, params.service, ifaddr->path);
838     }
839     createIfAddr<AF_INET>(bus, params, address.value_or(ifaddr->address),
840                           prefix.value_or(fallbackPrefix));
841 }
842 
843 template <int family>
844 std::optional<IfNeigh<family>>
845     findStaticNeighbor(sdbusplus::bus::bus& bus, const ChannelParams& params,
846                        const typename AddrFamily<family>::addr& ip,
847                        ObjectLookupCache& neighbors)
848 {
849     const auto state =
850         sdbusplus::xyz::openbmc_project::Network::server::convertForMessage(
851             Neighbor::State::Permanent);
852     for (const auto& [path, neighbor] : neighbors)
853     {
854         const auto& ipStr = std::get<std::string>(neighbor.at("IPAddress"));
855         auto neighIP = maybeStringToAddr<family>(ipStr.c_str());
856         if (!neighIP)
857         {
858             continue;
859         }
860         if (!equal(*neighIP, ip))
861         {
862             continue;
863         }
864         if (state != std::get<std::string>(neighbor.at("State")))
865         {
866             continue;
867         }
868 
869         IfNeigh<family> ret;
870         ret.path = path;
871         ret.ip = ip;
872         const auto& macStr = std::get<std::string>(neighbor.at("MACAddress"));
873         ret.mac = stringToMAC(macStr.c_str());
874         return std::move(ret);
875     }
876 
877     return std::nullopt;
878 }
879 
880 template <int family>
881 void createNeighbor(sdbusplus::bus::bus& bus, const ChannelParams& params,
882                     const typename AddrFamily<family>::addr& address,
883                     const ether_addr& mac)
884 {
885     auto newreq =
886         bus.new_method_call(params.service.c_str(), params.logicalPath.c_str(),
887                             INTF_NEIGHBOR_CREATE_STATIC, "Neighbor");
888     std::string macStr = ether_ntoa(&mac);
889     newreq.append(addrToString<family>(address), macStr);
890     bus.call_noreply(newreq);
891 }
892 
893 /** @brief Sets the system wide value for the default gateway
894  *
895  *  @param[in] bus     - The bus object used for lookups
896  *  @param[in] params  - The parameters for the channel
897  *  @param[in] gateway - Gateway address to apply
898  */
899 template <int family>
900 void setGatewayProperty(sdbusplus::bus::bus& bus, const ChannelParams& params,
901                         const typename AddrFamily<family>::addr& address)
902 {
903     // Save the old gateway MAC address if it exists so we can recreate it
904     auto gateway = getGatewayProperty<family>(bus, params);
905     std::optional<IfNeigh<family>> neighbor;
906     if (gateway)
907     {
908         ObjectLookupCache neighbors(bus, params, INTF_NEIGHBOR);
909         neighbor = findStaticNeighbor<family>(bus, params, *gateway, neighbors);
910     }
911 
912     setDbusProperty(bus, params.service, PATH_SYSTEMCONFIG, INTF_SYSTEMCONFIG,
913                     AddrFamily<family>::propertyGateway,
914                     addrToString<family>(address));
915 
916     // Restore the gateway MAC if we had one
917     if (neighbor)
918     {
919         deleteObjectIfExists(bus, params.service, neighbor->path);
920         createNeighbor<family>(bus, params, address, neighbor->mac);
921     }
922 }
923 
924 template <int family>
925 std::optional<IfNeigh<family>> findGatewayNeighbor(sdbusplus::bus::bus& bus,
926                                                    const ChannelParams& params,
927                                                    ObjectLookupCache& neighbors)
928 {
929     auto gateway = getGatewayProperty<family>(bus, params);
930     if (!gateway)
931     {
932         return std::nullopt;
933     }
934 
935     return findStaticNeighbor<family>(bus, params, *gateway, neighbors);
936 }
937 
938 template <int family>
939 std::optional<IfNeigh<family>> getGatewayNeighbor(sdbusplus::bus::bus& bus,
940                                                   const ChannelParams& params)
941 {
942     ObjectLookupCache neighbors(bus, params, INTF_NEIGHBOR);
943     return findGatewayNeighbor<family>(bus, params, neighbors);
944 }
945 
946 template <int family>
947 void reconfigureGatewayMAC(sdbusplus::bus::bus& bus,
948                            const ChannelParams& params, const ether_addr& mac)
949 {
950     auto gateway = getGatewayProperty<family>(bus, params);
951     if (!gateway)
952     {
953         log<level::ERR>("Tried to set Gateway MAC without Gateway");
954         elog<InternalFailure>();
955     }
956 
957     ObjectLookupCache neighbors(bus, params, INTF_NEIGHBOR);
958     auto neighbor =
959         findStaticNeighbor<family>(bus, params, *gateway, neighbors);
960     if (neighbor)
961     {
962         deleteObjectIfExists(bus, params.service, neighbor->path);
963     }
964 
965     createNeighbor<family>(bus, params, *gateway, mac);
966 }
967 
968 /** @brief Deconfigures the IPv6 address info configured for the interface
969  *
970  *  @param[in] bus     - The bus object used for lookups
971  *  @param[in] params  - The parameters for the channel
972  *  @param[in] idx     - The address index to operate on
973  */
974 void deconfigureIfAddr6(sdbusplus::bus::bus& bus, const ChannelParams& params,
975                         uint8_t idx)
976 {
977     auto ifaddr = getIfAddr<AF_INET6>(bus, params, idx, originsV6Static);
978     if (ifaddr)
979     {
980         deleteObjectIfExists(bus, params.service, ifaddr->path);
981     }
982 }
983 
984 /** @brief Reconfigures the IPv6 address info configured for the interface
985  *
986  *  @param[in] bus     - The bus object used for lookups
987  *  @param[in] params  - The parameters for the channel
988  *  @param[in] idx     - The address index to operate on
989  *  @param[in] address - The new address
990  *  @param[in] prefix  - The new address prefix
991  */
992 void reconfigureIfAddr6(sdbusplus::bus::bus& bus, const ChannelParams& params,
993                         uint8_t idx, const in6_addr& address, uint8_t prefix)
994 {
995     deconfigureIfAddr6(bus, params, idx);
996     createIfAddr<AF_INET6>(bus, params, address, prefix);
997 }
998 
999 /** @brief Converts the AddressOrigin into an IPv6Source
1000  *
1001  *  @param[in] origin - The DBus Address Origin to convert
1002  *  @return The IPv6Source version of the origin
1003  */
1004 IPv6Source originToSourceType(IP::AddressOrigin origin)
1005 {
1006     switch (origin)
1007     {
1008         case IP::AddressOrigin::Static:
1009             return IPv6Source::Static;
1010         case IP::AddressOrigin::DHCP:
1011             return IPv6Source::DHCP;
1012         case IP::AddressOrigin::SLAAC:
1013             return IPv6Source::SLAAC;
1014         default:
1015         {
1016             auto originStr = sdbusplus::xyz::openbmc_project::Network::server::
1017                 convertForMessage(origin);
1018             log<level::ERR>(
1019                 "Invalid IP::AddressOrigin conversion to IPv6Source",
1020                 entry("ORIGIN=%s", originStr.c_str()));
1021             elog<InternalFailure>();
1022         }
1023     }
1024 }
1025 
1026 /** @brief Packs the IPMI message response with IPv6 address data
1027  *
1028  *  @param[out] ret     - The IPMI response payload to be packed
1029  *  @param[in]  channel - The channel id corresponding to an ethernet interface
1030  *  @param[in]  set     - The set selector for determining address index
1031  *  @param[in]  origins - Set of valid origins for address filtering
1032  */
1033 void getLanIPv6Address(message::Payload& ret, uint8_t channel, uint8_t set,
1034                        const std::unordered_set<IP::AddressOrigin>& origins)
1035 {
1036     auto source = IPv6Source::Static;
1037     bool enabled = false;
1038     in6_addr addr{};
1039     uint8_t prefix = AddrFamily<AF_INET6>::defaultPrefix;
1040     auto status = IPv6AddressStatus::Disabled;
1041 
1042     auto ifaddr = channelCall<getIfAddr<AF_INET6>>(channel, set, origins);
1043     if (ifaddr)
1044     {
1045         source = originToSourceType(ifaddr->origin);
1046         enabled = true;
1047         addr = ifaddr->address;
1048         prefix = ifaddr->prefix;
1049         status = IPv6AddressStatus::Active;
1050     }
1051 
1052     ret.pack(set);
1053     ret.pack(static_cast<uint4_t>(source), uint3_t{}, enabled);
1054     ret.pack(std::string_view(reinterpret_cast<char*>(&addr), sizeof(addr)));
1055     ret.pack(prefix);
1056     ret.pack(static_cast<uint8_t>(status));
1057 }
1058 
1059 /** @brief Gets the vlan ID configured on the interface
1060  *
1061  *  @param[in] bus    - The bus object used for lookups
1062  *  @param[in] params - The parameters for the channel
1063  *  @return VLAN id or the standard 0 for no VLAN
1064  */
1065 uint16_t getVLANProperty(sdbusplus::bus::bus& bus, const ChannelParams& params)
1066 {
1067     // VLAN devices will always have a separate logical object
1068     if (params.ifPath == params.logicalPath)
1069     {
1070         return 0;
1071     }
1072 
1073     auto vlan = std::get<uint32_t>(getDbusProperty(
1074         bus, params.service, params.logicalPath, INTF_VLAN, "Id"));
1075     if ((vlan & VLAN_VALUE_MASK) != vlan)
1076     {
1077         logWithChannel<level::ERR>(params, "networkd returned an invalid vlan",
1078                                    entry("VLAN=%" PRIu32, vlan));
1079         elog<InternalFailure>();
1080     }
1081     return vlan;
1082 }
1083 
1084 /** @brief Deletes all of the possible configuration parameters for a channel
1085  *
1086  *  @param[in] bus    - The bus object used for lookups
1087  *  @param[in] params - The parameters for the channel
1088  */
1089 void deconfigureChannel(sdbusplus::bus::bus& bus, ChannelParams& params)
1090 {
1091     // Delete all objects associated with the interface
1092     auto objreq = bus.new_method_call(MAPPER_BUS_NAME, MAPPER_OBJ, MAPPER_INTF,
1093                                       "GetSubTree");
1094     objreq.append(PATH_ROOT, 0, std::vector<std::string>{DELETE_INTERFACE});
1095     auto objreply = bus.call(objreq);
1096     ObjectTree objs;
1097     objreply.read(objs);
1098     for (const auto& [path, impls] : objs)
1099     {
1100         if (path.find(params.ifname) == path.npos)
1101         {
1102             continue;
1103         }
1104         for (const auto& [service, intfs] : impls)
1105         {
1106             deleteObjectIfExists(bus, service, path);
1107         }
1108         // Update params to reflect the deletion of vlan
1109         if (path == params.logicalPath)
1110         {
1111             params.logicalPath = params.ifPath;
1112         }
1113     }
1114 
1115     // Clear out any settings on the lower physical interface
1116     setDHCPv6Property(bus, params, EthernetInterface::DHCPConf::none, false);
1117 }
1118 
1119 /** @brief Creates a new VLAN on the specified interface
1120  *
1121  *  @param[in] bus    - The bus object used for lookups
1122  *  @param[in] params - The parameters for the channel
1123  *  @param[in] vlan   - The id of the new vlan
1124  */
1125 void createVLAN(sdbusplus::bus::bus& bus, ChannelParams& params, uint16_t vlan)
1126 {
1127     if (vlan == 0)
1128     {
1129         return;
1130     }
1131 
1132     auto req = bus.new_method_call(params.service.c_str(), PATH_ROOT,
1133                                    INTF_VLAN_CREATE, "VLAN");
1134     req.append(params.ifname, static_cast<uint32_t>(vlan));
1135     auto reply = bus.call(req);
1136     sdbusplus::message::object_path newPath;
1137     reply.read(newPath);
1138     params.logicalPath = std::move(newPath);
1139 }
1140 
1141 /** @brief Performs the necessary reconfiguration to change the VLAN
1142  *
1143  *  @param[in] bus    - The bus object used for lookups
1144  *  @param[in] params - The parameters for the channel
1145  *  @param[in] vlan   - The new vlan id to use
1146  */
1147 void reconfigureVLAN(sdbusplus::bus::bus& bus, ChannelParams& params,
1148                      uint16_t vlan)
1149 {
1150     // Unfortunatetly we don't have built-in functions to migrate our interface
1151     // customizations to new VLAN interfaces, or have some kind of decoupling.
1152     // We therefore must retain all of our old information, setup the new VLAN
1153     // configuration, then restore the old info.
1154 
1155     // Save info from the old logical interface
1156     ObjectLookupCache ips(bus, params, INTF_IP);
1157     auto ifaddr4 = findIfAddr<AF_INET>(bus, params, 0, originsV4, ips);
1158     std::vector<IfAddr<AF_INET6>> ifaddrs6;
1159     for (uint8_t i = 0; i < MAX_IPV6_STATIC_ADDRESSES; ++i)
1160     {
1161         auto ifaddr6 =
1162             findIfAddr<AF_INET6>(bus, params, i, originsV6Static, ips);
1163         if (!ifaddr6)
1164         {
1165             break;
1166         }
1167         ifaddrs6.push_back(std::move(*ifaddr6));
1168     }
1169     EthernetInterface::DHCPConf dhcp = getDHCPProperty(bus, params);
1170     ObjectLookupCache neighbors(bus, params, INTF_NEIGHBOR);
1171     auto neighbor4 = findGatewayNeighbor<AF_INET>(bus, params, neighbors);
1172     auto neighbor6 = findGatewayNeighbor<AF_INET6>(bus, params, neighbors);
1173 
1174     deconfigureChannel(bus, params);
1175     createVLAN(bus, params, vlan);
1176 
1177     // Re-establish the saved settings
1178     setDHCPv6Property(bus, params, dhcp, false);
1179     if (ifaddr4)
1180     {
1181         createIfAddr<AF_INET>(bus, params, ifaddr4->address, ifaddr4->prefix);
1182     }
1183     for (const auto& ifaddr6 : ifaddrs6)
1184     {
1185         createIfAddr<AF_INET6>(bus, params, ifaddr6.address, ifaddr6.prefix);
1186     }
1187     if (neighbor4)
1188     {
1189         createNeighbor<AF_INET>(bus, params, neighbor4->ip, neighbor4->mac);
1190     }
1191     if (neighbor6)
1192     {
1193         createNeighbor<AF_INET6>(bus, params, neighbor6->ip, neighbor6->mac);
1194     }
1195 }
1196 
1197 /** @brief Turns a prefix into a netmask
1198  *
1199  *  @param[in] prefix - The prefix length
1200  *  @return The netmask
1201  */
1202 in_addr prefixToNetmask(uint8_t prefix)
1203 {
1204     if (prefix > 32)
1205     {
1206         log<level::ERR>("Invalid prefix", entry("PREFIX=%" PRIu8, prefix));
1207         elog<InternalFailure>();
1208     }
1209     if (prefix == 0)
1210     {
1211         // Avoids 32-bit lshift by 32 UB
1212         return {};
1213     }
1214     return {htobe32(~UINT32_C(0) << (32 - prefix))};
1215 }
1216 
1217 /** @brief Turns a a netmask into a prefix length
1218  *
1219  *  @param[in] netmask - The netmask in byte form
1220  *  @return The prefix length
1221  */
1222 uint8_t netmaskToPrefix(in_addr netmask)
1223 {
1224     uint32_t x = be32toh(netmask.s_addr);
1225     if ((~x & (~x + 1)) != 0)
1226     {
1227         char maskStr[INET_ADDRSTRLEN];
1228         inet_ntop(AF_INET, &netmask, maskStr, sizeof(maskStr));
1229         log<level::ERR>("Invalid netmask", entry("NETMASK=%s", maskStr));
1230         elog<InternalFailure>();
1231     }
1232     return static_cast<bool>(x)
1233                ? AddrFamily<AF_INET>::defaultPrefix - __builtin_ctz(x)
1234                : 0;
1235 }
1236 
1237 // We need to store this value so it can be returned to the client
1238 // It is volatile so safe to store in daemon memory.
1239 static std::unordered_map<uint8_t, SetStatus> setStatus;
1240 
1241 // Until we have good support for fixed versions of IPMI tool
1242 // we need to return the VLAN id for disabled VLANs. The value is only
1243 // used for verification that a disable operation succeeded and will only
1244 // be sent if our system indicates that vlans are disabled.
1245 static std::unordered_map<uint8_t, uint16_t> lastDisabledVlan;
1246 
1247 /** @brief Gets the set status for the channel if it exists
1248  *         Otherise populates and returns the default value.
1249  *
1250  *  @param[in] channel - The channel id corresponding to an ethernet interface
1251  *  @return A reference to the SetStatus for the channel
1252  */
1253 SetStatus& getSetStatus(uint8_t channel)
1254 {
1255     auto it = setStatus.find(channel);
1256     if (it != setStatus.end())
1257     {
1258         return it->second;
1259     }
1260     return setStatus[channel] = SetStatus::Complete;
1261 }
1262 
1263 /**
1264  * Define placeholder command handlers for the OEM Extension bytes for the Set
1265  * LAN Configuration Parameters and Get LAN Configuration Parameters
1266  * commands. Using "weak" linking allows the placeholder setLanOem/getLanOem
1267  * functions below to be overridden.
1268  * To create handlers for your own proprietary command set:
1269  *   Create/modify a phosphor-ipmi-host Bitbake append file within your Yocto
1270  *   recipe
1271  *   Create C++ file(s) that define IPMI handler functions matching the
1272  *     function names below (i.e. setLanOem). The default name for the
1273  *     transport IPMI commands is transporthandler_oem.cpp.
1274  *   Add:
1275  *      EXTRA_OECONF_append = " --enable-transport-oem=yes"
1276  *   Create a do_compile_prepend()/do_install_append method in your
1277  *   bbappend file to copy the file to the build directory.
1278  *   Add:
1279  *   PROJECT_SRC_DIR := "${THISDIR}/${PN}"
1280  *   # Copy the "strong" functions into the working directory, overriding the
1281  *   # placeholder functions.
1282  *   do_compile_prepend(){
1283  *      cp -f ${PROJECT_SRC_DIR}/transporthandler_oem.cpp ${S}
1284  *   }
1285  *
1286  *   # Clean up after complilation has completed
1287  *   do_install_append(){
1288  *      rm -f ${S}/transporthandler_oem.cpp
1289  *   }
1290  *
1291  */
1292 
1293 /**
1294  * Define the placeholder OEM commands as having weak linkage. Create
1295  * setLanOem, and getLanOem functions in the transporthandler_oem.cpp
1296  * file. The functions defined there must not have the "weak" attribute
1297  * applied to them.
1298  */
1299 RspType<> setLanOem(uint8_t channel, uint8_t parameter, message::Payload& req)
1300     __attribute__((weak));
1301 RspType<message::Payload> getLanOem(uint8_t channel, uint8_t parameter,
1302                                     uint8_t set, uint8_t block)
1303     __attribute__((weak));
1304 
1305 RspType<> setLanOem(uint8_t channel, uint8_t parameter, message::Payload& req)
1306 {
1307     req.trailingOk = true;
1308     return response(ccParamNotSupported);
1309 }
1310 
1311 RspType<message::Payload> getLanOem(uint8_t channel, uint8_t parameter,
1312                                     uint8_t set, uint8_t block)
1313 {
1314     return response(ccParamNotSupported);
1315 }
1316 /**
1317  * @brief is MAC address valid.
1318  *
1319  * This function checks whether the MAC address is valid or not.
1320  *
1321  * @param[in] mac - MAC address.
1322  * @return true if MAC address is valid else retun false.
1323  **/
1324 bool isValidMACAddress(const ether_addr& mac)
1325 {
1326     // check if mac address is empty
1327     if (equal(mac, ether_addr{}))
1328     {
1329         return false;
1330     }
1331     // we accept only unicast MAC addresses and  same thing has been checked in
1332     // phosphor-network layer. If the least significant bit of the first octet
1333     // is set to 1, it is multicast MAC else it is unicast MAC address.
1334     if (mac.ether_addr_octet[0] & 1)
1335     {
1336         return false;
1337     }
1338     return true;
1339 }
1340 
1341 RspType<> setLan(Context::ptr ctx, uint4_t channelBits, uint4_t reserved1,
1342                  uint8_t parameter, message::Payload& req)
1343 {
1344     const uint8_t channel = convertCurrentChannelNum(
1345         static_cast<uint8_t>(channelBits), ctx->channel);
1346     if (reserved1 || !isValidChannel(channel))
1347     {
1348         log<level::ERR>("Set Lan - Invalid field in request");
1349         req.trailingOk = true;
1350         return responseInvalidFieldRequest();
1351     }
1352 
1353     switch (static_cast<LanParam>(parameter))
1354     {
1355         case LanParam::SetStatus:
1356         {
1357             uint2_t flag;
1358             uint6_t rsvd;
1359             if (req.unpack(flag, rsvd) != 0 || !req.fullyUnpacked())
1360             {
1361                 return responseReqDataLenInvalid();
1362             }
1363             if (rsvd)
1364             {
1365                 return responseInvalidFieldRequest();
1366             }
1367             auto status = static_cast<SetStatus>(static_cast<uint8_t>(flag));
1368             switch (status)
1369             {
1370                 case SetStatus::Complete:
1371                 {
1372                     getSetStatus(channel) = status;
1373                     return responseSuccess();
1374                 }
1375                 case SetStatus::InProgress:
1376                 {
1377                     auto& storedStatus = getSetStatus(channel);
1378                     if (storedStatus == SetStatus::InProgress)
1379                     {
1380                         return response(ccParamSetLocked);
1381                     }
1382                     storedStatus = status;
1383                     return responseSuccess();
1384                 }
1385                 case SetStatus::Commit:
1386                     if (getSetStatus(channel) != SetStatus::InProgress)
1387                     {
1388                         return responseInvalidFieldRequest();
1389                     }
1390                     return responseSuccess();
1391             }
1392             return response(ccParamNotSupported);
1393         }
1394         case LanParam::AuthSupport:
1395         {
1396             req.trailingOk = true;
1397             return response(ccParamReadOnly);
1398         }
1399         case LanParam::AuthEnables:
1400         {
1401             req.trailingOk = true;
1402             return response(ccParamReadOnly);
1403         }
1404         case LanParam::IP:
1405         {
1406             EthernetInterface::DHCPConf dhcp =
1407                 channelCall<getDHCPProperty>(channel);
1408             if ((dhcp == EthernetInterface::DHCPConf::v4) ||
1409                 (dhcp == EthernetInterface::DHCPConf::both))
1410             {
1411                 return responseCommandNotAvailable();
1412             }
1413             in_addr ip;
1414             std::array<uint8_t, sizeof(ip)> bytes;
1415             if (req.unpack(bytes) != 0 || !req.fullyUnpacked())
1416             {
1417                 return responseReqDataLenInvalid();
1418             }
1419             copyInto(ip, bytes);
1420             channelCall<reconfigureIfAddr4>(channel, ip, std::nullopt);
1421             return responseSuccess();
1422         }
1423         case LanParam::IPSrc:
1424         {
1425             uint4_t flag;
1426             uint4_t rsvd;
1427             if (req.unpack(flag, rsvd) != 0 || !req.fullyUnpacked())
1428             {
1429                 return responseReqDataLenInvalid();
1430             }
1431             if (rsvd)
1432             {
1433                 return responseInvalidFieldRequest();
1434             }
1435             switch (static_cast<IPSrc>(static_cast<uint8_t>(flag)))
1436             {
1437                 case IPSrc::DHCP:
1438                 {
1439                     // The IPSrc IPMI command is only for IPv4
1440                     // management. Modifying IPv6 state is done using
1441                     // a completely different Set LAN Configuration
1442                     // subcommand.
1443                     channelCall<setDHCPv4Property>(
1444                         channel, EthernetInterface::DHCPConf::v4);
1445                     return responseSuccess();
1446                 }
1447                 case IPSrc::Unspecified:
1448                 case IPSrc::Static:
1449                 {
1450                     channelCall<setDHCPv4Property>(
1451                         channel, EthernetInterface::DHCPConf::none);
1452                     return responseSuccess();
1453                 }
1454                 case IPSrc::BIOS:
1455                 case IPSrc::BMC:
1456                 {
1457                     return responseInvalidFieldRequest();
1458                 }
1459             }
1460             return response(ccParamNotSupported);
1461         }
1462         case LanParam::MAC:
1463         {
1464             ether_addr mac;
1465             std::array<uint8_t, sizeof(mac)> bytes;
1466             if (req.unpack(bytes) != 0 || !req.fullyUnpacked())
1467             {
1468                 return responseReqDataLenInvalid();
1469             }
1470             copyInto(mac, bytes);
1471 
1472             if (!isValidMACAddress(mac))
1473             {
1474                 return responseInvalidFieldRequest();
1475             }
1476             channelCall<setMACProperty>(channel, mac);
1477             return responseSuccess();
1478         }
1479         case LanParam::SubnetMask:
1480         {
1481             EthernetInterface::DHCPConf dhcp =
1482                 channelCall<getDHCPProperty>(channel);
1483             if ((dhcp == EthernetInterface::DHCPConf::v4) ||
1484                 (dhcp == EthernetInterface::DHCPConf::both))
1485             {
1486                 return responseCommandNotAvailable();
1487             }
1488             in_addr netmask;
1489             std::array<uint8_t, sizeof(netmask)> bytes;
1490             if (req.unpack(bytes) != 0 || !req.fullyUnpacked())
1491             {
1492                 return responseReqDataLenInvalid();
1493             }
1494             copyInto(netmask, bytes);
1495             channelCall<reconfigureIfAddr4>(channel, std::nullopt,
1496                                             netmaskToPrefix(netmask));
1497             return responseSuccess();
1498         }
1499         case LanParam::Gateway1:
1500         {
1501             EthernetInterface::DHCPConf dhcp =
1502                 channelCall<getDHCPProperty>(channel);
1503             if ((dhcp == EthernetInterface::DHCPConf::v4) ||
1504                 (dhcp == EthernetInterface::DHCPConf::both))
1505             {
1506                 return responseCommandNotAvailable();
1507             }
1508             in_addr gateway;
1509             std::array<uint8_t, sizeof(gateway)> bytes;
1510             if (req.unpack(bytes) != 0 || !req.fullyUnpacked())
1511             {
1512                 return responseReqDataLenInvalid();
1513             }
1514             copyInto(gateway, bytes);
1515             channelCall<setGatewayProperty<AF_INET>>(channel, gateway);
1516             return responseSuccess();
1517         }
1518         case LanParam::Gateway1MAC:
1519         {
1520             ether_addr gatewayMAC;
1521             std::array<uint8_t, sizeof(gatewayMAC)> bytes;
1522             if (req.unpack(bytes) != 0 || !req.fullyUnpacked())
1523             {
1524                 return responseReqDataLenInvalid();
1525             }
1526             copyInto(gatewayMAC, bytes);
1527             channelCall<reconfigureGatewayMAC<AF_INET>>(channel, gatewayMAC);
1528             return responseSuccess();
1529         }
1530         case LanParam::VLANId:
1531         {
1532             uint12_t vlanData = 0;
1533             uint3_t reserved = 0;
1534             bool vlanEnable = 0;
1535 
1536             if (req.unpack(vlanData) || req.unpack(reserved) ||
1537                 req.unpack(vlanEnable) || !req.fullyUnpacked())
1538             {
1539                 return responseReqDataLenInvalid();
1540             }
1541 
1542             if (reserved)
1543             {
1544                 return responseInvalidFieldRequest();
1545             }
1546 
1547             uint16_t vlan = static_cast<uint16_t>(vlanData);
1548 
1549             if (!vlanEnable)
1550             {
1551                 lastDisabledVlan[channel] = vlan;
1552                 vlan = 0;
1553             }
1554             else if (vlan == 0 || vlan == VLAN_VALUE_MASK)
1555             {
1556                 return responseInvalidFieldRequest();
1557             }
1558 
1559             channelCall<reconfigureVLAN>(channel, vlan);
1560             return responseSuccess();
1561         }
1562         case LanParam::CiphersuiteSupport:
1563         case LanParam::CiphersuiteEntries:
1564         case LanParam::IPFamilySupport:
1565         {
1566             req.trailingOk = true;
1567             return response(ccParamReadOnly);
1568         }
1569         case LanParam::IPFamilyEnables:
1570         {
1571             uint8_t enables;
1572             if (req.unpack(enables) != 0 || !req.fullyUnpacked())
1573             {
1574                 return responseReqDataLenInvalid();
1575             }
1576             switch (static_cast<IPFamilyEnables>(enables))
1577             {
1578                 case IPFamilyEnables::DualStack:
1579                     return responseSuccess();
1580                 case IPFamilyEnables::IPv4Only:
1581                 case IPFamilyEnables::IPv6Only:
1582                     return response(ccParamNotSupported);
1583             }
1584             return response(ccParamNotSupported);
1585         }
1586         case LanParam::IPv6Status:
1587         {
1588             req.trailingOk = true;
1589             return response(ccParamReadOnly);
1590         }
1591         case LanParam::IPv6StaticAddresses:
1592         {
1593             uint8_t set;
1594             uint7_t rsvd;
1595             bool enabled;
1596             in6_addr ip;
1597             std::array<uint8_t, sizeof(ip)> ipbytes;
1598             uint8_t prefix;
1599             uint8_t status;
1600             if (req.unpack(set, rsvd, enabled, ipbytes, prefix, status) != 0 ||
1601                 !req.fullyUnpacked())
1602             {
1603                 return responseReqDataLenInvalid();
1604             }
1605             if (rsvd)
1606             {
1607                 return responseInvalidFieldRequest();
1608             }
1609             copyInto(ip, ipbytes);
1610             if (enabled)
1611             {
1612                 channelCall<reconfigureIfAddr6>(channel, set, ip, prefix);
1613             }
1614             else
1615             {
1616                 channelCall<deconfigureIfAddr6>(channel, set);
1617             }
1618             return responseSuccess();
1619         }
1620         case LanParam::IPv6DynamicAddresses:
1621         {
1622             req.trailingOk = true;
1623             return response(ccParamReadOnly);
1624         }
1625         case LanParam::IPv6RouterControl:
1626         {
1627             std::bitset<8> control;
1628             if (req.unpack(control) != 0 || !req.fullyUnpacked())
1629             {
1630                 return responseReqDataLenInvalid();
1631             }
1632             std::bitset<8> expected;
1633             EthernetInterface::DHCPConf dhcp =
1634                 channelCall<getDHCPProperty>(channel);
1635             if ((dhcp == EthernetInterface::DHCPConf::both) |
1636                 (dhcp == EthernetInterface::DHCPConf::v6))
1637             {
1638                 expected[IPv6RouterControlFlag::Dynamic] = 1;
1639             }
1640             else
1641             {
1642                 expected[IPv6RouterControlFlag::Static] = 1;
1643             }
1644             if (expected != control)
1645             {
1646                 return responseInvalidFieldRequest();
1647             }
1648             return responseSuccess();
1649         }
1650         case LanParam::IPv6StaticRouter1IP:
1651         {
1652             in6_addr gateway;
1653             std::array<uint8_t, sizeof(gateway)> bytes;
1654             if (req.unpack(bytes) != 0 || !req.fullyUnpacked())
1655             {
1656                 return responseReqDataLenInvalid();
1657             }
1658             copyInto(gateway, bytes);
1659             channelCall<setGatewayProperty<AF_INET6>>(channel, gateway);
1660             return responseSuccess();
1661         }
1662         case LanParam::IPv6StaticRouter1MAC:
1663         {
1664             ether_addr mac;
1665             std::array<uint8_t, sizeof(mac)> bytes;
1666             if (req.unpack(bytes) != 0 || !req.fullyUnpacked())
1667             {
1668                 return responseReqDataLenInvalid();
1669             }
1670             copyInto(mac, bytes);
1671             channelCall<reconfigureGatewayMAC<AF_INET6>>(channel, mac);
1672             return responseSuccess();
1673         }
1674         case LanParam::IPv6StaticRouter1PrefixLength:
1675         {
1676             uint8_t prefix;
1677             if (req.unpack(prefix) != 0 || !req.fullyUnpacked())
1678             {
1679                 return responseReqDataLenInvalid();
1680             }
1681             if (prefix != 0)
1682             {
1683                 return responseInvalidFieldRequest();
1684             }
1685             return responseSuccess();
1686         }
1687         case LanParam::IPv6StaticRouter1PrefixValue:
1688         {
1689             std::array<uint8_t, sizeof(in6_addr)> bytes;
1690             if (req.unpack(bytes) != 0 || !req.fullyUnpacked())
1691             {
1692                 return responseReqDataLenInvalid();
1693             }
1694             // Accept any prefix value since our prefix length has to be 0
1695             return responseSuccess();
1696         }
1697         case LanParam::cipherSuitePrivilegeLevels:
1698         {
1699             uint8_t reserved;
1700             std::array<uint4_t, ipmi::maxCSRecords> cipherSuitePrivs;
1701 
1702             if (req.unpack(reserved, cipherSuitePrivs) || !req.fullyUnpacked())
1703             {
1704                 return responseReqDataLenInvalid();
1705             }
1706 
1707             if (reserved)
1708             {
1709                 return responseInvalidFieldRequest();
1710             }
1711 
1712             uint8_t resp =
1713                 getCipherConfigObject(csPrivFileName, csPrivDefaultFileName)
1714                     .setCSPrivilegeLevels(channel, cipherSuitePrivs);
1715             if (!resp)
1716             {
1717                 return responseSuccess();
1718             }
1719             else
1720             {
1721                 req.trailingOk = true;
1722                 return response(resp);
1723             }
1724         }
1725     }
1726 
1727     if ((parameter >= oemCmdStart) && (parameter <= oemCmdEnd))
1728     {
1729         return setLanOem(channel, parameter, req);
1730     }
1731 
1732     req.trailingOk = true;
1733     return response(ccParamNotSupported);
1734 }
1735 
1736 RspType<message::Payload> getLan(Context::ptr ctx, uint4_t channelBits,
1737                                  uint3_t reserved, bool revOnly,
1738                                  uint8_t parameter, uint8_t set, uint8_t block)
1739 {
1740     message::Payload ret;
1741     constexpr uint8_t current_revision = 0x11;
1742     ret.pack(current_revision);
1743 
1744     if (revOnly)
1745     {
1746         return responseSuccess(std::move(ret));
1747     }
1748 
1749     const uint8_t channel = convertCurrentChannelNum(
1750         static_cast<uint8_t>(channelBits), ctx->channel);
1751     if (reserved || !isValidChannel(channel))
1752     {
1753         log<level::ERR>("Get Lan - Invalid field in request");
1754         return responseInvalidFieldRequest();
1755     }
1756 
1757     static std::vector<uint8_t> cipherList;
1758     static bool listInit = false;
1759     if (!listInit)
1760     {
1761         try
1762         {
1763             cipherList = cipher::getCipherList();
1764             listInit = true;
1765         }
1766         catch (const std::exception& e)
1767         {
1768         }
1769     }
1770 
1771     switch (static_cast<LanParam>(parameter))
1772     {
1773         case LanParam::SetStatus:
1774         {
1775             SetStatus status;
1776             try
1777             {
1778                 status = setStatus.at(channel);
1779             }
1780             catch (const std::out_of_range&)
1781             {
1782                 status = SetStatus::Complete;
1783             }
1784             ret.pack(static_cast<uint2_t>(status), uint6_t{});
1785             return responseSuccess(std::move(ret));
1786         }
1787         case LanParam::AuthSupport:
1788         {
1789             std::bitset<6> support;
1790             ret.pack(support, uint2_t{});
1791             return responseSuccess(std::move(ret));
1792         }
1793         case LanParam::AuthEnables:
1794         {
1795             std::bitset<6> enables;
1796             ret.pack(enables, uint2_t{}); // Callback
1797             ret.pack(enables, uint2_t{}); // User
1798             ret.pack(enables, uint2_t{}); // Operator
1799             ret.pack(enables, uint2_t{}); // Admin
1800             ret.pack(enables, uint2_t{}); // OEM
1801             return responseSuccess(std::move(ret));
1802         }
1803         case LanParam::IP:
1804         {
1805             auto ifaddr = channelCall<getIfAddr4>(channel);
1806             in_addr addr{};
1807             if (ifaddr)
1808             {
1809                 addr = ifaddr->address;
1810             }
1811             ret.pack(dataRef(addr));
1812             return responseSuccess(std::move(ret));
1813         }
1814         case LanParam::IPSrc:
1815         {
1816             auto src = IPSrc::Static;
1817             EthernetInterface::DHCPConf dhcp =
1818                 channelCall<getDHCPProperty>(channel);
1819             if ((dhcp == EthernetInterface::DHCPConf::v4) ||
1820                 (dhcp == EthernetInterface::DHCPConf::both))
1821             {
1822                 src = IPSrc::DHCP;
1823             }
1824             ret.pack(static_cast<uint4_t>(src), uint4_t{});
1825             return responseSuccess(std::move(ret));
1826         }
1827         case LanParam::MAC:
1828         {
1829             ether_addr mac = channelCall<getMACProperty>(channel);
1830             ret.pack(dataRef(mac));
1831             return responseSuccess(std::move(ret));
1832         }
1833         case LanParam::SubnetMask:
1834         {
1835             auto ifaddr = channelCall<getIfAddr4>(channel);
1836             uint8_t prefix = AddrFamily<AF_INET>::defaultPrefix;
1837             if (ifaddr)
1838             {
1839                 prefix = ifaddr->prefix;
1840             }
1841             in_addr netmask = prefixToNetmask(prefix);
1842             ret.pack(dataRef(netmask));
1843             return responseSuccess(std::move(ret));
1844         }
1845         case LanParam::Gateway1:
1846         {
1847             auto gateway =
1848                 channelCall<getGatewayProperty<AF_INET>>(channel).value_or(
1849                     in_addr{});
1850             ret.pack(dataRef(gateway));
1851             return responseSuccess(std::move(ret));
1852         }
1853         case LanParam::Gateway1MAC:
1854         {
1855             ether_addr mac{};
1856             auto neighbor = channelCall<getGatewayNeighbor<AF_INET>>(channel);
1857             if (neighbor)
1858             {
1859                 mac = neighbor->mac;
1860             }
1861             ret.pack(dataRef(mac));
1862             return responseSuccess(std::move(ret));
1863         }
1864         case LanParam::VLANId:
1865         {
1866             uint16_t vlan = channelCall<getVLANProperty>(channel);
1867             if (vlan != 0)
1868             {
1869                 vlan |= VLAN_ENABLE_FLAG;
1870             }
1871             else
1872             {
1873                 vlan = lastDisabledVlan[channel];
1874             }
1875             ret.pack(vlan);
1876             return responseSuccess(std::move(ret));
1877         }
1878         case LanParam::CiphersuiteSupport:
1879         {
1880             if (getChannelSessionSupport(channel) ==
1881                 EChannelSessSupported::none)
1882             {
1883                 return responseInvalidFieldRequest();
1884             }
1885             if (!listInit)
1886             {
1887                 return responseUnspecifiedError();
1888             }
1889             ret.pack(static_cast<uint8_t>(cipherList.size() - 1));
1890             return responseSuccess(std::move(ret));
1891         }
1892         case LanParam::CiphersuiteEntries:
1893         {
1894             if (getChannelSessionSupport(channel) ==
1895                 EChannelSessSupported::none)
1896             {
1897                 return responseInvalidFieldRequest();
1898             }
1899             if (!listInit)
1900             {
1901                 return responseUnspecifiedError();
1902             }
1903             ret.pack(cipherList);
1904             return responseSuccess(std::move(ret));
1905         }
1906         case LanParam::IPFamilySupport:
1907         {
1908             std::bitset<8> support;
1909             support[IPFamilySupportFlag::IPv6Only] = 0;
1910             support[IPFamilySupportFlag::DualStack] = 1;
1911             support[IPFamilySupportFlag::IPv6Alerts] = 1;
1912             ret.pack(support);
1913             return responseSuccess(std::move(ret));
1914         }
1915         case LanParam::IPFamilyEnables:
1916         {
1917             ret.pack(static_cast<uint8_t>(IPFamilyEnables::DualStack));
1918             return responseSuccess(std::move(ret));
1919         }
1920         case LanParam::IPv6Status:
1921         {
1922             ret.pack(MAX_IPV6_STATIC_ADDRESSES);
1923             ret.pack(MAX_IPV6_DYNAMIC_ADDRESSES);
1924             std::bitset<8> support;
1925             support[IPv6StatusFlag::DHCP] = 1;
1926             support[IPv6StatusFlag::SLAAC] = 1;
1927             ret.pack(support);
1928             return responseSuccess(std::move(ret));
1929         }
1930         case LanParam::IPv6StaticAddresses:
1931         {
1932             if (set >= MAX_IPV6_STATIC_ADDRESSES)
1933             {
1934                 return responseParmOutOfRange();
1935             }
1936             getLanIPv6Address(ret, channel, set, originsV6Static);
1937             return responseSuccess(std::move(ret));
1938         }
1939         case LanParam::IPv6DynamicAddresses:
1940         {
1941             if (set >= MAX_IPV6_DYNAMIC_ADDRESSES)
1942             {
1943                 return responseParmOutOfRange();
1944             }
1945             getLanIPv6Address(ret, channel, set, originsV6Dynamic);
1946             return responseSuccess(std::move(ret));
1947         }
1948         case LanParam::IPv6RouterControl:
1949         {
1950             std::bitset<8> control;
1951             EthernetInterface::DHCPConf dhcp =
1952                 channelCall<getDHCPProperty>(channel);
1953             if ((dhcp == EthernetInterface::DHCPConf::both) ||
1954                 (dhcp == EthernetInterface::DHCPConf::v6))
1955             {
1956                 control[IPv6RouterControlFlag::Dynamic] = 1;
1957             }
1958             else
1959             {
1960                 control[IPv6RouterControlFlag::Static] = 1;
1961             }
1962             ret.pack(control);
1963             return responseSuccess(std::move(ret));
1964         }
1965         case LanParam::IPv6StaticRouter1IP:
1966         {
1967             in6_addr gateway{};
1968             EthernetInterface::DHCPConf dhcp =
1969                 channelCall<getDHCPProperty>(channel);
1970             if ((dhcp == EthernetInterface::DHCPConf::v4) ||
1971                 (dhcp == EthernetInterface::DHCPConf::none))
1972             {
1973                 gateway =
1974                     channelCall<getGatewayProperty<AF_INET6>>(channel).value_or(
1975                         in6_addr{});
1976             }
1977             ret.pack(dataRef(gateway));
1978             return responseSuccess(std::move(ret));
1979         }
1980         case LanParam::IPv6StaticRouter1MAC:
1981         {
1982             ether_addr mac{};
1983             auto neighbor = channelCall<getGatewayNeighbor<AF_INET6>>(channel);
1984             if (neighbor)
1985             {
1986                 mac = neighbor->mac;
1987             }
1988             ret.pack(dataRef(mac));
1989             return responseSuccess(std::move(ret));
1990         }
1991         case LanParam::IPv6StaticRouter1PrefixLength:
1992         {
1993             ret.pack(UINT8_C(0));
1994             return responseSuccess(std::move(ret));
1995         }
1996         case LanParam::IPv6StaticRouter1PrefixValue:
1997         {
1998             in6_addr prefix{};
1999             ret.pack(dataRef(prefix));
2000             return responseSuccess(std::move(ret));
2001         }
2002         case LanParam::cipherSuitePrivilegeLevels:
2003         {
2004             std::array<uint4_t, ipmi::maxCSRecords> csPrivilegeLevels;
2005 
2006             uint8_t resp =
2007                 getCipherConfigObject(csPrivFileName, csPrivDefaultFileName)
2008                     .getCSPrivilegeLevels(channel, csPrivilegeLevels);
2009             if (!resp)
2010             {
2011                 constexpr uint8_t reserved1 = 0x00;
2012                 ret.pack(reserved1, csPrivilegeLevels);
2013                 return responseSuccess(std::move(ret));
2014             }
2015             else
2016             {
2017                 return response(resp);
2018             }
2019         }
2020     }
2021 
2022     if ((parameter >= oemCmdStart) && (parameter <= oemCmdEnd))
2023     {
2024         return getLanOem(channel, parameter, set, block);
2025     }
2026 
2027     return response(ccParamNotSupported);
2028 }
2029 
2030 } // namespace transport
2031 } // namespace ipmi
2032 
2033 void register_netfn_transport_functions() __attribute__((constructor));
2034 
2035 void register_netfn_transport_functions()
2036 {
2037     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnTransport,
2038                           ipmi::transport::cmdSetLanConfigParameters,
2039                           ipmi::Privilege::Admin, ipmi::transport::setLan);
2040     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnTransport,
2041                           ipmi::transport::cmdGetLanConfigParameters,
2042                           ipmi::Privilege::Operator, ipmi::transport::getLan);
2043 }
2044