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 // D-Bus Network Daemon definitions
93 constexpr auto PATH_ROOT = "/xyz/openbmc_project/network";
94 constexpr auto PATH_SYSTEMCONFIG = "/xyz/openbmc_project/network/config";
95 
96 constexpr auto INTF_SYSTEMCONFIG =
97     "xyz.openbmc_project.Network.SystemConfiguration";
98 constexpr auto INTF_ETHERNET = "xyz.openbmc_project.Network.EthernetInterface";
99 constexpr auto INTF_IP = "xyz.openbmc_project.Network.IP";
100 constexpr auto INTF_IP_CREATE = "xyz.openbmc_project.Network.IP.Create";
101 constexpr auto INTF_MAC = "xyz.openbmc_project.Network.MACAddress";
102 constexpr auto INTF_NEIGHBOR = "xyz.openbmc_project.Network.Neighbor";
103 constexpr auto INTF_NEIGHBOR_CREATE_STATIC =
104     "xyz.openbmc_project.Network.Neighbor.CreateStatic";
105 constexpr auto INTF_VLAN = "xyz.openbmc_project.Network.VLAN";
106 constexpr auto INTF_VLAN_CREATE = "xyz.openbmc_project.Network.VLAN.Create";
107 
108 /** @brief Generic paramters for different address families */
109 template <int family>
110 struct AddrFamily
111 {
112 };
113 
114 /** @brief Parameter specialization for IPv4 */
115 template <>
116 struct AddrFamily<AF_INET>
117 {
118     using addr = in_addr;
119     static constexpr auto protocol = IP::Protocol::IPv4;
120     static constexpr size_t maxStrLen = INET6_ADDRSTRLEN;
121     static constexpr uint8_t defaultPrefix = 32;
122     static constexpr char propertyGateway[] = "DefaultGateway";
123 };
124 
125 /** @brief Valid address origins for IPv4 */
126 const std::unordered_set<IP::AddressOrigin> originsV4 = {
127     IP::AddressOrigin::Static,
128     IP::AddressOrigin::DHCP,
129 };
130 
131 /** @brief Interface IP Address configuration parameters */
132 template <int family>
133 struct IfAddr
134 {
135     std::string path;
136     typename AddrFamily<family>::addr address;
137     IP::AddressOrigin origin;
138     uint8_t prefix;
139 };
140 
141 /** @brief Interface Neighbor configuration parameters */
142 template <int family>
143 struct IfNeigh
144 {
145     std::string path;
146     typename AddrFamily<family>::addr ip;
147     ether_addr mac;
148 };
149 
150 /** @brief IPMI LAN Parameters */
151 enum class LanParam : uint8_t
152 {
153     SetStatus = 0,
154     AuthSupport = 1,
155     AuthEnables = 2,
156     IP = 3,
157     IPSrc = 4,
158     MAC = 5,
159     SubnetMask = 6,
160     Gateway1 = 12,
161     Gateway1MAC = 13,
162     VLANId = 20,
163     CiphersuiteSupport = 22,
164     CiphersuiteEntries = 23,
165 };
166 
167 static constexpr uint8_t oemCmdStart = 192;
168 static constexpr uint8_t oemCmdEnd = 255;
169 
170 /** @brief IPMI IP Origin Types */
171 enum class IPSrc : uint8_t
172 {
173     Unspecified = 0,
174     Static = 1,
175     DHCP = 2,
176     BIOS = 3,
177     BMC = 4,
178 };
179 
180 /** @brief IPMI Set Status */
181 enum class SetStatus : uint8_t
182 {
183     Complete = 0,
184     InProgress = 1,
185     Commit = 2,
186 };
187 
188 /** @brief A trivial helper used to determine if two PODs are equal
189  *
190  *  @params[in] a - The first object to compare
191  *  @params[in] b - The second object to compare
192  *  @return True if the objects are the same bytewise
193  */
194 template <typename T>
195 bool equal(const T& a, const T& b)
196 {
197     static_assert(std::is_trivially_copyable_v<T>);
198     return std::memcmp(&a, &b, sizeof(T)) == 0;
199 }
200 
201 /** @brief Copies bytes from an array into a trivially copyable container
202  *
203  *  @params[out] t     - The container receiving the data
204  *  @params[in]  bytes - The data to copy
205  */
206 template <size_t N, typename T>
207 void copyInto(T& t, const std::array<uint8_t, N>& bytes)
208 {
209     static_assert(std::is_trivially_copyable_v<T>);
210     static_assert(N == sizeof(T));
211     std::memcpy(&t, bytes.data(), bytes.size());
212 }
213 
214 /** @brief Gets a generic view of the bytes in the input container
215  *
216  *  @params[in] t - The data to reference
217  *  @return A string_view referencing the bytes in the container
218  */
219 template <typename T>
220 std::string_view dataRef(const T& t)
221 {
222     static_assert(std::is_trivially_copyable_v<T>);
223     return {reinterpret_cast<const char*>(&t), sizeof(T)};
224 }
225 
226 /** @brief The dbus parameters for the interface corresponding to a channel
227  *         This helps reduce the number of mapper lookups we need for each
228  *         query and simplifies finding the VLAN interface if needed.
229  */
230 struct ChannelParams
231 {
232     /** @brief The channel ID */
233     int id;
234     /** @brief channel name for the interface */
235     std::string ifname;
236     /** @brief Name of the service on the bus */
237     std::string service;
238     /** @brief Lower level adapter path that is guaranteed to not be a VLAN */
239     std::string ifPath;
240     /** @brief Logical adapter path used for address assignment */
241     std::string logicalPath;
242 };
243 
244 /** @brief Determines the ethernet interface name corresponding to a channel
245  *         Tries to map a VLAN object first so that the address information
246  *         is accurate. Otherwise it gets the standard ethernet interface.
247  *
248  *  @param[in] bus     - The bus object used for lookups
249  *  @param[in] channel - The channel id corresponding to an ethernet interface
250  *  @return Ethernet interface service and object path if it exists
251  */
252 std::optional<ChannelParams> maybeGetChannelParams(sdbusplus::bus::bus& bus,
253                                                    uint8_t channel)
254 {
255     auto ifname = getChannelName(channel);
256     if (ifname.empty())
257     {
258         return std::nullopt;
259     }
260 
261     // Enumerate all VLAN + ETHERNET interfaces
262     auto req = bus.new_method_call(MAPPER_BUS_NAME, MAPPER_OBJ, MAPPER_INTF,
263                                    "GetSubTree");
264     req.append(PATH_ROOT, 0,
265                std::vector<std::string>{INTF_VLAN, INTF_ETHERNET});
266     auto reply = bus.call(req);
267     ObjectTree objs;
268     reply.read(objs);
269 
270     ChannelParams params;
271     for (const auto& [path, impls] : objs)
272     {
273         if (path.find(ifname) == path.npos)
274         {
275             continue;
276         }
277         for (const auto& [service, intfs] : impls)
278         {
279             bool vlan = false;
280             bool ethernet = false;
281             for (const auto& intf : intfs)
282             {
283                 if (intf == INTF_VLAN)
284                 {
285                     vlan = true;
286                 }
287                 else if (intf == INTF_ETHERNET)
288                 {
289                     ethernet = true;
290                 }
291             }
292             if (params.service.empty() && (vlan || ethernet))
293             {
294                 params.service = service;
295             }
296             if (params.ifPath.empty() && !vlan && ethernet)
297             {
298                 params.ifPath = path;
299             }
300             if (params.logicalPath.empty() && vlan)
301             {
302                 params.logicalPath = path;
303             }
304         }
305     }
306 
307     // We must have a path for the underlying interface
308     if (params.ifPath.empty())
309     {
310         return std::nullopt;
311     }
312     // We don't have a VLAN so the logical path is the same
313     if (params.logicalPath.empty())
314     {
315         params.logicalPath = params.ifPath;
316     }
317 
318     params.id = channel;
319     params.ifname = std::move(ifname);
320     return std::move(params);
321 }
322 
323 /** @brief A trivial helper around maybeGetChannelParams() that throws an
324  *         exception when it is unable to acquire parameters for the channel.
325  *
326  *  @param[in] bus     - The bus object used for lookups
327  *  @param[in] channel - The channel id corresponding to an ethernet interface
328  *  @return Ethernet interface service and object path
329  */
330 ChannelParams getChannelParams(sdbusplus::bus::bus& bus, uint8_t channel)
331 {
332     auto params = maybeGetChannelParams(bus, channel);
333     if (!params)
334     {
335         log<level::ERR>("Failed to get channel params",
336                         entry("CHANNEL=%" PRIu8, channel));
337         elog<InternalFailure>();
338     }
339     return std::move(*params);
340 }
341 
342 /** @brief Wraps the phosphor logging method to insert some additional metadata
343  *
344  *  @param[in] params - The parameters for the channel
345  *  ...
346  */
347 template <auto level, typename... Args>
348 auto logWithChannel(const ChannelParams& params, Args&&... args)
349 {
350     return log<level>(std::forward<Args>(args)...,
351                       entry("CHANNEL=%d", params.id),
352                       entry("IFNAME=%s", params.ifname.c_str()));
353 }
354 template <auto level, typename... Args>
355 auto logWithChannel(const std::optional<ChannelParams>& params, Args&&... args)
356 {
357     if (params)
358     {
359         return logWithChannel<level>(*params, std::forward<Args>(args)...);
360     }
361     return log<level>(std::forward<Args>(args)...);
362 }
363 
364 /** @brief Trivializes using parameter getter functions by providing a bus
365  *         and channel parameters automatically.
366  *
367  *  @param[in] channel - The channel id corresponding to an ethernet interface
368  *  ...
369  */
370 template <auto func, typename... Args>
371 auto channelCall(uint8_t channel, Args&&... args)
372 {
373     sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection());
374     auto params = getChannelParams(bus, channel);
375     return std::invoke(func, bus, params, std::forward<Args>(args)...);
376 }
377 
378 /** @brief Determines if the ethernet interface is using DHCP
379  *
380  *  @param[in] bus    - The bus object used for lookups
381  *  @param[in] params - The parameters for the channel
382  *  @return True if DHCP is enabled, false otherwise
383  */
384 bool getDHCPProperty(sdbusplus::bus::bus& bus, const ChannelParams& params)
385 {
386     return std::get<bool>(getDbusProperty(
387         bus, params.service, params.logicalPath, INTF_ETHERNET, "DHCPEnabled"));
388 }
389 
390 /** @brief Sets the system value for DHCP on the given interface
391  *
392  *  @param[in] bus    - The bus object used for lookups
393  *  @param[in] params - The parameters for the channel
394  *  @param[in] on     - Whether or not to enable DHCP
395  */
396 void setDHCPProperty(sdbusplus::bus::bus& bus, const ChannelParams& params,
397                      bool on)
398 {
399     setDbusProperty(bus, params.service, params.logicalPath, INTF_ETHERNET,
400                     "DHCPEnabled", on);
401 }
402 
403 /** @brief Converts a human readable MAC string into MAC bytes
404  *
405  *  @param[in] mac - The MAC string
406  *  @return MAC in bytes
407  */
408 ether_addr stringToMAC(const char* mac)
409 {
410     const ether_addr* ret = ether_aton(mac);
411     if (ret == nullptr)
412     {
413         log<level::ERR>("Invalid MAC Address", entry("MAC=%s", mac));
414         elog<InternalFailure>();
415     }
416     return *ret;
417 }
418 
419 /** @brief Determines the MAC of the ethernet interface
420  *
421  *  @param[in] bus    - The bus object used for lookups
422  *  @param[in] params - The parameters for the channel
423  *  @return The configured mac address
424  */
425 ether_addr getMACProperty(sdbusplus::bus::bus& bus, const ChannelParams& params)
426 {
427     auto macStr = std::get<std::string>(getDbusProperty(
428         bus, params.service, params.ifPath, INTF_MAC, "MACAddress"));
429     return stringToMAC(macStr.c_str());
430 }
431 
432 /** @brief Sets the system value for MAC address on the given interface
433  *
434  *  @param[in] bus    - The bus object used for lookups
435  *  @param[in] params - The parameters for the channel
436  *  @param[in] mac    - MAC address to apply
437  */
438 void setMACProperty(sdbusplus::bus::bus& bus, const ChannelParams& params,
439                     const ether_addr& mac)
440 {
441     std::string macStr = ether_ntoa(&mac);
442     setDbusProperty(bus, params.service, params.ifPath, INTF_MAC, "MACAddress",
443                     macStr);
444 }
445 
446 /** @brief Turns an IP address string into the network byte order form
447  *         NOTE: This version strictly validates family matches
448  *
449  *  @param[in] address - The string form of the address
450  *  @return A network byte order address or none if conversion failed
451  */
452 template <int family>
453 std::optional<typename AddrFamily<family>::addr>
454     maybeStringToAddr(const char* address)
455 {
456     typename AddrFamily<family>::addr ret;
457     if (inet_pton(family, address, &ret) == 1)
458     {
459         return ret;
460     }
461     return std::nullopt;
462 }
463 
464 /** @brief Turns an IP address string into the network byte order form
465  *         NOTE: This version strictly validates family matches
466  *
467  *  @param[in] address - The string form of the address
468  *  @return A network byte order address
469  */
470 template <int family>
471 typename AddrFamily<family>::addr stringToAddr(const char* address)
472 {
473     auto ret = maybeStringToAddr<family>(address);
474     if (!ret)
475     {
476         log<level::ERR>("Failed to convert IP Address",
477                         entry("FAMILY=%d", family),
478                         entry("ADDRESS=%s", address));
479         elog<InternalFailure>();
480     }
481     return *ret;
482 }
483 
484 /** @brief Turns an IP address in network byte order into a string
485  *
486  *  @param[in] address - The string form of the address
487  *  @return A network byte order address
488  */
489 template <int family>
490 std::string addrToString(const typename AddrFamily<family>::addr& address)
491 {
492     std::string ret(AddrFamily<family>::maxStrLen, '\0');
493     inet_ntop(family, &address, ret.data(), ret.size());
494     ret.resize(strlen(ret.c_str()));
495     return ret;
496 }
497 
498 /** @brief Retrieves the current gateway for the address family on the system
499  *         NOTE: The gateway is currently system wide and not per channel
500  *
501  *  @param[in] bus    - The bus object used for lookups
502  *  @param[in] params - The parameters for the channel
503  *  @return An address representing the gateway address if it exists
504  */
505 template <int family>
506 std::optional<typename AddrFamily<family>::addr>
507     getGatewayProperty(sdbusplus::bus::bus& bus, const ChannelParams& params)
508 {
509     auto gatewayStr = std::get<std::string>(getDbusProperty(
510         bus, params.service, PATH_SYSTEMCONFIG, INTF_SYSTEMCONFIG,
511         AddrFamily<family>::propertyGateway));
512     if (gatewayStr.empty())
513     {
514         return std::nullopt;
515     }
516     return stringToAddr<family>(gatewayStr.c_str());
517 }
518 
519 /** @brief A lazy lookup mechanism for iterating over object properties stored
520  *         in DBus. This will only perform the object lookup when needed, and
521  *         retains a cache of previous lookups to speed up future iterations.
522  */
523 class ObjectLookupCache
524 {
525   public:
526     using PropertiesCache = std::unordered_map<std::string, PropertyMap>;
527 
528     /** @brief Creates a new ObjectLookupCache for the interface on the bus
529      *         NOTE: The inputs to this object must outlive the object since
530      *         they are only referenced by it.
531      *
532      *  @param[in] bus    - The bus object used for lookups
533      *  @param[in] params - The parameters for the channel
534      *  @param[in] intf   - The interface we are looking up
535      */
536     ObjectLookupCache(sdbusplus::bus::bus& bus, const ChannelParams& params,
537                       const char* intf) :
538         bus(bus),
539         params(params), intf(intf),
540         objs(getAllDbusObjects(bus, params.logicalPath, intf, ""))
541     {
542     }
543 
544     class iterator : public ObjectTree::const_iterator
545     {
546       public:
547         using value_type = PropertiesCache::value_type;
548 
549         iterator(ObjectTree::const_iterator it, ObjectLookupCache& container) :
550             ObjectTree::const_iterator(it), container(container),
551             ret(container.cache.end())
552         {
553         }
554         value_type& operator*()
555         {
556             ret = container.get(ObjectTree::const_iterator::operator*().first);
557             return *ret;
558         }
559         value_type* operator->()
560         {
561             return &operator*();
562         }
563 
564       private:
565         ObjectLookupCache& container;
566         PropertiesCache::iterator ret;
567     };
568 
569     iterator begin() noexcept
570     {
571         return iterator(objs.begin(), *this);
572     }
573 
574     iterator end() noexcept
575     {
576         return iterator(objs.end(), *this);
577     }
578 
579   private:
580     sdbusplus::bus::bus& bus;
581     const ChannelParams& params;
582     const char* const intf;
583     const ObjectTree objs;
584     PropertiesCache cache;
585 
586     /** @brief Gets a cached copy of the object properties if possible
587      *         Otherwise performs a query on DBus to look them up
588      *
589      *  @param[in] path - The object path to lookup
590      *  @return An iterator for the specified object path + properties
591      */
592     PropertiesCache::iterator get(const std::string& path)
593     {
594         auto it = cache.find(path);
595         if (it != cache.end())
596         {
597             return it;
598         }
599         auto properties = getAllDbusProperties(bus, params.service, path, intf);
600         return cache.insert({path, std::move(properties)}).first;
601     }
602 };
603 
604 /** @brief Searches the ip object lookup cache for an address matching
605  *         the input parameters. NOTE: The index lacks stability across address
606  *         changes since the network daemon has no notion of stable indicies.
607  *
608  *  @param[in] bus     - The bus object used for lookups
609  *  @param[in] params  - The parameters for the channel
610  *  @param[in] idx     - The index of the desired address on the interface
611  *  @param[in] origins - The allowed origins for the address objects
612  *  @param[in] ips     - The object lookup cache holding all of the address info
613  *  @return The address and prefix if it was found
614  */
615 template <int family>
616 std::optional<IfAddr<family>>
617     findIfAddr(sdbusplus::bus::bus& bus, const ChannelParams& params,
618                uint8_t idx,
619                const std::unordered_set<IP::AddressOrigin>& origins,
620                ObjectLookupCache& ips)
621 {
622     for (const auto& [path, properties] : ips)
623     {
624         const auto& addrStr = std::get<std::string>(properties.at("Address"));
625         auto addr = maybeStringToAddr<family>(addrStr.c_str());
626         if (!addr)
627         {
628             continue;
629         }
630 
631         IP::AddressOrigin origin = IP::convertAddressOriginFromString(
632             std::get<std::string>(properties.at("Origin")));
633         if (origins.find(origin) == origins.end())
634         {
635             continue;
636         }
637 
638         if (idx > 0)
639         {
640             idx--;
641             continue;
642         }
643 
644         IfAddr<family> ifaddr;
645         ifaddr.path = path;
646         ifaddr.address = *addr;
647         ifaddr.prefix = std::get<uint8_t>(properties.at("PrefixLength"));
648         ifaddr.origin = origin;
649         return std::move(ifaddr);
650     }
651 
652     return std::nullopt;
653 }
654 
655 /** @brief Trivial helper around findIfAddr that simplifies calls
656  *         for one off lookups. Don't use this if you intend to do multiple
657  *         lookups at a time.
658  *
659  *  @param[in] bus     - The bus object used for lookups
660  *  @param[in] params  - The parameters for the channel
661  *  @param[in] idx     - The index of the desired address on the interface
662  *  @param[in] origins - The allowed origins for the address objects
663  *  @return The address and prefix if it was found
664  */
665 template <int family>
666 auto getIfAddr(sdbusplus::bus::bus& bus, const ChannelParams& params,
667                uint8_t idx,
668                const std::unordered_set<IP::AddressOrigin>& origins)
669 {
670     ObjectLookupCache ips(bus, params, INTF_IP);
671     return findIfAddr<family>(bus, params, idx, origins, ips);
672 }
673 
674 /** @brief Deletes the dbus object. Ignores empty objects or objects that are
675  *         missing from the bus.
676  *
677  *  @param[in] bus     - The bus object used for lookups
678  *  @param[in] service - The name of the service
679  *  @param[in] path    - The path of the object to delete
680  */
681 void deleteObjectIfExists(sdbusplus::bus::bus& bus, const std::string& service,
682                           const std::string& path)
683 {
684     if (path.empty())
685     {
686         return;
687     }
688     try
689     {
690         auto req = bus.new_method_call(service.c_str(), path.c_str(),
691                                        ipmi::DELETE_INTERFACE, "Delete");
692         bus.call_noreply(req);
693     }
694     catch (const sdbusplus::exception::SdBusError& e)
695     {
696         if (strcmp(e.name(), "org.freedesktop.DBus.Error.UnknownObject") != 0)
697         {
698             // We want to rethrow real errors
699             throw;
700         }
701     }
702 }
703 
704 /** @brief Sets the address info configured for the interface
705  *         If a previous address path exists then it will be removed
706  *         before the new address is added.
707  *
708  *  @param[in] bus     - The bus object used for lookups
709  *  @param[in] params  - The parameters for the channel
710  *  @param[in] address - The address of the new IP
711  *  @param[in] prefix  - The prefix of the new IP
712  */
713 template <int family>
714 void createIfAddr(sdbusplus::bus::bus& bus, const ChannelParams& params,
715                   const typename AddrFamily<family>::addr& address,
716                   uint8_t prefix)
717 {
718     auto newreq =
719         bus.new_method_call(params.service.c_str(), params.logicalPath.c_str(),
720                             INTF_IP_CREATE, "IP");
721     std::string protocol =
722         sdbusplus::xyz::openbmc_project::Network::server::convertForMessage(
723             AddrFamily<family>::protocol);
724     newreq.append(protocol, addrToString<family>(address), prefix, "");
725     bus.call_noreply(newreq);
726 }
727 
728 /** @brief Trivial helper for getting the IPv4 address from getIfAddrs()
729  *
730  *  @param[in] bus    - The bus object used for lookups
731  *  @param[in] params - The parameters for the channel
732  *  @return The address and prefix if found
733  */
734 auto getIfAddr4(sdbusplus::bus::bus& bus, const ChannelParams& params)
735 {
736     return getIfAddr<AF_INET>(bus, params, 0, originsV4);
737 }
738 
739 /** @brief Reconfigures the IPv4 address info configured for the interface
740  *
741  *  @param[in] bus     - The bus object used for lookups
742  *  @param[in] params  - The parameters for the channel
743  *  @param[in] address - The new address if specified
744  *  @param[in] prefix  - The new address prefix if specified
745  */
746 void reconfigureIfAddr4(sdbusplus::bus::bus& bus, const ChannelParams& params,
747                         const std::optional<in_addr>& address,
748                         std::optional<uint8_t> prefix)
749 {
750     auto ifaddr = getIfAddr4(bus, params);
751     if (!ifaddr && !address)
752     {
753         log<level::ERR>("Missing address for IPv4 assignment");
754         elog<InternalFailure>();
755     }
756     uint8_t fallbackPrefix = AddrFamily<AF_INET>::defaultPrefix;
757     if (ifaddr)
758     {
759         fallbackPrefix = ifaddr->prefix;
760         deleteObjectIfExists(bus, params.service, ifaddr->path);
761     }
762     createIfAddr<AF_INET>(bus, params, address.value_or(ifaddr->address),
763                           prefix.value_or(fallbackPrefix));
764 }
765 
766 template <int family>
767 std::optional<IfNeigh<family>>
768     findStaticNeighbor(sdbusplus::bus::bus& bus, const ChannelParams& params,
769                        const typename AddrFamily<family>::addr& ip,
770                        ObjectLookupCache& neighbors)
771 {
772     const auto state =
773         sdbusplus::xyz::openbmc_project::Network::server::convertForMessage(
774             Neighbor::State::Permanent);
775     for (const auto& [path, neighbor] : neighbors)
776     {
777         const auto& ipStr = std::get<std::string>(neighbor.at("IPAddress"));
778         auto neighIP = maybeStringToAddr<family>(ipStr.c_str());
779         if (!neighIP)
780         {
781             continue;
782         }
783         if (!equal(*neighIP, ip))
784         {
785             continue;
786         }
787         if (state != std::get<std::string>(neighbor.at("State")))
788         {
789             continue;
790         }
791 
792         IfNeigh<family> ret;
793         ret.path = path;
794         ret.ip = ip;
795         const auto& macStr = std::get<std::string>(neighbor.at("MACAddress"));
796         ret.mac = stringToMAC(macStr.c_str());
797         return std::move(ret);
798     }
799 
800     return std::nullopt;
801 }
802 
803 template <int family>
804 void createNeighbor(sdbusplus::bus::bus& bus, const ChannelParams& params,
805                     const typename AddrFamily<family>::addr& address,
806                     const ether_addr& mac)
807 {
808     auto newreq =
809         bus.new_method_call(params.service.c_str(), params.logicalPath.c_str(),
810                             INTF_NEIGHBOR_CREATE_STATIC, "Neighbor");
811     std::string macStr = ether_ntoa(&mac);
812     newreq.append(addrToString<family>(address), macStr);
813     bus.call_noreply(newreq);
814 }
815 
816 /** @brief Sets the system wide value for the default gateway
817  *
818  *  @param[in] bus     - The bus object used for lookups
819  *  @param[in] params  - The parameters for the channel
820  *  @param[in] gateway - Gateway address to apply
821  */
822 template <int family>
823 void setGatewayProperty(sdbusplus::bus::bus& bus, const ChannelParams& params,
824                         const typename AddrFamily<family>::addr& address)
825 {
826     // Save the old gateway MAC address if it exists so we can recreate it
827     auto gateway = getGatewayProperty<family>(bus, params);
828     std::optional<IfNeigh<family>> neighbor;
829     if (gateway)
830     {
831         ObjectLookupCache neighbors(bus, params, INTF_NEIGHBOR);
832         neighbor = findStaticNeighbor<family>(bus, params, *gateway, neighbors);
833     }
834 
835     setDbusProperty(bus, params.service, PATH_SYSTEMCONFIG, INTF_SYSTEMCONFIG,
836                     AddrFamily<family>::propertyGateway,
837                     addrToString<family>(address));
838 
839     // Restore the gateway MAC if we had one
840     if (neighbor)
841     {
842         deleteObjectIfExists(bus, params.service, neighbor->path);
843         createNeighbor<family>(bus, params, address, neighbor->mac);
844     }
845 }
846 
847 template <int family>
848 std::optional<IfNeigh<family>> findGatewayNeighbor(sdbusplus::bus::bus& bus,
849                                                    const ChannelParams& params,
850                                                    ObjectLookupCache& neighbors)
851 {
852     auto gateway = getGatewayProperty<family>(bus, params);
853     if (!gateway)
854     {
855         return std::nullopt;
856     }
857 
858     return findStaticNeighbor<family>(bus, params, *gateway, neighbors);
859 }
860 
861 template <int family>
862 std::optional<IfNeigh<family>> getGatewayNeighbor(sdbusplus::bus::bus& bus,
863                                                   const ChannelParams& params)
864 {
865     ObjectLookupCache neighbors(bus, params, INTF_NEIGHBOR);
866     return findGatewayNeighbor<family>(bus, params, neighbors);
867 }
868 
869 template <int family>
870 void reconfigureGatewayMAC(sdbusplus::bus::bus& bus,
871                            const ChannelParams& params, const ether_addr& mac)
872 {
873     auto gateway = getGatewayProperty<family>(bus, params);
874     if (!gateway)
875     {
876         log<level::ERR>("Tried to set Gateway MAC without Gateway");
877         elog<InternalFailure>();
878     }
879 
880     ObjectLookupCache neighbors(bus, params, INTF_NEIGHBOR);
881     auto neighbor =
882         findStaticNeighbor<family>(bus, params, *gateway, neighbors);
883     if (neighbor)
884     {
885         deleteObjectIfExists(bus, params.service, neighbor->path);
886     }
887 
888     createNeighbor<family>(bus, params, *gateway, mac);
889 }
890 
891 /** @brief Gets the vlan ID configured on the interface
892  *
893  *  @param[in] bus    - The bus object used for lookups
894  *  @param[in] params - The parameters for the channel
895  *  @return VLAN id or the standard 0 for no VLAN
896  */
897 uint16_t getVLANProperty(sdbusplus::bus::bus& bus, const ChannelParams& params)
898 {
899     // VLAN devices will always have a separate logical object
900     if (params.ifPath == params.logicalPath)
901     {
902         return 0;
903     }
904 
905     auto vlan = std::get<uint32_t>(getDbusProperty(
906         bus, params.service, params.logicalPath, INTF_VLAN, "Id"));
907     if ((vlan & VLAN_VALUE_MASK) != vlan)
908     {
909         logWithChannel<level::ERR>(params, "networkd returned an invalid vlan",
910                                    entry("VLAN=%" PRIu32, vlan));
911         elog<InternalFailure>();
912     }
913     return vlan;
914 }
915 
916 /** @brief Deletes all of the possible configuration parameters for a channel
917  *
918  *  @param[in] bus    - The bus object used for lookups
919  *  @param[in] params - The parameters for the channel
920  */
921 void deconfigureChannel(sdbusplus::bus::bus& bus, ChannelParams& params)
922 {
923     // Delete all objects associated with the interface
924     auto objreq = bus.new_method_call(MAPPER_BUS_NAME, MAPPER_OBJ, MAPPER_INTF,
925                                       "GetSubTree");
926     objreq.append(PATH_ROOT, 0, std::vector<std::string>{DELETE_INTERFACE});
927     auto objreply = bus.call(objreq);
928     ObjectTree objs;
929     objreply.read(objs);
930     for (const auto& [path, impls] : objs)
931     {
932         if (path.find(params.ifname) == path.npos)
933         {
934             continue;
935         }
936         for (const auto& [service, intfs] : impls)
937         {
938             deleteObjectIfExists(bus, service, path);
939         }
940         // Update params to reflect the deletion of vlan
941         if (path == params.logicalPath)
942         {
943             params.logicalPath = params.ifPath;
944         }
945     }
946 
947     // Clear out any settings on the lower physical interface
948     setDHCPProperty(bus, params, false);
949 }
950 
951 /** @brief Creates a new VLAN on the specified interface
952  *
953  *  @param[in] bus    - The bus object used for lookups
954  *  @param[in] params - The parameters for the channel
955  *  @param[in] vlan   - The id of the new vlan
956  */
957 void createVLAN(sdbusplus::bus::bus& bus, ChannelParams& params, uint16_t vlan)
958 {
959     if (vlan == 0)
960     {
961         return;
962     }
963 
964     auto req = bus.new_method_call(params.service.c_str(), PATH_ROOT,
965                                    INTF_VLAN_CREATE, "VLAN");
966     req.append(params.ifname, static_cast<uint32_t>(vlan));
967     auto reply = bus.call(req);
968     sdbusplus::message::object_path newPath;
969     reply.read(newPath);
970     params.logicalPath = std::move(newPath);
971 }
972 
973 /** @brief Performs the necessary reconfiguration to change the VLAN
974  *
975  *  @param[in] bus    - The bus object used for lookups
976  *  @param[in] params - The parameters for the channel
977  *  @param[in] vlan   - The new vlan id to use
978  */
979 void reconfigureVLAN(sdbusplus::bus::bus& bus, ChannelParams& params,
980                      uint16_t vlan)
981 {
982     // Unfortunatetly we don't have built-in functions to migrate our interface
983     // customizations to new VLAN interfaces, or have some kind of decoupling.
984     // We therefore must retain all of our old information, setup the new VLAN
985     // configuration, then restore the old info.
986 
987     // Save info from the old logical interface
988     ObjectLookupCache ips(bus, params, INTF_IP);
989     auto ifaddr4 = findIfAddr<AF_INET>(bus, params, 0, originsV4, ips);
990     auto dhcp = getDHCPProperty(bus, params);
991     ObjectLookupCache neighbors(bus, params, INTF_NEIGHBOR);
992     auto neighbor4 = findGatewayNeighbor<AF_INET>(bus, params, neighbors);
993 
994     deconfigureChannel(bus, params);
995     createVLAN(bus, params, vlan);
996 
997     // Re-establish the saved settings
998     setDHCPProperty(bus, params, dhcp);
999     if (ifaddr4)
1000     {
1001         createIfAddr<AF_INET>(bus, params, ifaddr4->address, ifaddr4->prefix);
1002     }
1003     if (neighbor4)
1004     {
1005         createNeighbor<AF_INET>(bus, params, neighbor4->ip, neighbor4->mac);
1006     }
1007 }
1008 
1009 /** @brief Turns a prefix into a netmask
1010  *
1011  *  @param[in] prefix - The prefix length
1012  *  @return The netmask
1013  */
1014 in_addr prefixToNetmask(uint8_t prefix)
1015 {
1016     if (prefix > 32)
1017     {
1018         log<level::ERR>("Invalid prefix", entry("PREFIX=%" PRIu8, prefix));
1019         elog<InternalFailure>();
1020     }
1021     if (prefix == 0)
1022     {
1023         // Avoids 32-bit lshift by 32 UB
1024         return {};
1025     }
1026     return {htobe32(~UINT32_C(0) << (32 - prefix))};
1027 }
1028 
1029 /** @brief Turns a a netmask into a prefix length
1030  *
1031  *  @param[in] netmask - The netmask in byte form
1032  *  @return The prefix length
1033  */
1034 uint8_t netmaskToPrefix(in_addr netmask)
1035 {
1036     uint32_t x = be32toh(netmask.s_addr);
1037     if ((~x & (~x + 1)) != 0)
1038     {
1039         char maskStr[INET_ADDRSTRLEN];
1040         inet_ntop(AF_INET, &netmask, maskStr, sizeof(maskStr));
1041         log<level::ERR>("Invalid netmask", entry("NETMASK=%s", maskStr));
1042         elog<InternalFailure>();
1043     }
1044     return 32 - __builtin_ctz(x);
1045 }
1046 
1047 // We need to store this value so it can be returned to the client
1048 // It is volatile so safe to store in daemon memory.
1049 static std::unordered_map<uint8_t, SetStatus> setStatus;
1050 
1051 // Until we have good support for fixed versions of IPMI tool
1052 // we need to return the VLAN id for disabled VLANs. The value is only
1053 // used for verification that a disable operation succeeded and will only
1054 // be sent if our system indicates that vlans are disabled.
1055 static std::unordered_map<uint8_t, uint16_t> lastDisabledVlan;
1056 
1057 /** @brief Gets the set status for the channel if it exists
1058  *         Otherise populates and returns the default value.
1059  *
1060  *  @param[in] channel - The channel id corresponding to an ethernet interface
1061  *  @return A reference to the SetStatus for the channel
1062  */
1063 SetStatus& getSetStatus(uint8_t channel)
1064 {
1065     auto it = setStatus.find(channel);
1066     if (it != setStatus.end())
1067     {
1068         return it->second;
1069     }
1070     return setStatus[channel] = SetStatus::Complete;
1071 }
1072 
1073 /**
1074  * Define placeholder command handlers for the OEM Extension bytes for the Set
1075  * LAN Configuration Parameters and Get LAN Configuration Parameters
1076  * commands. Using "weak" linking allows the placeholder setLanOem/getLanOem
1077  * functions below to be overridden.
1078  * To create handlers for your own proprietary command set:
1079  *   Create/modify a phosphor-ipmi-host Bitbake append file within your Yocto
1080  *   recipe
1081  *   Create C++ file(s) that define IPMI handler functions matching the
1082  *     function names below (i.e. setLanOem). The default name for the
1083  *     transport IPMI commands is transporthandler_oem.cpp.
1084  *   Add:
1085  *      EXTRA_OECONF_append = " --enable-transport-oem=yes"
1086  *   Create a do_compile_prepend()/do_install_append method in your
1087  *   bbappend file to copy the file to the build directory.
1088  *   Add:
1089  *   PROJECT_SRC_DIR := "${THISDIR}/${PN}"
1090  *   # Copy the "strong" functions into the working directory, overriding the
1091  *   # placeholder functions.
1092  *   do_compile_prepend(){
1093  *      cp -f ${PROJECT_SRC_DIR}/transporthandler_oem.cpp ${S}
1094  *   }
1095  *
1096  *   # Clean up after complilation has completed
1097  *   do_install_append(){
1098  *      rm -f ${S}/transporthandler_oem.cpp
1099  *   }
1100  *
1101  */
1102 
1103 /**
1104  * Define the placeholder OEM commands as having weak linkage. Create
1105  * setLanOem, and getLanOem functions in the transporthandler_oem.cpp
1106  * file. The functions defined there must not have the "weak" attribute
1107  * applied to them.
1108  */
1109 RspType<> setLanOem(uint8_t channel, uint8_t parameter, message::Payload& req)
1110     __attribute__((weak));
1111 RspType<message::Payload> getLanOem(uint8_t channel, uint8_t parameter,
1112                                     uint8_t set, uint8_t block)
1113     __attribute__((weak));
1114 
1115 RspType<> setLanOem(uint8_t channel, uint8_t parameter, message::Payload& req)
1116 {
1117     req.trailingOk = true;
1118     return response(ccParamNotSupported);
1119 }
1120 
1121 RspType<message::Payload> getLanOem(uint8_t channel, uint8_t parameter,
1122                                     uint8_t set, uint8_t block)
1123 {
1124     return response(ccParamNotSupported);
1125 }
1126 
1127 RspType<> setLan(uint4_t channelBits, uint4_t, uint8_t parameter,
1128                  message::Payload& req)
1129 {
1130     auto channel = static_cast<uint8_t>(channelBits);
1131     if (!doesDeviceExist(channel))
1132     {
1133         req.trailingOk = true;
1134         return responseInvalidFieldRequest();
1135     }
1136 
1137     switch (static_cast<LanParam>(parameter))
1138     {
1139         case LanParam::SetStatus:
1140         {
1141             uint2_t flag;
1142             uint6_t rsvd;
1143             if (req.unpack(flag, rsvd) != 0 || !req.fullyUnpacked())
1144             {
1145                 return responseReqDataLenInvalid();
1146             }
1147             auto status = static_cast<SetStatus>(static_cast<uint8_t>(flag));
1148             switch (status)
1149             {
1150                 case SetStatus::Complete:
1151                 {
1152                     getSetStatus(channel) = status;
1153                     return responseSuccess();
1154                 }
1155                 case SetStatus::InProgress:
1156                 {
1157                     auto& storedStatus = getSetStatus(channel);
1158                     if (storedStatus == SetStatus::InProgress)
1159                     {
1160                         return response(ccParamSetLocked);
1161                     }
1162                     storedStatus = status;
1163                     return responseSuccess();
1164                 }
1165                 case SetStatus::Commit:
1166                     if (getSetStatus(channel) != SetStatus::InProgress)
1167                     {
1168                         return responseInvalidFieldRequest();
1169                     }
1170                     return responseSuccess();
1171             }
1172             return response(ccParamNotSupported);
1173         }
1174         case LanParam::AuthSupport:
1175         {
1176             req.trailingOk = true;
1177             return response(ccParamReadOnly);
1178         }
1179         case LanParam::AuthEnables:
1180         {
1181             req.trailingOk = true;
1182             return response(ccParamNotSupported);
1183         }
1184         case LanParam::IP:
1185         {
1186             in_addr ip;
1187             std::array<uint8_t, sizeof(ip)> bytes;
1188             if (req.unpack(bytes) != 0 || !req.fullyUnpacked())
1189             {
1190                 return responseReqDataLenInvalid();
1191             }
1192             copyInto(ip, bytes);
1193             channelCall<reconfigureIfAddr4>(channel, ip, std::nullopt);
1194             return responseSuccess();
1195         }
1196         case LanParam::IPSrc:
1197         {
1198             uint4_t flag;
1199             uint4_t rsvd;
1200             if (req.unpack(flag, rsvd) != 0 || !req.fullyUnpacked())
1201             {
1202                 return responseReqDataLenInvalid();
1203             }
1204             switch (static_cast<IPSrc>(static_cast<uint8_t>(flag)))
1205             {
1206                 case IPSrc::DHCP:
1207                 {
1208                     channelCall<setDHCPProperty>(channel, true);
1209                     return responseSuccess();
1210                 }
1211                 case IPSrc::Unspecified:
1212                 case IPSrc::Static:
1213                 case IPSrc::BIOS:
1214                 case IPSrc::BMC:
1215                 {
1216                     channelCall<setDHCPProperty>(channel, false);
1217                     return responseSuccess();
1218                 }
1219             }
1220             return response(ccParamNotSupported);
1221         }
1222         case LanParam::MAC:
1223         {
1224             ether_addr mac;
1225             std::array<uint8_t, sizeof(mac)> bytes;
1226             if (req.unpack(bytes) != 0 || !req.fullyUnpacked())
1227             {
1228                 return responseReqDataLenInvalid();
1229             }
1230             copyInto(mac, bytes);
1231             channelCall<setMACProperty>(channel, mac);
1232             return responseSuccess();
1233         }
1234         case LanParam::SubnetMask:
1235         {
1236             in_addr netmask;
1237             std::array<uint8_t, sizeof(netmask)> bytes;
1238             if (req.unpack(bytes) != 0 || !req.fullyUnpacked())
1239             {
1240                 return responseReqDataLenInvalid();
1241             }
1242             copyInto(netmask, bytes);
1243             channelCall<reconfigureIfAddr4>(channel, std::nullopt,
1244                                             netmaskToPrefix(netmask));
1245             return responseSuccess();
1246         }
1247         case LanParam::Gateway1:
1248         {
1249             in_addr gateway;
1250             std::array<uint8_t, sizeof(gateway)> bytes;
1251             if (req.unpack(bytes) != 0 || !req.fullyUnpacked())
1252             {
1253                 return responseReqDataLenInvalid();
1254             }
1255             copyInto(gateway, bytes);
1256             channelCall<setGatewayProperty<AF_INET>>(channel, gateway);
1257             return responseSuccess();
1258         }
1259         case LanParam::Gateway1MAC:
1260         {
1261             ether_addr gatewayMAC;
1262             std::array<uint8_t, sizeof(gatewayMAC)> bytes;
1263             if (req.unpack(bytes) != 0 || !req.fullyUnpacked())
1264             {
1265                 return responseReqDataLenInvalid();
1266             }
1267             copyInto(gatewayMAC, bytes);
1268             channelCall<reconfigureGatewayMAC<AF_INET>>(channel, gatewayMAC);
1269             return responseSuccess();
1270         }
1271         case LanParam::VLANId:
1272         {
1273             uint16_t vlanData;
1274             if (req.unpack(vlanData) != 0 || !req.fullyUnpacked())
1275             {
1276                 return responseReqDataLenInvalid();
1277             }
1278             if ((vlanData & VLAN_ENABLE_FLAG) == 0)
1279             {
1280                 lastDisabledVlan[channel] = vlanData & VLAN_VALUE_MASK;
1281                 vlanData = 0;
1282             }
1283             channelCall<reconfigureVLAN>(channel, vlanData & VLAN_VALUE_MASK);
1284             return responseSuccess();
1285         }
1286         case LanParam::CiphersuiteSupport:
1287         case LanParam::CiphersuiteEntries:
1288         {
1289             req.trailingOk = true;
1290             return response(ccParamReadOnly);
1291         }
1292     }
1293 
1294     if ((parameter >= oemCmdStart) && (parameter <= oemCmdEnd))
1295     {
1296         return setLanOem(channel, parameter, req);
1297     }
1298 
1299     req.trailingOk = true;
1300     return response(ccParamNotSupported);
1301 }
1302 
1303 RspType<message::Payload> getLan(uint4_t channelBits, uint3_t, bool revOnly,
1304                                  uint8_t parameter, uint8_t set, uint8_t block)
1305 {
1306     message::Payload ret;
1307     constexpr uint8_t current_revision = 0x11;
1308     ret.pack(current_revision);
1309 
1310     if (revOnly)
1311     {
1312         return responseSuccess(std::move(ret));
1313     }
1314 
1315     auto channel = static_cast<uint8_t>(channelBits);
1316     if (!doesDeviceExist(channel))
1317     {
1318         return responseInvalidFieldRequest();
1319     }
1320 
1321     static std::vector<uint8_t> cipherList;
1322     static bool listInit = false;
1323     if (!listInit)
1324     {
1325         try
1326         {
1327             cipherList = cipher::getCipherList();
1328             listInit = true;
1329         }
1330         catch (const std::exception& e)
1331         {
1332         }
1333     }
1334 
1335     switch (static_cast<LanParam>(parameter))
1336     {
1337         case LanParam::SetStatus:
1338         {
1339             SetStatus status;
1340             try
1341             {
1342                 status = setStatus.at(channel);
1343             }
1344             catch (const std::out_of_range&)
1345             {
1346                 status = SetStatus::Complete;
1347             }
1348             ret.pack(static_cast<uint2_t>(status), uint6_t{});
1349             return responseSuccess(std::move(ret));
1350         }
1351         case LanParam::AuthSupport:
1352         {
1353             std::bitset<6> support;
1354             ret.pack(support, uint2_t{});
1355             return responseSuccess(std::move(ret));
1356         }
1357         case LanParam::AuthEnables:
1358         {
1359             std::bitset<6> enables;
1360             ret.pack(enables, uint2_t{}); // Callback
1361             ret.pack(enables, uint2_t{}); // User
1362             ret.pack(enables, uint2_t{}); // Operator
1363             ret.pack(enables, uint2_t{}); // Admin
1364             ret.pack(enables, uint2_t{}); // OEM
1365             return responseSuccess(std::move(ret));
1366         }
1367         case LanParam::IP:
1368         {
1369             auto ifaddr = channelCall<getIfAddr4>(channel);
1370             in_addr addr{};
1371             if (ifaddr)
1372             {
1373                 addr = ifaddr->address;
1374             }
1375             ret.pack(dataRef(addr));
1376             return responseSuccess(std::move(ret));
1377         }
1378         case LanParam::IPSrc:
1379         {
1380             auto src = IPSrc::Static;
1381             if (channelCall<getDHCPProperty>(channel))
1382             {
1383                 src = IPSrc::DHCP;
1384             }
1385             ret.pack(static_cast<uint4_t>(src), uint4_t{});
1386             return responseSuccess(std::move(ret));
1387         }
1388         case LanParam::MAC:
1389         {
1390             ether_addr mac = channelCall<getMACProperty>(channel);
1391             ret.pack(dataRef(mac));
1392             return responseSuccess(std::move(ret));
1393         }
1394         case LanParam::SubnetMask:
1395         {
1396             auto ifaddr = channelCall<getIfAddr4>(channel);
1397             uint8_t prefix = AddrFamily<AF_INET>::defaultPrefix;
1398             if (ifaddr)
1399             {
1400                 prefix = ifaddr->prefix;
1401             }
1402             in_addr netmask = prefixToNetmask(prefix);
1403             ret.pack(dataRef(netmask));
1404             return responseSuccess(std::move(ret));
1405         }
1406         case LanParam::Gateway1:
1407         {
1408             auto gateway =
1409                 channelCall<getGatewayProperty<AF_INET>>(channel).value_or(
1410                     in_addr{});
1411             ret.pack(dataRef(gateway));
1412             return responseSuccess(std::move(ret));
1413         }
1414         case LanParam::Gateway1MAC:
1415         {
1416             ether_addr mac{};
1417             auto neighbor = channelCall<getGatewayNeighbor<AF_INET>>(channel);
1418             if (neighbor)
1419             {
1420                 mac = neighbor->mac;
1421             }
1422             ret.pack(dataRef(mac));
1423             return responseSuccess(std::move(ret));
1424         }
1425         case LanParam::VLANId:
1426         {
1427             uint16_t vlan = channelCall<getVLANProperty>(channel);
1428             if (vlan != 0)
1429             {
1430                 vlan |= VLAN_ENABLE_FLAG;
1431             }
1432             else
1433             {
1434                 vlan = lastDisabledVlan[channel];
1435             }
1436             ret.pack(vlan);
1437             return responseSuccess(std::move(ret));
1438         }
1439         case LanParam::CiphersuiteSupport:
1440         {
1441             if (!listInit)
1442             {
1443                 return responseUnspecifiedError();
1444             }
1445             ret.pack(static_cast<uint8_t>(cipherList.size() - 1));
1446             return responseSuccess(std::move(ret));
1447         }
1448         case LanParam::CiphersuiteEntries:
1449         {
1450             if (!listInit)
1451             {
1452                 return responseUnspecifiedError();
1453             }
1454             ret.pack(cipherList);
1455             return responseSuccess(std::move(ret));
1456         }
1457     }
1458 
1459     if ((parameter >= oemCmdStart) && (parameter <= oemCmdEnd))
1460     {
1461         return getLanOem(channel, parameter, set, block);
1462     }
1463 
1464     return response(ccParamNotSupported);
1465 }
1466 
1467 } // namespace transport
1468 } // namespace ipmi
1469 
1470 void register_netfn_transport_functions() __attribute__((constructor));
1471 
1472 void register_netfn_transport_functions()
1473 {
1474     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnTransport,
1475                           ipmi::transport::cmdSetLanConfigParameters,
1476                           ipmi::Privilege::Admin, ipmi::transport::setLan);
1477     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnTransport,
1478                           ipmi::transport::cmdGetLanConfigParameters,
1479                           ipmi::Privilege::Admin, ipmi::transport::getLan);
1480 }
1481