1 #include "app/channel.hpp"
2 
3 #include <arpa/inet.h>
4 #include <netinet/ether.h>
5 
6 #include <array>
7 #include <bitset>
8 #include <cinttypes>
9 #include <cstdint>
10 #include <cstring>
11 #include <fstream>
12 #include <functional>
13 #include <ipmid/api.hpp>
14 #include <ipmid/message.hpp>
15 #include <ipmid/message/types.hpp>
16 #include <ipmid/types.hpp>
17 #include <ipmid/utils.hpp>
18 #include <optional>
19 #include <phosphor-logging/elog-errors.hpp>
20 #include <phosphor-logging/elog.hpp>
21 #include <phosphor-logging/log.hpp>
22 #include <sdbusplus/bus.hpp>
23 #include <sdbusplus/exception.hpp>
24 #include <string>
25 #include <string_view>
26 #include <type_traits>
27 #include <unordered_map>
28 #include <unordered_set>
29 #include <user_channel/channel_layer.hpp>
30 #include <utility>
31 #include <vector>
32 #include <xyz/openbmc_project/Common/error.hpp>
33 #include <xyz/openbmc_project/Network/IP/server.hpp>
34 #include <xyz/openbmc_project/Network/Neighbor/server.hpp>
35 
36 using phosphor::logging::commit;
37 using phosphor::logging::elog;
38 using phosphor::logging::entry;
39 using phosphor::logging::level;
40 using phosphor::logging::log;
41 using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
42 using sdbusplus::xyz::openbmc_project::Network::server::IP;
43 using sdbusplus::xyz::openbmc_project::Network::server::Neighbor;
44 
45 namespace cipher
46 {
47 
48 std::vector<uint8_t> getCipherList()
49 {
50     std::vector<uint8_t> cipherList;
51 
52     std::ifstream jsonFile(cipher::configFile);
53     if (!jsonFile.is_open())
54     {
55         log<level::ERR>("Channel Cipher suites file not found");
56         elog<InternalFailure>();
57     }
58 
59     auto data = Json::parse(jsonFile, nullptr, false);
60     if (data.is_discarded())
61     {
62         log<level::ERR>("Parsing channel cipher suites JSON failed");
63         elog<InternalFailure>();
64     }
65 
66     // Byte 1 is reserved
67     cipherList.push_back(0x00);
68 
69     for (const auto& record : data)
70     {
71         cipherList.push_back(record.value(cipher, 0));
72     }
73 
74     return cipherList;
75 }
76 } // namespace cipher
77 
78 namespace ipmi
79 {
80 namespace transport
81 {
82 
83 // LAN Handler specific response codes
84 constexpr Cc ccParamNotSupported = 0x80;
85 constexpr Cc ccParamSetLocked = 0x81;
86 constexpr Cc ccParamReadOnly = 0x82;
87 
88 // VLANs are a 12-bit value
89 constexpr uint16_t VLAN_VALUE_MASK = 0x0fff;
90 constexpr uint16_t VLAN_ENABLE_FLAG = 0x8000;
91 
92 // Arbitrary v6 Address Limits to prevent too much output in ipmitool
93 constexpr uint8_t MAX_IPV6_STATIC_ADDRESSES = 15;
94 constexpr uint8_t MAX_IPV6_DYNAMIC_ADDRESSES = 15;
95 
96 // D-Bus Network Daemon definitions
97 constexpr auto PATH_ROOT = "/xyz/openbmc_project/network";
98 constexpr auto PATH_SYSTEMCONFIG = "/xyz/openbmc_project/network/config";
99 
100 constexpr auto INTF_SYSTEMCONFIG =
101     "xyz.openbmc_project.Network.SystemConfiguration";
102 constexpr auto INTF_ETHERNET = "xyz.openbmc_project.Network.EthernetInterface";
103 constexpr auto INTF_IP = "xyz.openbmc_project.Network.IP";
104 constexpr auto INTF_IP_CREATE = "xyz.openbmc_project.Network.IP.Create";
105 constexpr auto INTF_MAC = "xyz.openbmc_project.Network.MACAddress";
106 constexpr auto INTF_NEIGHBOR = "xyz.openbmc_project.Network.Neighbor";
107 constexpr auto INTF_NEIGHBOR_CREATE_STATIC =
108     "xyz.openbmc_project.Network.Neighbor.CreateStatic";
109 constexpr auto INTF_VLAN = "xyz.openbmc_project.Network.VLAN";
110 constexpr auto INTF_VLAN_CREATE = "xyz.openbmc_project.Network.VLAN.Create";
111 
112 /** @brief Generic paramters for different address families */
113 template <int family>
114 struct AddrFamily
115 {
116 };
117 
118 /** @brief Parameter specialization for IPv4 */
119 template <>
120 struct AddrFamily<AF_INET>
121 {
122     using addr = in_addr;
123     static constexpr auto protocol = IP::Protocol::IPv4;
124     static constexpr size_t maxStrLen = INET6_ADDRSTRLEN;
125     static constexpr uint8_t defaultPrefix = 32;
126     static constexpr char propertyGateway[] = "DefaultGateway";
127 };
128 
129 /** @brief Parameter specialization for IPv6 */
130 template <>
131 struct AddrFamily<AF_INET6>
132 {
133     using addr = in6_addr;
134     static constexpr auto protocol = IP::Protocol::IPv6;
135     static constexpr size_t maxStrLen = INET6_ADDRSTRLEN;
136     static constexpr uint8_t defaultPrefix = 128;
137     static constexpr char propertyGateway[] = "DefaultGateway6";
138 };
139 
140 /** @brief Valid address origins for IPv4 */
141 const std::unordered_set<IP::AddressOrigin> originsV4 = {
142     IP::AddressOrigin::Static,
143     IP::AddressOrigin::DHCP,
144 };
145 
146 /** @brief Valid address origins for IPv6 */
147 const std::unordered_set<IP::AddressOrigin> originsV6Static = {
148     IP::AddressOrigin::Static};
149 const std::unordered_set<IP::AddressOrigin> originsV6Dynamic = {
150     IP::AddressOrigin::DHCP,
151     IP::AddressOrigin::SLAAC,
152 };
153 
154 /** @brief Interface IP Address configuration parameters */
155 template <int family>
156 struct IfAddr
157 {
158     std::string path;
159     typename AddrFamily<family>::addr address;
160     IP::AddressOrigin origin;
161     uint8_t prefix;
162 };
163 
164 /** @brief Interface Neighbor configuration parameters */
165 template <int family>
166 struct IfNeigh
167 {
168     std::string path;
169     typename AddrFamily<family>::addr ip;
170     ether_addr mac;
171 };
172 
173 /** @brief IPMI LAN Parameters */
174 enum class LanParam : uint8_t
175 {
176     SetStatus = 0,
177     AuthSupport = 1,
178     AuthEnables = 2,
179     IP = 3,
180     IPSrc = 4,
181     MAC = 5,
182     SubnetMask = 6,
183     Gateway1 = 12,
184     Gateway1MAC = 13,
185     VLANId = 20,
186     CiphersuiteSupport = 22,
187     CiphersuiteEntries = 23,
188     IPFamilySupport = 50,
189     IPFamilyEnables = 51,
190     IPv6Status = 55,
191     IPv6StaticAddresses = 56,
192     IPv6DynamicAddresses = 59,
193     IPv6RouterControl = 64,
194     IPv6StaticRouter1IP = 65,
195     IPv6StaticRouter1MAC = 66,
196     IPv6StaticRouter1PrefixLength = 67,
197     IPv6StaticRouter1PrefixValue = 68,
198 };
199 
200 static constexpr uint8_t oemCmdStart = 192;
201 static constexpr uint8_t oemCmdEnd = 255;
202 
203 /** @brief IPMI IP Origin Types */
204 enum class IPSrc : uint8_t
205 {
206     Unspecified = 0,
207     Static = 1,
208     DHCP = 2,
209     BIOS = 3,
210     BMC = 4,
211 };
212 
213 /** @brief IPMI Set Status */
214 enum class SetStatus : uint8_t
215 {
216     Complete = 0,
217     InProgress = 1,
218     Commit = 2,
219 };
220 
221 /** @brief IPMI Family Suport Bits */
222 namespace IPFamilySupportFlag
223 {
224 constexpr uint8_t IPv6Only = 0;
225 constexpr uint8_t DualStack = 1;
226 constexpr uint8_t IPv6Alerts = 2;
227 } // namespace IPFamilySupportFlag
228 
229 /** @brief IPMI IPFamily Enables Flag */
230 enum class IPFamilyEnables : uint8_t
231 {
232     IPv4Only = 0,
233     IPv6Only = 1,
234     DualStack = 2,
235 };
236 
237 /** @brief IPMI IPv6 Dyanmic Status Bits */
238 namespace IPv6StatusFlag
239 {
240 constexpr uint8_t DHCP = 0;
241 constexpr uint8_t SLAAC = 1;
242 }; // namespace IPv6StatusFlag
243 
244 /** @brief IPMI IPv6 Source */
245 enum class IPv6Source : uint8_t
246 {
247     Static = 0,
248     SLAAC = 1,
249     DHCP = 2,
250 };
251 
252 /** @brief IPMI IPv6 Address Status */
253 enum class IPv6AddressStatus : uint8_t
254 {
255     Active = 0,
256     Disabled = 1,
257 };
258 
259 namespace IPv6RouterControlFlag
260 {
261 constexpr uint8_t Static = 0;
262 constexpr uint8_t Dynamic = 1;
263 }; // namespace IPv6RouterControlFlag
264 
265 /** @brief A trivial helper used to determine if two PODs are equal
266  *
267  *  @params[in] a - The first object to compare
268  *  @params[in] b - The second object to compare
269  *  @return True if the objects are the same bytewise
270  */
271 template <typename T>
272 bool equal(const T& a, const T& b)
273 {
274     static_assert(std::is_trivially_copyable_v<T>);
275     return std::memcmp(&a, &b, sizeof(T)) == 0;
276 }
277 
278 /** @brief Copies bytes from an array into a trivially copyable container
279  *
280  *  @params[out] t     - The container receiving the data
281  *  @params[in]  bytes - The data to copy
282  */
283 template <size_t N, typename T>
284 void copyInto(T& t, const std::array<uint8_t, N>& bytes)
285 {
286     static_assert(std::is_trivially_copyable_v<T>);
287     static_assert(N == sizeof(T));
288     std::memcpy(&t, bytes.data(), bytes.size());
289 }
290 
291 /** @brief Gets a generic view of the bytes in the input container
292  *
293  *  @params[in] t - The data to reference
294  *  @return A string_view referencing the bytes in the container
295  */
296 template <typename T>
297 std::string_view dataRef(const T& t)
298 {
299     static_assert(std::is_trivially_copyable_v<T>);
300     return {reinterpret_cast<const char*>(&t), sizeof(T)};
301 }
302 
303 /** @brief The dbus parameters for the interface corresponding to a channel
304  *         This helps reduce the number of mapper lookups we need for each
305  *         query and simplifies finding the VLAN interface if needed.
306  */
307 struct ChannelParams
308 {
309     /** @brief The channel ID */
310     int id;
311     /** @brief channel name for the interface */
312     std::string ifname;
313     /** @brief Name of the service on the bus */
314     std::string service;
315     /** @brief Lower level adapter path that is guaranteed to not be a VLAN */
316     std::string ifPath;
317     /** @brief Logical adapter path used for address assignment */
318     std::string logicalPath;
319 };
320 
321 /** @brief Determines the ethernet interface name corresponding to a channel
322  *         Tries to map a VLAN object first so that the address information
323  *         is accurate. Otherwise it gets the standard ethernet interface.
324  *
325  *  @param[in] bus     - The bus object used for lookups
326  *  @param[in] channel - The channel id corresponding to an ethernet interface
327  *  @return Ethernet interface service and object path if it exists
328  */
329 std::optional<ChannelParams> maybeGetChannelParams(sdbusplus::bus::bus& bus,
330                                                    uint8_t channel)
331 {
332     auto ifname = getChannelName(channel);
333     if (ifname.empty())
334     {
335         return std::nullopt;
336     }
337 
338     // Enumerate all VLAN + ETHERNET interfaces
339     auto req = bus.new_method_call(MAPPER_BUS_NAME, MAPPER_OBJ, MAPPER_INTF,
340                                    "GetSubTree");
341     req.append(PATH_ROOT, 0,
342                std::vector<std::string>{INTF_VLAN, INTF_ETHERNET});
343     auto reply = bus.call(req);
344     ObjectTree objs;
345     reply.read(objs);
346 
347     ChannelParams params;
348     for (const auto& [path, impls] : objs)
349     {
350         if (path.find(ifname) == path.npos)
351         {
352             continue;
353         }
354         for (const auto& [service, intfs] : impls)
355         {
356             bool vlan = false;
357             bool ethernet = false;
358             for (const auto& intf : intfs)
359             {
360                 if (intf == INTF_VLAN)
361                 {
362                     vlan = true;
363                 }
364                 else if (intf == INTF_ETHERNET)
365                 {
366                     ethernet = true;
367                 }
368             }
369             if (params.service.empty() && (vlan || ethernet))
370             {
371                 params.service = service;
372             }
373             if (params.ifPath.empty() && !vlan && ethernet)
374             {
375                 params.ifPath = path;
376             }
377             if (params.logicalPath.empty() && vlan)
378             {
379                 params.logicalPath = path;
380             }
381         }
382     }
383 
384     // We must have a path for the underlying interface
385     if (params.ifPath.empty())
386     {
387         return std::nullopt;
388     }
389     // We don't have a VLAN so the logical path is the same
390     if (params.logicalPath.empty())
391     {
392         params.logicalPath = params.ifPath;
393     }
394 
395     params.id = channel;
396     params.ifname = std::move(ifname);
397     return std::move(params);
398 }
399 
400 /** @brief A trivial helper around maybeGetChannelParams() that throws an
401  *         exception when it is unable to acquire parameters for the channel.
402  *
403  *  @param[in] bus     - The bus object used for lookups
404  *  @param[in] channel - The channel id corresponding to an ethernet interface
405  *  @return Ethernet interface service and object path
406  */
407 ChannelParams getChannelParams(sdbusplus::bus::bus& bus, uint8_t channel)
408 {
409     auto params = maybeGetChannelParams(bus, channel);
410     if (!params)
411     {
412         log<level::ERR>("Failed to get channel params",
413                         entry("CHANNEL=%" PRIu8, channel));
414         elog<InternalFailure>();
415     }
416     return std::move(*params);
417 }
418 
419 /** @brief Wraps the phosphor logging method to insert some additional metadata
420  *
421  *  @param[in] params - The parameters for the channel
422  *  ...
423  */
424 template <auto level, typename... Args>
425 auto logWithChannel(const ChannelParams& params, Args&&... args)
426 {
427     return log<level>(std::forward<Args>(args)...,
428                       entry("CHANNEL=%d", params.id),
429                       entry("IFNAME=%s", params.ifname.c_str()));
430 }
431 template <auto level, typename... Args>
432 auto logWithChannel(const std::optional<ChannelParams>& params, Args&&... args)
433 {
434     if (params)
435     {
436         return logWithChannel<level>(*params, std::forward<Args>(args)...);
437     }
438     return log<level>(std::forward<Args>(args)...);
439 }
440 
441 /** @brief Trivializes using parameter getter functions by providing a bus
442  *         and channel parameters automatically.
443  *
444  *  @param[in] channel - The channel id corresponding to an ethernet interface
445  *  ...
446  */
447 template <auto func, typename... Args>
448 auto channelCall(uint8_t channel, Args&&... args)
449 {
450     sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection());
451     auto params = getChannelParams(bus, channel);
452     return std::invoke(func, bus, params, std::forward<Args>(args)...);
453 }
454 
455 /** @brief Determines if the ethernet interface is using DHCP
456  *
457  *  @param[in] bus    - The bus object used for lookups
458  *  @param[in] params - The parameters for the channel
459  *  @return True if DHCP is enabled, false otherwise
460  */
461 bool getDHCPProperty(sdbusplus::bus::bus& bus, const ChannelParams& params)
462 {
463     return std::get<bool>(getDbusProperty(
464         bus, params.service, params.logicalPath, INTF_ETHERNET, "DHCPEnabled"));
465 }
466 
467 /** @brief Sets the system value for DHCP on the given interface
468  *
469  *  @param[in] bus    - The bus object used for lookups
470  *  @param[in] params - The parameters for the channel
471  *  @param[in] on     - Whether or not to enable DHCP
472  */
473 void setDHCPProperty(sdbusplus::bus::bus& bus, const ChannelParams& params,
474                      bool on)
475 {
476     setDbusProperty(bus, params.service, params.logicalPath, INTF_ETHERNET,
477                     "DHCPEnabled", on);
478 }
479 
480 /** @brief Converts a human readable MAC string into MAC bytes
481  *
482  *  @param[in] mac - The MAC string
483  *  @return MAC in bytes
484  */
485 ether_addr stringToMAC(const char* mac)
486 {
487     const ether_addr* ret = ether_aton(mac);
488     if (ret == nullptr)
489     {
490         log<level::ERR>("Invalid MAC Address", entry("MAC=%s", mac));
491         elog<InternalFailure>();
492     }
493     return *ret;
494 }
495 
496 /** @brief Determines the MAC of the ethernet interface
497  *
498  *  @param[in] bus    - The bus object used for lookups
499  *  @param[in] params - The parameters for the channel
500  *  @return The configured mac address
501  */
502 ether_addr getMACProperty(sdbusplus::bus::bus& bus, const ChannelParams& params)
503 {
504     auto macStr = std::get<std::string>(getDbusProperty(
505         bus, params.service, params.ifPath, INTF_MAC, "MACAddress"));
506     return stringToMAC(macStr.c_str());
507 }
508 
509 /** @brief Sets the system value for MAC address on the given interface
510  *
511  *  @param[in] bus    - The bus object used for lookups
512  *  @param[in] params - The parameters for the channel
513  *  @param[in] mac    - MAC address to apply
514  */
515 void setMACProperty(sdbusplus::bus::bus& bus, const ChannelParams& params,
516                     const ether_addr& mac)
517 {
518     std::string macStr = ether_ntoa(&mac);
519     setDbusProperty(bus, params.service, params.ifPath, INTF_MAC, "MACAddress",
520                     macStr);
521 }
522 
523 /** @brief Turns an IP address string into the network byte order form
524  *         NOTE: This version strictly validates family matches
525  *
526  *  @param[in] address - The string form of the address
527  *  @return A network byte order address or none if conversion failed
528  */
529 template <int family>
530 std::optional<typename AddrFamily<family>::addr>
531     maybeStringToAddr(const char* address)
532 {
533     typename AddrFamily<family>::addr ret;
534     if (inet_pton(family, address, &ret) == 1)
535     {
536         return ret;
537     }
538     return std::nullopt;
539 }
540 
541 /** @brief Turns an IP address string into the network byte order form
542  *         NOTE: This version strictly validates family matches
543  *
544  *  @param[in] address - The string form of the address
545  *  @return A network byte order address
546  */
547 template <int family>
548 typename AddrFamily<family>::addr stringToAddr(const char* address)
549 {
550     auto ret = maybeStringToAddr<family>(address);
551     if (!ret)
552     {
553         log<level::ERR>("Failed to convert IP Address",
554                         entry("FAMILY=%d", family),
555                         entry("ADDRESS=%s", address));
556         elog<InternalFailure>();
557     }
558     return *ret;
559 }
560 
561 /** @brief Turns an IP address in network byte order into a string
562  *
563  *  @param[in] address - The string form of the address
564  *  @return A network byte order address
565  */
566 template <int family>
567 std::string addrToString(const typename AddrFamily<family>::addr& address)
568 {
569     std::string ret(AddrFamily<family>::maxStrLen, '\0');
570     inet_ntop(family, &address, ret.data(), ret.size());
571     ret.resize(strlen(ret.c_str()));
572     return ret;
573 }
574 
575 /** @brief Retrieves the current gateway for the address family on the system
576  *         NOTE: The gateway is currently system wide and not per channel
577  *
578  *  @param[in] bus    - The bus object used for lookups
579  *  @param[in] params - The parameters for the channel
580  *  @return An address representing the gateway address if it exists
581  */
582 template <int family>
583 std::optional<typename AddrFamily<family>::addr>
584     getGatewayProperty(sdbusplus::bus::bus& bus, const ChannelParams& params)
585 {
586     auto gatewayStr = std::get<std::string>(getDbusProperty(
587         bus, params.service, PATH_SYSTEMCONFIG, INTF_SYSTEMCONFIG,
588         AddrFamily<family>::propertyGateway));
589     if (gatewayStr.empty())
590     {
591         return std::nullopt;
592     }
593     return stringToAddr<family>(gatewayStr.c_str());
594 }
595 
596 /** @brief A lazy lookup mechanism for iterating over object properties stored
597  *         in DBus. This will only perform the object lookup when needed, and
598  *         retains a cache of previous lookups to speed up future iterations.
599  */
600 class ObjectLookupCache
601 {
602   public:
603     using PropertiesCache = std::unordered_map<std::string, PropertyMap>;
604 
605     /** @brief Creates a new ObjectLookupCache for the interface on the bus
606      *         NOTE: The inputs to this object must outlive the object since
607      *         they are only referenced by it.
608      *
609      *  @param[in] bus    - The bus object used for lookups
610      *  @param[in] params - The parameters for the channel
611      *  @param[in] intf   - The interface we are looking up
612      */
613     ObjectLookupCache(sdbusplus::bus::bus& bus, const ChannelParams& params,
614                       const char* intf) :
615         bus(bus),
616         params(params), intf(intf),
617         objs(getAllDbusObjects(bus, params.logicalPath, intf, ""))
618     {
619     }
620 
621     class iterator : public ObjectTree::const_iterator
622     {
623       public:
624         using value_type = PropertiesCache::value_type;
625 
626         iterator(ObjectTree::const_iterator it, ObjectLookupCache& container) :
627             ObjectTree::const_iterator(it), container(container),
628             ret(container.cache.end())
629         {
630         }
631         value_type& operator*()
632         {
633             ret = container.get(ObjectTree::const_iterator::operator*().first);
634             return *ret;
635         }
636         value_type* operator->()
637         {
638             return &operator*();
639         }
640 
641       private:
642         ObjectLookupCache& container;
643         PropertiesCache::iterator ret;
644     };
645 
646     iterator begin() noexcept
647     {
648         return iterator(objs.begin(), *this);
649     }
650 
651     iterator end() noexcept
652     {
653         return iterator(objs.end(), *this);
654     }
655 
656   private:
657     sdbusplus::bus::bus& bus;
658     const ChannelParams& params;
659     const char* const intf;
660     const ObjectTree objs;
661     PropertiesCache cache;
662 
663     /** @brief Gets a cached copy of the object properties if possible
664      *         Otherwise performs a query on DBus to look them up
665      *
666      *  @param[in] path - The object path to lookup
667      *  @return An iterator for the specified object path + properties
668      */
669     PropertiesCache::iterator get(const std::string& path)
670     {
671         auto it = cache.find(path);
672         if (it != cache.end())
673         {
674             return it;
675         }
676         auto properties = getAllDbusProperties(bus, params.service, path, intf);
677         return cache.insert({path, std::move(properties)}).first;
678     }
679 };
680 
681 /** @brief Searches the ip object lookup cache for an address matching
682  *         the input parameters. NOTE: The index lacks stability across address
683  *         changes since the network daemon has no notion of stable indicies.
684  *
685  *  @param[in] bus     - The bus object used for lookups
686  *  @param[in] params  - The parameters for the channel
687  *  @param[in] idx     - The index of the desired address on the interface
688  *  @param[in] origins - The allowed origins for the address objects
689  *  @param[in] ips     - The object lookup cache holding all of the address info
690  *  @return The address and prefix if it was found
691  */
692 template <int family>
693 std::optional<IfAddr<family>>
694     findIfAddr(sdbusplus::bus::bus& bus, const ChannelParams& params,
695                uint8_t idx,
696                const std::unordered_set<IP::AddressOrigin>& origins,
697                ObjectLookupCache& ips)
698 {
699     for (const auto& [path, properties] : ips)
700     {
701         const auto& addrStr = std::get<std::string>(properties.at("Address"));
702         auto addr = maybeStringToAddr<family>(addrStr.c_str());
703         if (!addr)
704         {
705             continue;
706         }
707 
708         IP::AddressOrigin origin = IP::convertAddressOriginFromString(
709             std::get<std::string>(properties.at("Origin")));
710         if (origins.find(origin) == origins.end())
711         {
712             continue;
713         }
714 
715         if (idx > 0)
716         {
717             idx--;
718             continue;
719         }
720 
721         IfAddr<family> ifaddr;
722         ifaddr.path = path;
723         ifaddr.address = *addr;
724         ifaddr.prefix = std::get<uint8_t>(properties.at("PrefixLength"));
725         ifaddr.origin = origin;
726         return std::move(ifaddr);
727     }
728 
729     return std::nullopt;
730 }
731 
732 /** @brief Trivial helper around findIfAddr that simplifies calls
733  *         for one off lookups. Don't use this if you intend to do multiple
734  *         lookups at a time.
735  *
736  *  @param[in] bus     - The bus object used for lookups
737  *  @param[in] params  - The parameters for the channel
738  *  @param[in] idx     - The index of the desired address on the interface
739  *  @param[in] origins - The allowed origins for the address objects
740  *  @return The address and prefix if it was found
741  */
742 template <int family>
743 auto getIfAddr(sdbusplus::bus::bus& bus, const ChannelParams& params,
744                uint8_t idx,
745                const std::unordered_set<IP::AddressOrigin>& origins)
746 {
747     ObjectLookupCache ips(bus, params, INTF_IP);
748     return findIfAddr<family>(bus, params, idx, origins, ips);
749 }
750 
751 /** @brief Deletes the dbus object. Ignores empty objects or objects that are
752  *         missing from the bus.
753  *
754  *  @param[in] bus     - The bus object used for lookups
755  *  @param[in] service - The name of the service
756  *  @param[in] path    - The path of the object to delete
757  */
758 void deleteObjectIfExists(sdbusplus::bus::bus& bus, const std::string& service,
759                           const std::string& path)
760 {
761     if (path.empty())
762     {
763         return;
764     }
765     try
766     {
767         auto req = bus.new_method_call(service.c_str(), path.c_str(),
768                                        ipmi::DELETE_INTERFACE, "Delete");
769         bus.call_noreply(req);
770     }
771     catch (const sdbusplus::exception::SdBusError& e)
772     {
773         if (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     setDHCPProperty(bus, params, 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     auto 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     setDHCPProperty(bus, params, dhcp);
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 RspType<> setLan(uint4_t channelBits, uint4_t, uint8_t parameter,
1318                  message::Payload& req)
1319 {
1320     auto channel = static_cast<uint8_t>(channelBits);
1321     if (!doesDeviceExist(channel))
1322     {
1323         req.trailingOk = true;
1324         return responseInvalidFieldRequest();
1325     }
1326 
1327     switch (static_cast<LanParam>(parameter))
1328     {
1329         case LanParam::SetStatus:
1330         {
1331             uint2_t flag;
1332             uint6_t rsvd;
1333             if (req.unpack(flag, rsvd) != 0 || !req.fullyUnpacked())
1334             {
1335                 return responseReqDataLenInvalid();
1336             }
1337             if (rsvd)
1338             {
1339                 return responseInvalidFieldRequest();
1340             }
1341             auto status = static_cast<SetStatus>(static_cast<uint8_t>(flag));
1342             switch (status)
1343             {
1344                 case SetStatus::Complete:
1345                 {
1346                     getSetStatus(channel) = status;
1347                     return responseSuccess();
1348                 }
1349                 case SetStatus::InProgress:
1350                 {
1351                     auto& storedStatus = getSetStatus(channel);
1352                     if (storedStatus == SetStatus::InProgress)
1353                     {
1354                         return response(ccParamSetLocked);
1355                     }
1356                     storedStatus = status;
1357                     return responseSuccess();
1358                 }
1359                 case SetStatus::Commit:
1360                     if (getSetStatus(channel) != SetStatus::InProgress)
1361                     {
1362                         return responseInvalidFieldRequest();
1363                     }
1364                     return responseSuccess();
1365             }
1366             return response(ccParamNotSupported);
1367         }
1368         case LanParam::AuthSupport:
1369         {
1370             req.trailingOk = true;
1371             return response(ccParamReadOnly);
1372         }
1373         case LanParam::AuthEnables:
1374         {
1375             req.trailingOk = true;
1376             return response(ccParamReadOnly);
1377         }
1378         case LanParam::IP:
1379         {
1380             in_addr ip;
1381             std::array<uint8_t, sizeof(ip)> bytes;
1382             if (req.unpack(bytes) != 0 || !req.fullyUnpacked())
1383             {
1384                 return responseReqDataLenInvalid();
1385             }
1386             copyInto(ip, bytes);
1387             channelCall<reconfigureIfAddr4>(channel, ip, std::nullopt);
1388             return responseSuccess();
1389         }
1390         case LanParam::IPSrc:
1391         {
1392             uint4_t flag;
1393             uint4_t rsvd;
1394             if (req.unpack(flag, rsvd) != 0 || !req.fullyUnpacked())
1395             {
1396                 return responseReqDataLenInvalid();
1397             }
1398             if (rsvd)
1399             {
1400                 return responseInvalidFieldRequest();
1401             }
1402             switch (static_cast<IPSrc>(static_cast<uint8_t>(flag)))
1403             {
1404                 case IPSrc::DHCP:
1405                 {
1406                     channelCall<setDHCPProperty>(channel, true);
1407                     return responseSuccess();
1408                 }
1409                 case IPSrc::Unspecified:
1410                 case IPSrc::Static:
1411                 case IPSrc::BIOS:
1412                 case IPSrc::BMC:
1413                 {
1414                     channelCall<setDHCPProperty>(channel, false);
1415                     return responseSuccess();
1416                 }
1417             }
1418             return response(ccParamNotSupported);
1419         }
1420         case LanParam::MAC:
1421         {
1422             ether_addr mac;
1423             std::array<uint8_t, sizeof(mac)> bytes;
1424             if (req.unpack(bytes) != 0 || !req.fullyUnpacked())
1425             {
1426                 return responseReqDataLenInvalid();
1427             }
1428             copyInto(mac, bytes);
1429             channelCall<setMACProperty>(channel, mac);
1430             return responseSuccess();
1431         }
1432         case LanParam::SubnetMask:
1433         {
1434             in_addr netmask;
1435             std::array<uint8_t, sizeof(netmask)> bytes;
1436             if (req.unpack(bytes) != 0 || !req.fullyUnpacked())
1437             {
1438                 return responseReqDataLenInvalid();
1439             }
1440             copyInto(netmask, bytes);
1441             channelCall<reconfigureIfAddr4>(channel, std::nullopt,
1442                                             netmaskToPrefix(netmask));
1443             return responseSuccess();
1444         }
1445         case LanParam::Gateway1:
1446         {
1447             in_addr gateway;
1448             std::array<uint8_t, sizeof(gateway)> bytes;
1449             if (req.unpack(bytes) != 0 || !req.fullyUnpacked())
1450             {
1451                 return responseReqDataLenInvalid();
1452             }
1453             copyInto(gateway, bytes);
1454             channelCall<setGatewayProperty<AF_INET>>(channel, gateway);
1455             return responseSuccess();
1456         }
1457         case LanParam::Gateway1MAC:
1458         {
1459             ether_addr gatewayMAC;
1460             std::array<uint8_t, sizeof(gatewayMAC)> bytes;
1461             if (req.unpack(bytes) != 0 || !req.fullyUnpacked())
1462             {
1463                 return responseReqDataLenInvalid();
1464             }
1465             copyInto(gatewayMAC, bytes);
1466             channelCall<reconfigureGatewayMAC<AF_INET>>(channel, gatewayMAC);
1467             return responseSuccess();
1468         }
1469         case LanParam::VLANId:
1470         {
1471             uint16_t vlanData;
1472             if (req.unpack(vlanData) != 0 || !req.fullyUnpacked())
1473             {
1474                 return responseReqDataLenInvalid();
1475             }
1476             if ((vlanData & VLAN_ENABLE_FLAG) == 0)
1477             {
1478                 lastDisabledVlan[channel] = vlanData & VLAN_VALUE_MASK;
1479                 vlanData = 0;
1480             }
1481             channelCall<reconfigureVLAN>(channel, vlanData & VLAN_VALUE_MASK);
1482             return responseSuccess();
1483         }
1484         case LanParam::CiphersuiteSupport:
1485         case LanParam::CiphersuiteEntries:
1486         case LanParam::IPFamilySupport:
1487         {
1488             req.trailingOk = true;
1489             return response(ccParamReadOnly);
1490         }
1491         case LanParam::IPFamilyEnables:
1492         {
1493             uint8_t enables;
1494             if (req.unpack(enables) != 0 || !req.fullyUnpacked())
1495             {
1496                 return responseReqDataLenInvalid();
1497             }
1498             switch (static_cast<IPFamilyEnables>(enables))
1499             {
1500                 case IPFamilyEnables::DualStack:
1501                     return responseSuccess();
1502                 case IPFamilyEnables::IPv4Only:
1503                 case IPFamilyEnables::IPv6Only:
1504                     return response(ccParamNotSupported);
1505             }
1506             return response(ccParamNotSupported);
1507         }
1508         case LanParam::IPv6Status:
1509         {
1510             req.trailingOk = true;
1511             return response(ccParamReadOnly);
1512         }
1513         case LanParam::IPv6StaticAddresses:
1514         {
1515             uint8_t set;
1516             uint7_t rsvd;
1517             bool enabled;
1518             in6_addr ip;
1519             std::array<uint8_t, sizeof(ip)> ipbytes;
1520             uint8_t prefix;
1521             uint8_t status;
1522             if (req.unpack(set, rsvd, enabled, ipbytes, prefix, status) != 0 ||
1523                 !req.fullyUnpacked())
1524             {
1525                 return responseReqDataLenInvalid();
1526             }
1527             if (rsvd)
1528             {
1529                 return responseInvalidFieldRequest();
1530             }
1531             copyInto(ip, ipbytes);
1532             if (enabled)
1533             {
1534                 channelCall<reconfigureIfAddr6>(channel, set, ip, prefix);
1535             }
1536             else
1537             {
1538                 channelCall<deconfigureIfAddr6>(channel, set);
1539             }
1540             return responseSuccess();
1541         }
1542         case LanParam::IPv6DynamicAddresses:
1543         {
1544             req.trailingOk = true;
1545             return response(ccParamReadOnly);
1546         }
1547         case LanParam::IPv6RouterControl:
1548         {
1549             std::bitset<8> control;
1550             if (req.unpack(control) != 0 || !req.fullyUnpacked())
1551             {
1552                 return responseReqDataLenInvalid();
1553             }
1554             std::bitset<8> expected;
1555             if (channelCall<getDHCPProperty>(channel))
1556             {
1557                 expected[IPv6RouterControlFlag::Dynamic] = 1;
1558             }
1559             else
1560             {
1561                 expected[IPv6RouterControlFlag::Static] = 1;
1562             }
1563             if (expected != control)
1564             {
1565                 return responseInvalidFieldRequest();
1566             }
1567             return responseSuccess();
1568         }
1569         case LanParam::IPv6StaticRouter1IP:
1570         {
1571             in6_addr gateway;
1572             std::array<uint8_t, sizeof(gateway)> bytes;
1573             if (req.unpack(bytes) != 0 || !req.fullyUnpacked())
1574             {
1575                 return responseReqDataLenInvalid();
1576             }
1577             copyInto(gateway, bytes);
1578             channelCall<setGatewayProperty<AF_INET6>>(channel, gateway);
1579             return responseSuccess();
1580         }
1581         case LanParam::IPv6StaticRouter1MAC:
1582         {
1583             ether_addr mac;
1584             std::array<uint8_t, sizeof(mac)> bytes;
1585             if (req.unpack(bytes) != 0 || !req.fullyUnpacked())
1586             {
1587                 return responseReqDataLenInvalid();
1588             }
1589             copyInto(mac, bytes);
1590             channelCall<reconfigureGatewayMAC<AF_INET6>>(channel, mac);
1591             return responseSuccess();
1592         }
1593         case LanParam::IPv6StaticRouter1PrefixLength:
1594         {
1595             uint8_t prefix;
1596             if (req.unpack(prefix) != 0 || !req.fullyUnpacked())
1597             {
1598                 return responseReqDataLenInvalid();
1599             }
1600             if (prefix != 0)
1601             {
1602                 return responseInvalidFieldRequest();
1603             }
1604             return responseSuccess();
1605         }
1606         case LanParam::IPv6StaticRouter1PrefixValue:
1607         {
1608             std::array<uint8_t, sizeof(in6_addr)> bytes;
1609             if (req.unpack(bytes) != 0 || !req.fullyUnpacked())
1610             {
1611                 return responseReqDataLenInvalid();
1612             }
1613             // Accept any prefix value since our prefix length has to be 0
1614             return responseSuccess();
1615         }
1616     }
1617 
1618     if ((parameter >= oemCmdStart) && (parameter <= oemCmdEnd))
1619     {
1620         return setLanOem(channel, parameter, req);
1621     }
1622 
1623     req.trailingOk = true;
1624     return response(ccParamNotSupported);
1625 }
1626 
1627 RspType<message::Payload> getLan(uint4_t channelBits, uint3_t, bool revOnly,
1628                                  uint8_t parameter, uint8_t set, uint8_t block)
1629 {
1630     message::Payload ret;
1631     constexpr uint8_t current_revision = 0x11;
1632     ret.pack(current_revision);
1633 
1634     if (revOnly)
1635     {
1636         return responseSuccess(std::move(ret));
1637     }
1638 
1639     auto channel = static_cast<uint8_t>(channelBits);
1640     if (!doesDeviceExist(channel))
1641     {
1642         return responseInvalidFieldRequest();
1643     }
1644 
1645     static std::vector<uint8_t> cipherList;
1646     static bool listInit = false;
1647     if (!listInit)
1648     {
1649         try
1650         {
1651             cipherList = cipher::getCipherList();
1652             listInit = true;
1653         }
1654         catch (const std::exception& e)
1655         {
1656         }
1657     }
1658 
1659     switch (static_cast<LanParam>(parameter))
1660     {
1661         case LanParam::SetStatus:
1662         {
1663             SetStatus status;
1664             try
1665             {
1666                 status = setStatus.at(channel);
1667             }
1668             catch (const std::out_of_range&)
1669             {
1670                 status = SetStatus::Complete;
1671             }
1672             ret.pack(static_cast<uint2_t>(status), uint6_t{});
1673             return responseSuccess(std::move(ret));
1674         }
1675         case LanParam::AuthSupport:
1676         {
1677             std::bitset<6> support;
1678             ret.pack(support, uint2_t{});
1679             return responseSuccess(std::move(ret));
1680         }
1681         case LanParam::AuthEnables:
1682         {
1683             std::bitset<6> enables;
1684             ret.pack(enables, uint2_t{}); // Callback
1685             ret.pack(enables, uint2_t{}); // User
1686             ret.pack(enables, uint2_t{}); // Operator
1687             ret.pack(enables, uint2_t{}); // Admin
1688             ret.pack(enables, uint2_t{}); // OEM
1689             return responseSuccess(std::move(ret));
1690         }
1691         case LanParam::IP:
1692         {
1693             auto ifaddr = channelCall<getIfAddr4>(channel);
1694             in_addr addr{};
1695             if (ifaddr)
1696             {
1697                 addr = ifaddr->address;
1698             }
1699             ret.pack(dataRef(addr));
1700             return responseSuccess(std::move(ret));
1701         }
1702         case LanParam::IPSrc:
1703         {
1704             auto src = IPSrc::Static;
1705             if (channelCall<getDHCPProperty>(channel))
1706             {
1707                 src = IPSrc::DHCP;
1708             }
1709             ret.pack(static_cast<uint4_t>(src), uint4_t{});
1710             return responseSuccess(std::move(ret));
1711         }
1712         case LanParam::MAC:
1713         {
1714             ether_addr mac = channelCall<getMACProperty>(channel);
1715             ret.pack(dataRef(mac));
1716             return responseSuccess(std::move(ret));
1717         }
1718         case LanParam::SubnetMask:
1719         {
1720             auto ifaddr = channelCall<getIfAddr4>(channel);
1721             uint8_t prefix = AddrFamily<AF_INET>::defaultPrefix;
1722             if (ifaddr)
1723             {
1724                 prefix = ifaddr->prefix;
1725             }
1726             in_addr netmask = prefixToNetmask(prefix);
1727             ret.pack(dataRef(netmask));
1728             return responseSuccess(std::move(ret));
1729         }
1730         case LanParam::Gateway1:
1731         {
1732             auto gateway =
1733                 channelCall<getGatewayProperty<AF_INET>>(channel).value_or(
1734                     in_addr{});
1735             ret.pack(dataRef(gateway));
1736             return responseSuccess(std::move(ret));
1737         }
1738         case LanParam::Gateway1MAC:
1739         {
1740             ether_addr mac{};
1741             auto neighbor = channelCall<getGatewayNeighbor<AF_INET>>(channel);
1742             if (neighbor)
1743             {
1744                 mac = neighbor->mac;
1745             }
1746             ret.pack(dataRef(mac));
1747             return responseSuccess(std::move(ret));
1748         }
1749         case LanParam::VLANId:
1750         {
1751             uint16_t vlan = channelCall<getVLANProperty>(channel);
1752             if (vlan != 0)
1753             {
1754                 vlan |= VLAN_ENABLE_FLAG;
1755             }
1756             else
1757             {
1758                 vlan = lastDisabledVlan[channel];
1759             }
1760             ret.pack(vlan);
1761             return responseSuccess(std::move(ret));
1762         }
1763         case LanParam::CiphersuiteSupport:
1764         {
1765             if (!listInit)
1766             {
1767                 return responseUnspecifiedError();
1768             }
1769             ret.pack(static_cast<uint8_t>(cipherList.size() - 1));
1770             return responseSuccess(std::move(ret));
1771         }
1772         case LanParam::CiphersuiteEntries:
1773         {
1774             if (!listInit)
1775             {
1776                 return responseUnspecifiedError();
1777             }
1778             ret.pack(cipherList);
1779             return responseSuccess(std::move(ret));
1780         }
1781         case LanParam::IPFamilySupport:
1782         {
1783             std::bitset<8> support;
1784             support[IPFamilySupportFlag::IPv6Only] = 0;
1785             support[IPFamilySupportFlag::DualStack] = 1;
1786             support[IPFamilySupportFlag::IPv6Alerts] = 1;
1787             ret.pack(support);
1788             return responseSuccess(std::move(ret));
1789         }
1790         case LanParam::IPFamilyEnables:
1791         {
1792             ret.pack(static_cast<uint8_t>(IPFamilyEnables::DualStack));
1793             return responseSuccess(std::move(ret));
1794         }
1795         case LanParam::IPv6Status:
1796         {
1797             ret.pack(MAX_IPV6_STATIC_ADDRESSES);
1798             ret.pack(MAX_IPV6_DYNAMIC_ADDRESSES);
1799             std::bitset<8> support;
1800             support[IPv6StatusFlag::DHCP] = 1;
1801             support[IPv6StatusFlag::SLAAC] = 1;
1802             ret.pack(support);
1803             return responseSuccess(std::move(ret));
1804         }
1805         case LanParam::IPv6StaticAddresses:
1806         {
1807             if (set >= MAX_IPV6_STATIC_ADDRESSES)
1808             {
1809                 return responseParmOutOfRange();
1810             }
1811             getLanIPv6Address(ret, channel, set, originsV6Static);
1812             return responseSuccess(std::move(ret));
1813         }
1814         case LanParam::IPv6DynamicAddresses:
1815         {
1816             if (set >= MAX_IPV6_DYNAMIC_ADDRESSES)
1817             {
1818                 return responseParmOutOfRange();
1819             }
1820             getLanIPv6Address(ret, channel, set, originsV6Dynamic);
1821             return responseSuccess(std::move(ret));
1822         }
1823         case LanParam::IPv6RouterControl:
1824         {
1825             std::bitset<8> control;
1826             if (channelCall<getDHCPProperty>(channel))
1827             {
1828                 control[IPv6RouterControlFlag::Dynamic] = 1;
1829             }
1830             else
1831             {
1832                 control[IPv6RouterControlFlag::Static] = 1;
1833             }
1834             ret.pack(control);
1835             return responseSuccess(std::move(ret));
1836         }
1837         case LanParam::IPv6StaticRouter1IP:
1838         {
1839             in6_addr gateway{};
1840             if (!channelCall<getDHCPProperty>(channel))
1841             {
1842                 gateway =
1843                     channelCall<getGatewayProperty<AF_INET6>>(channel).value_or(
1844                         in6_addr{});
1845             }
1846             ret.pack(dataRef(gateway));
1847             return responseSuccess(std::move(ret));
1848         }
1849         case LanParam::IPv6StaticRouter1MAC:
1850         {
1851             ether_addr mac{};
1852             auto neighbor = channelCall<getGatewayNeighbor<AF_INET6>>(channel);
1853             if (neighbor)
1854             {
1855                 mac = neighbor->mac;
1856             }
1857             ret.pack(dataRef(mac));
1858             return responseSuccess(std::move(ret));
1859         }
1860         case LanParam::IPv6StaticRouter1PrefixLength:
1861         {
1862             ret.pack(UINT8_C(0));
1863             return responseSuccess(std::move(ret));
1864         }
1865         case LanParam::IPv6StaticRouter1PrefixValue:
1866         {
1867             in6_addr prefix{};
1868             ret.pack(dataRef(prefix));
1869             return responseSuccess(std::move(ret));
1870         }
1871     }
1872 
1873     if ((parameter >= oemCmdStart) && (parameter <= oemCmdEnd))
1874     {
1875         return getLanOem(channel, parameter, set, block);
1876     }
1877 
1878     return response(ccParamNotSupported);
1879 }
1880 
1881 } // namespace transport
1882 } // namespace ipmi
1883 
1884 void register_netfn_transport_functions() __attribute__((constructor));
1885 
1886 void register_netfn_transport_functions()
1887 {
1888     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnTransport,
1889                           ipmi::transport::cmdSetLanConfigParameters,
1890                           ipmi::Privilege::Admin, ipmi::transport::setLan);
1891     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnTransport,
1892                           ipmi::transport::cmdGetLanConfigParameters,
1893                           ipmi::Privilege::Operator, ipmi::transport::getLan);
1894 }
1895