1 #include "transporthandler.hpp"
2 
3 #include <ipmid/utils.hpp>
4 #include <phosphor-logging/lg2.hpp>
5 #include <stdplus/net/addr/subnet.hpp>
6 #include <stdplus/raw.hpp>
7 
8 #include <array>
9 #include <fstream>
10 
11 using phosphor::logging::commit;
12 using phosphor::logging::elog;
13 using phosphor::logging::entry;
14 using phosphor::logging::level;
15 using phosphor::logging::log;
16 using sdbusplus::error::xyz::openbmc_project::common::InternalFailure;
17 using sdbusplus::error::xyz::openbmc_project::common::InvalidArgument;
18 using sdbusplus::server::xyz::openbmc_project::network::EthernetInterface;
19 using sdbusplus::server::xyz::openbmc_project::network::IP;
20 using sdbusplus::server::xyz::openbmc_project::network::Neighbor;
21 
22 namespace cipher
23 {
24 
25 std::vector<uint8_t> getCipherList()
26 {
27     std::vector<uint8_t> cipherList;
28 
29     std::ifstream jsonFile(cipher::configFile);
30     if (!jsonFile.is_open())
31     {
32         lg2::error("Channel Cipher suites file not found");
33         elog<InternalFailure>();
34     }
35 
36     auto data = Json::parse(jsonFile, nullptr, false);
37     if (data.is_discarded())
38     {
39         lg2::error("Parsing channel cipher suites JSON failed");
40         elog<InternalFailure>();
41     }
42 
43     // Byte 1 is reserved
44     cipherList.push_back(0x00);
45 
46     for (const auto& record : data)
47     {
48         cipherList.push_back(record.value(cipher, 0));
49     }
50 
51     return cipherList;
52 }
53 } // namespace cipher
54 
55 namespace ipmi
56 {
57 namespace transport
58 {
59 
60 /** @brief Valid address origins for IPv4 */
61 const std::unordered_set<IP::AddressOrigin> originsV4 = {
62     IP::AddressOrigin::Static,
63     IP::AddressOrigin::DHCP,
64 };
65 
66 static constexpr uint8_t oemCmdStart = 192;
67 
68 // Checks if the ifname is part of the networkd path
69 // This assumes the path came from the network subtree PATH_ROOT
70 bool ifnameInPath(std::string_view ifname, std::string_view path)
71 {
72     constexpr auto rs = PATH_ROOT.size() + 1; // ROOT + separator
73     const auto is = rs + ifname.size();       // ROOT + sep + ifname
74     return path.size() > rs && path.substr(rs).starts_with(ifname) &&
75            (path.size() == is || path[is] == '/');
76 }
77 
78 std::optional<ChannelParams>
79     maybeGetChannelParams(sdbusplus::bus_t& bus, uint8_t channel)
80 {
81     auto ifname = getChannelName(channel);
82     if (ifname.empty())
83     {
84         return std::nullopt;
85     }
86 
87     // Enumerate all VLAN + ETHERNET interfaces
88     std::vector<std::string> interfaces = {INTF_VLAN, INTF_ETHERNET};
89     ipmi::ObjectTree objs =
90         ipmi::getSubTree(bus, interfaces, std::string{PATH_ROOT});
91 
92     ChannelParams params;
93     for (const auto& [path, impls] : objs)
94     {
95         if (!ifnameInPath(ifname, path))
96         {
97             continue;
98         }
99         for (const auto& [service, intfs] : impls)
100         {
101             bool vlan = false;
102             bool ethernet = false;
103             for (const auto& intf : intfs)
104             {
105                 if (intf == INTF_VLAN)
106                 {
107                     vlan = true;
108                 }
109                 else if (intf == INTF_ETHERNET)
110                 {
111                     ethernet = true;
112                 }
113             }
114             if (params.service.empty() && (vlan || ethernet))
115             {
116                 params.service = service;
117             }
118             if (params.ifPath.empty() && !vlan && ethernet)
119             {
120                 params.ifPath = path;
121             }
122             if (params.logicalPath.empty() && vlan)
123             {
124                 params.logicalPath = path;
125             }
126         }
127     }
128 
129     // We must have a path for the underlying interface
130     if (params.ifPath.empty())
131     {
132         return std::nullopt;
133     }
134     // We don't have a VLAN so the logical path is the same
135     if (params.logicalPath.empty())
136     {
137         params.logicalPath = params.ifPath;
138     }
139 
140     params.id = channel;
141     params.ifname = std::move(ifname);
142     return params;
143 }
144 
145 ChannelParams getChannelParams(sdbusplus::bus_t& bus, uint8_t channel)
146 {
147     auto params = maybeGetChannelParams(bus, channel);
148     if (!params)
149     {
150         lg2::error("Failed to get channel params: {CHANNEL}", "CHANNEL",
151                    channel);
152         elog<InternalFailure>();
153     }
154     return std::move(*params);
155 }
156 
157 /** @brief Wraps the phosphor logging method to insert some additional metadata
158  *
159  *  @param[in] params - The parameters for the channel
160  *  ...
161  */
162 template <auto level, typename... Args>
163 auto logWithChannel(const ChannelParams& params, Args&&... args)
164 {
165     return log<level>(std::forward<Args>(args)...,
166                       entry("CHANNEL=%d", params.id),
167                       entry("IFNAME=%s", params.ifname.c_str()));
168 }
169 template <auto level, typename... Args>
170 auto logWithChannel(const std::optional<ChannelParams>& params, Args&&... args)
171 {
172     if (params)
173     {
174         return logWithChannel<level>(*params, std::forward<Args>(args)...);
175     }
176     return log<level>(std::forward<Args>(args)...);
177 }
178 
179 /** @brief Get / Set the Property value from phosphor-networkd EthernetInterface
180  */
181 template <typename T>
182 static T getEthProp(sdbusplus::bus_t& bus, const ChannelParams& params,
183                     const std::string& prop)
184 {
185     return std::get<T>(getDbusProperty(bus, params.service, params.logicalPath,
186                                        INTF_ETHERNET, prop));
187 }
188 template <typename T>
189 static void setEthProp(sdbusplus::bus_t& bus, const ChannelParams& params,
190                        const std::string& prop, const T& t)
191 {
192     return setDbusProperty(bus, params.service, params.logicalPath,
193                            INTF_ETHERNET, prop, t);
194 }
195 
196 /** @brief Determines the MAC of the ethernet interface
197  *
198  *  @param[in] bus    - The bus object used for lookups
199  *  @param[in] params - The parameters for the channel
200  *  @return The configured mac address
201  */
202 stdplus::EtherAddr getMACProperty(sdbusplus::bus_t& bus,
203                                   const ChannelParams& params)
204 {
205     auto prop = getDbusProperty(bus, params.service, params.ifPath, INTF_MAC,
206                                 "MACAddress");
207     return stdplus::fromStr<stdplus::EtherAddr>(std::get<std::string>(prop));
208 }
209 
210 /** @brief Sets the system value for MAC address on the given interface
211  *
212  *  @param[in] bus    - The bus object used for lookups
213  *  @param[in] params - The parameters for the channel
214  *  @param[in] mac    - MAC address to apply
215  */
216 void setMACProperty(sdbusplus::bus_t& bus, const ChannelParams& params,
217                     stdplus::EtherAddr mac)
218 {
219     setDbusProperty(bus, params.service, params.ifPath, INTF_MAC, "MACAddress",
220                     stdplus::toStr(mac));
221 }
222 
223 void deleteObjectIfExists(sdbusplus::bus_t& bus, const std::string& service,
224                           const std::string& path)
225 {
226     if (path.empty())
227     {
228         return;
229     }
230     try
231     {
232         auto req = bus.new_method_call(service.c_str(), path.c_str(),
233                                        ipmi::DELETE_INTERFACE, "Delete");
234         bus.call_noreply(req);
235     }
236     catch (const sdbusplus::exception_t& e)
237     {
238         if (strcmp(e.name(),
239                    "xyz.openbmc_project.Common.Error.InternalFailure") != 0 &&
240             strcmp(e.name(), "org.freedesktop.DBus.Error.UnknownObject") != 0)
241         {
242             // We want to rethrow real errors
243             throw;
244         }
245     }
246 }
247 
248 /** @brief Sets the address info configured for the interface
249  *         If a previous address path exists then it will be removed
250  *         before the new address is added.
251  *
252  *  @param[in] bus     - The bus object used for lookups
253  *  @param[in] params  - The parameters for the channel
254  *  @param[in] address - The address of the new IP
255  *  @param[in] prefix  - The prefix of the new IP
256  */
257 template <int family>
258 void createIfAddr(sdbusplus::bus_t& bus, const ChannelParams& params,
259                   typename AddrFamily<family>::addr address, uint8_t prefix)
260 {
261     auto newreq =
262         bus.new_method_call(params.service.c_str(), params.logicalPath.c_str(),
263                             INTF_IP_CREATE, "IP");
264     std::string protocol =
265         sdbusplus::common::xyz::openbmc_project::network::convertForMessage(
266             AddrFamily<family>::protocol);
267     stdplus::ToStrHandle<stdplus::ToStr<typename AddrFamily<family>::addr>> tsh;
268     newreq.append(protocol, tsh(address), prefix, "");
269     bus.call_noreply(newreq);
270 }
271 
272 /** @brief Trivial helper for getting the IPv4 address from getIfAddrs()
273  *
274  *  @param[in] bus    - The bus object used for lookups
275  *  @param[in] params - The parameters for the channel
276  *  @return The address and prefix if found
277  */
278 auto getIfAddr4(sdbusplus::bus_t& bus, const ChannelParams& params)
279 {
280     std::optional<IfAddr<AF_INET>> ifaddr4 = std::nullopt;
281     IP::AddressOrigin src;
282 
283     try
284     {
285         src = std::get<bool>(
286                   getDbusProperty(bus, params.service, params.logicalPath,
287                                   INTF_ETHERNET, "DHCP4"))
288                   ? IP::AddressOrigin::DHCP
289                   : IP::AddressOrigin::Static;
290     }
291     catch (const sdbusplus::exception_t& e)
292     {
293         lg2::error("Failed to get IPv4 source");
294         return ifaddr4;
295     }
296 
297     for (uint8_t i = 0; i < MAX_IPV4_ADDRESSES; ++i)
298     {
299         ifaddr4 = getIfAddr<AF_INET>(bus, params, i, originsV4);
300         if (src == ifaddr4->origin)
301         {
302             break;
303         }
304         else
305         {
306             ifaddr4 = std::nullopt;
307         }
308     }
309     return ifaddr4;
310 }
311 
312 /** @brief Reconfigures the IPv4 address info configured for the interface
313  *
314  *  @param[in] bus     - The bus object used for lookups
315  *  @param[in] params  - The parameters for the channel
316  *  @param[in] address - The new address if specified
317  *  @param[in] prefix  - The new address prefix if specified
318  */
319 void reconfigureIfAddr4(sdbusplus::bus_t& bus, const ChannelParams& params,
320                         std::optional<stdplus::In4Addr> address,
321                         std::optional<uint8_t> prefix)
322 {
323     auto ifaddr = getIfAddr4(bus, params);
324     if (!ifaddr && !address)
325     {
326         lg2::error("Missing address for IPv4 assignment");
327         elog<InternalFailure>();
328     }
329     uint8_t fallbackPrefix = AddrFamily<AF_INET>::defaultPrefix;
330     if (ifaddr)
331     {
332         fallbackPrefix = ifaddr->prefix;
333         deleteObjectIfExists(bus, params.service, ifaddr->path);
334     }
335     auto addr = address.value_or(ifaddr->address);
336     if (addr != stdplus::In4Addr{})
337     {
338         createIfAddr<AF_INET>(bus, params, addr,
339                               prefix.value_or(fallbackPrefix));
340     }
341 }
342 
343 template <int family>
344 std::optional<IfNeigh<family>>
345     findGatewayNeighbor(sdbusplus::bus_t& bus, const ChannelParams& params,
346                         ObjectLookupCache& neighbors)
347 {
348     auto gateway = getGatewayProperty<family>(bus, params);
349     if (!gateway)
350     {
351         return std::nullopt;
352     }
353 
354     return findStaticNeighbor<family>(bus, params, *gateway, neighbors);
355 }
356 
357 template <int family>
358 std::optional<IfNeigh<family>>
359     getGatewayNeighbor(sdbusplus::bus_t& bus, const ChannelParams& params)
360 {
361     ObjectLookupCache neighbors(bus, params, INTF_NEIGHBOR);
362     return findGatewayNeighbor<family>(bus, params, neighbors);
363 }
364 
365 template <int family>
366 void reconfigureGatewayMAC(sdbusplus::bus_t& bus, const ChannelParams& params,
367                            stdplus::EtherAddr mac)
368 {
369     auto gateway = getGatewayProperty<family>(bus, params);
370     if (!gateway)
371     {
372         lg2::error("Tried to set Gateway MAC without Gateway");
373         elog<InternalFailure>();
374     }
375 
376     ObjectLookupCache neighbors(bus, params, INTF_NEIGHBOR);
377     auto neighbor =
378         findStaticNeighbor<family>(bus, params, *gateway, neighbors);
379     if (neighbor)
380     {
381         deleteObjectIfExists(bus, params.service, neighbor->path);
382     }
383 
384     createNeighbor<family>(bus, params, *gateway, mac);
385 }
386 
387 /** @brief Deconfigures the IPv6 address info configured for the interface
388  *
389  *  @param[in] bus     - The bus object used for lookups
390  *  @param[in] params  - The parameters for the channel
391  *  @param[in] idx     - The address index to operate on
392  */
393 void deconfigureIfAddr6(sdbusplus::bus_t& bus, const ChannelParams& params,
394                         uint8_t idx)
395 {
396     auto ifaddr = getIfAddr<AF_INET6>(bus, params, idx, originsV6Static);
397     if (ifaddr)
398     {
399         deleteObjectIfExists(bus, params.service, ifaddr->path);
400     }
401 }
402 
403 /** @brief Reconfigures the IPv6 address info configured for the interface
404  *
405  *  @param[in] bus     - The bus object used for lookups
406  *  @param[in] params  - The parameters for the channel
407  *  @param[in] idx     - The address index to operate on
408  *  @param[in] address - The new address
409  *  @param[in] prefix  - The new address prefix
410  */
411 void reconfigureIfAddr6(sdbusplus::bus_t& bus, const ChannelParams& params,
412                         uint8_t idx, stdplus::In6Addr address, uint8_t prefix)
413 {
414     deconfigureIfAddr6(bus, params, idx);
415     createIfAddr<AF_INET6>(bus, params, address, prefix);
416 }
417 
418 /** @brief Converts the AddressOrigin into an IPv6Source
419  *
420  *  @param[in] origin - The DBus Address Origin to convert
421  *  @return The IPv6Source version of the origin
422  */
423 IPv6Source originToSourceType(IP::AddressOrigin origin)
424 {
425     switch (origin)
426     {
427         case IP::AddressOrigin::Static:
428             return IPv6Source::Static;
429         case IP::AddressOrigin::DHCP:
430             return IPv6Source::DHCP;
431         case IP::AddressOrigin::SLAAC:
432             return IPv6Source::SLAAC;
433         default:
434         {
435             auto originStr = sdbusplus::common::xyz::openbmc_project::network::
436                 convertForMessage(origin);
437             lg2::error("Invalid IP::AddressOrigin conversion to IPv6Source, "
438                        "origin: {ORIGIN}",
439                        "ORIGIN", originStr);
440             elog<InternalFailure>();
441         }
442     }
443 }
444 
445 /** @brief Packs the IPMI message response with IPv6 address data
446  *
447  *  @param[out] ret     - The IPMI response payload to be packed
448  *  @param[in]  channel - The channel id corresponding to an ethernet interface
449  *  @param[in]  set     - The set selector for determining address index
450  *  @param[in]  origins - Set of valid origins for address filtering
451  */
452 void getLanIPv6Address(message::Payload& ret, uint8_t channel, uint8_t set,
453                        const std::unordered_set<IP::AddressOrigin>& origins)
454 {
455     auto source = IPv6Source::Static;
456     bool enabled = false;
457     stdplus::In6Addr addr{};
458     uint8_t prefix{};
459     auto status = IPv6AddressStatus::Disabled;
460 
461     auto ifaddr = channelCall<getIfAddr<AF_INET6>>(channel, set, origins);
462     if (ifaddr)
463     {
464         source = originToSourceType(ifaddr->origin);
465         enabled = (origins == originsV6Static);
466         addr = ifaddr->address;
467         prefix = ifaddr->prefix;
468         status = IPv6AddressStatus::Active;
469     }
470 
471     ret.pack(set);
472     ret.pack(types::enum_cast<uint4_t>(source), uint3_t{}, enabled);
473     ret.pack(stdplus::raw::asView<char>(addr));
474     ret.pack(prefix);
475     ret.pack(types::enum_cast<uint8_t>(status));
476 }
477 
478 /** @brief Gets the vlan ID configured on the interface
479  *
480  *  @param[in] bus    - The bus object used for lookups
481  *  @param[in] params - The parameters for the channel
482  *  @return VLAN id or the standard 0 for no VLAN
483  */
484 uint16_t getVLANProperty(sdbusplus::bus_t& bus, const ChannelParams& params)
485 {
486     // VLAN devices will always have a separate logical object
487     if (params.ifPath == params.logicalPath)
488     {
489         return 0;
490     }
491 
492     auto vlan = std::get<uint32_t>(getDbusProperty(
493         bus, params.service, params.logicalPath, INTF_VLAN, "Id"));
494     if ((vlan & VLAN_VALUE_MASK) != vlan)
495     {
496         logWithChannel<level::ERR>(params, "networkd returned an invalid vlan",
497                                    entry("VLAN=%" PRIu32, vlan));
498         elog<InternalFailure>();
499     }
500     return vlan;
501 }
502 
503 /** @brief Deletes all of the possible configuration parameters for a channel
504  *
505  *  @param[in] bus    - The bus object used for lookups
506  *  @param[in] params - The parameters for the channel
507  */
508 void deconfigureChannel(sdbusplus::bus_t& bus, ChannelParams& params)
509 {
510     // Delete all objects associated with the interface
511     ObjectTree objs =
512         ipmi::getSubTree(bus, std::vector<std::string>{DELETE_INTERFACE},
513                          std::string{PATH_ROOT});
514     for (const auto& [path, impls] : objs)
515     {
516         if (!ifnameInPath(params.ifname, path))
517         {
518             continue;
519         }
520         for (const auto& [service, intfs] : impls)
521         {
522             deleteObjectIfExists(bus, service, path);
523         }
524         // Update params to reflect the deletion of vlan
525         if (path == params.logicalPath)
526         {
527             params.logicalPath = params.ifPath;
528         }
529     }
530 
531     // Clear out any settings on the lower physical interface
532     setEthProp(bus, params, "DHCP4", false);
533     setEthProp(bus, params, "DHCP6", false);
534     setEthProp(bus, params, "IPv6AcceptRA", false);
535 }
536 
537 /** @brief Creates a new VLAN on the specified interface
538  *
539  *  @param[in] bus    - The bus object used for lookups
540  *  @param[in] params - The parameters for the channel
541  *  @param[in] vlan   - The id of the new vlan
542  */
543 void createVLAN(sdbusplus::bus_t& bus, ChannelParams& params, uint16_t vlan)
544 {
545     if (vlan == 0)
546     {
547         return;
548     }
549 
550     auto req = bus.new_method_call(params.service.c_str(), PATH_ROOT.c_str(),
551                                    INTF_VLAN_CREATE, "VLAN");
552     req.append(params.ifname, static_cast<uint32_t>(vlan));
553     auto reply = bus.call(req);
554     sdbusplus::message::object_path newPath;
555     reply.read(newPath);
556     params.logicalPath = std::move(newPath);
557 }
558 
559 /** @brief Performs the necessary reconfiguration to change the VLAN
560  *
561  *  @param[in] bus    - The bus object used for lookups
562  *  @param[in] params - The parameters for the channel
563  *  @param[in] vlan   - The new vlan id to use
564  */
565 void reconfigureVLAN(sdbusplus::bus_t& bus, ChannelParams& params,
566                      uint16_t vlan)
567 {
568     // Unfortunatetly we don't have built-in functions to migrate our interface
569     // customizations to new VLAN interfaces, or have some kind of decoupling.
570     // We therefore must retain all of our old information, setup the new VLAN
571     // configuration, then restore the old info.
572 
573     // Save info from the old logical interface
574     bool dhcp4 = getEthProp<bool>(bus, params, "DHCP4");
575     bool dhcp6 = getEthProp<bool>(bus, params, "DHCP6");
576     bool ra = getEthProp<bool>(bus, params, "IPv6AcceptRA");
577     ObjectLookupCache ips(bus, params, INTF_IP);
578     auto ifaddr4 = findIfAddr<AF_INET>(bus, params, 0, originsV4, ips);
579     std::vector<IfAddr<AF_INET6>> ifaddrs6;
580     for (uint8_t i = 0; i < MAX_IPV6_STATIC_ADDRESSES; ++i)
581     {
582         auto ifaddr6 =
583             findIfAddr<AF_INET6>(bus, params, i, originsV6Static, ips);
584         if (!ifaddr6)
585         {
586             break;
587         }
588         ifaddrs6.push_back(std::move(*ifaddr6));
589     }
590     ObjectLookupCache neighbors(bus, params, INTF_NEIGHBOR);
591     auto neighbor4 = findGatewayNeighbor<AF_INET>(bus, params, neighbors);
592     auto neighbor6 = findGatewayNeighbor<AF_INET6>(bus, params, neighbors);
593 
594     deconfigureChannel(bus, params);
595     createVLAN(bus, params, vlan);
596 
597     // Re-establish the saved settings
598     setEthProp(bus, params, "DHCP4", dhcp4);
599     setEthProp(bus, params, "DHCP6", dhcp6);
600     setEthProp(bus, params, "IPv6AcceptRA", ra);
601     if (ifaddr4)
602     {
603         createIfAddr<AF_INET>(bus, params, ifaddr4->address, ifaddr4->prefix);
604     }
605     for (const auto& ifaddr6 : ifaddrs6)
606     {
607         createIfAddr<AF_INET6>(bus, params, ifaddr6.address, ifaddr6.prefix);
608     }
609     if (neighbor4)
610     {
611         createNeighbor<AF_INET>(bus, params, neighbor4->ip, neighbor4->mac);
612     }
613     if (neighbor6)
614     {
615         createNeighbor<AF_INET6>(bus, params, neighbor6->ip, neighbor6->mac);
616     }
617 }
618 
619 // We need to store this value so it can be returned to the client
620 // It is volatile so safe to store in daemon memory.
621 static std::unordered_map<uint8_t, SetStatus> setStatus;
622 
623 // Until we have good support for fixed versions of IPMI tool
624 // we need to return the VLAN id for disabled VLANs. The value is only
625 // used for verification that a disable operation succeeded and will only
626 // be sent if our system indicates that vlans are disabled.
627 static std::unordered_map<uint8_t, uint16_t> lastDisabledVlan;
628 
629 /** @brief Gets the set status for the channel if it exists
630  *         Otherise populates and returns the default value.
631  *
632  *  @param[in] channel - The channel id corresponding to an ethernet interface
633  *  @return A reference to the SetStatus for the channel
634  */
635 SetStatus& getSetStatus(uint8_t channel)
636 {
637     auto it = setStatus.find(channel);
638     if (it != setStatus.end())
639     {
640         return it->second;
641     }
642     return setStatus[channel] = SetStatus::Complete;
643 }
644 
645 /** @brief Unpacks the trivially copyable type from the message */
646 template <typename T>
647 static T unpackT(message::Payload& req)
648 {
649     std::array<uint8_t, sizeof(T)> bytes;
650     if (req.unpack(bytes) != 0)
651     {
652         throw ccReqDataLenInvalid;
653     }
654     return stdplus::raw::copyFrom<T>(bytes);
655 }
656 
657 /** @brief Ensure the message is fully unpacked */
658 static void unpackFinal(message::Payload& req)
659 {
660     if (!req.fullyUnpacked())
661     {
662         throw ccReqDataTruncated;
663     }
664 }
665 
666 /**
667  * Define placeholder command handlers for the OEM Extension bytes for the Set
668  * LAN Configuration Parameters and Get LAN Configuration Parameters
669  * commands. Using "weak" linking allows the placeholder setLanOem/getLanOem
670  * functions below to be overridden.
671  * To create handlers for your own proprietary command set:
672  *   Create/modify a phosphor-ipmi-host Bitbake append file within your Yocto
673  *   recipe
674  *   Create C++ file(s) that define IPMI handler functions matching the
675  *     function names below (i.e. setLanOem). The default name for the
676  *     transport IPMI commands is transporthandler_oem.cpp.
677  *   Add:
678  *      EXTRA_OEMESON:append = "-Dtransport-oem=enabled"
679  *   Create a do_configure:prepend()/do_install:append() method in your
680  *   bbappend file to copy the file to the build directory.
681  *   Add:
682  *   PROJECT_SRC_DIR := "${THISDIR}/${PN}"
683  *   # Copy the "strong" functions into the working directory, overriding the
684  *   # placeholder functions.
685  *   do_configure:prepend(){
686  *      cp -f ${PROJECT_SRC_DIR}/transporthandler_oem.cpp ${S}
687  *   }
688  *
689  *   # Clean up after complilation has completed
690  *   do_install:append(){
691  *      rm -f ${S}/transporthandler_oem.cpp
692  *   }
693  *
694  */
695 
696 /**
697  * Define the placeholder OEM commands as having weak linkage. Create
698  * setLanOem, and getLanOem functions in the transporthandler_oem.cpp
699  * file. The functions defined there must not have the "weak" attribute
700  * applied to them.
701  */
702 RspType<> setLanOem(uint8_t channel, uint8_t parameter, message::Payload& req)
703     __attribute__((weak));
704 RspType<message::Payload>
705     getLanOem(uint8_t channel, uint8_t parameter, uint8_t set, uint8_t block)
706         __attribute__((weak));
707 
708 RspType<> setLanOem(uint8_t, uint8_t, message::Payload& req)
709 {
710     req.trailingOk = true;
711     return response(ccParamNotSupported);
712 }
713 
714 RspType<message::Payload> getLanOem(uint8_t, uint8_t, uint8_t, uint8_t)
715 {
716     return response(ccParamNotSupported);
717 }
718 
719 /**
720  * @brief is a valid LAN channel.
721  *
722  * This function checks whether the input channel is a valid LAN channel or not.
723  *
724  * @param[in] channel: the channel number.
725  * @return nullopt if the channel is invalid, false if the channel is not a LAN
726  * channel, true if the channel is a LAN channel.
727  **/
728 std::optional<bool> isLanChannel(uint8_t channel)
729 {
730     ChannelInfo chInfo;
731     auto cc = getChannelInfo(channel, chInfo);
732     if (cc != ccSuccess)
733     {
734         return std::nullopt;
735     }
736 
737     return chInfo.mediumType ==
738            static_cast<uint8_t>(EChannelMediumType::lan8032);
739 }
740 
741 RspType<> setLanInt(Context::ptr ctx, uint4_t channelBits, uint4_t reserved1,
742                     uint8_t parameter, message::Payload& req)
743 {
744     const uint8_t channel = convertCurrentChannelNum(
745         static_cast<uint8_t>(channelBits), ctx->channel);
746     if (reserved1 || !isValidChannel(channel))
747     {
748         lg2::error("Set Lan - Invalid field in request");
749         req.trailingOk = true;
750         return responseInvalidFieldRequest();
751     }
752 
753     if (!isLanChannel(channel).value_or(false))
754     {
755         lg2::error("Set Lan - Not a LAN channel");
756         return responseInvalidFieldRequest();
757     }
758 
759     switch (static_cast<LanParam>(parameter))
760     {
761         case LanParam::SetStatus:
762         {
763             uint2_t flag;
764             uint6_t rsvd;
765             if (req.unpack(flag, rsvd) != 0)
766             {
767                 return responseReqDataLenInvalid();
768             }
769             unpackFinal(req);
770             if (rsvd)
771             {
772                 return responseInvalidFieldRequest();
773             }
774             auto status = static_cast<SetStatus>(static_cast<uint8_t>(flag));
775             switch (status)
776             {
777                 case SetStatus::Complete:
778                 {
779                     getSetStatus(channel) = status;
780                     return responseSuccess();
781                 }
782                 case SetStatus::InProgress:
783                 {
784                     auto& storedStatus = getSetStatus(channel);
785                     if (storedStatus == SetStatus::InProgress)
786                     {
787                         return response(ccParamSetLocked);
788                     }
789                     storedStatus = status;
790                     return responseSuccess();
791                 }
792                 case SetStatus::Commit:
793                     if (getSetStatus(channel) != SetStatus::InProgress)
794                     {
795                         return responseInvalidFieldRequest();
796                     }
797                     return responseSuccess();
798             }
799             return response(ccParamNotSupported);
800         }
801         case LanParam::AuthSupport:
802         {
803             req.trailingOk = true;
804             return response(ccParamReadOnly);
805         }
806         case LanParam::AuthEnables:
807         {
808             req.trailingOk = true;
809             return response(ccParamReadOnly);
810         }
811         case LanParam::IP:
812         {
813             if (channelCall<getEthProp<bool>>(channel, "DHCP4"))
814             {
815                 return responseCommandNotAvailable();
816             }
817             auto ip = unpackT<stdplus::In4Addr>(req);
818             unpackFinal(req);
819             channelCall<reconfigureIfAddr4>(channel, ip, std::nullopt);
820             return responseSuccess();
821         }
822         case LanParam::IPSrc:
823         {
824             uint4_t flag;
825             uint4_t rsvd;
826             if (req.unpack(flag, rsvd) != 0)
827             {
828                 return responseReqDataLenInvalid();
829             }
830             unpackFinal(req);
831             if (rsvd)
832             {
833                 return responseInvalidFieldRequest();
834             }
835             switch (static_cast<IPSrc>(static_cast<uint8_t>(flag)))
836             {
837                 case IPSrc::DHCP:
838                     // The IPSrc IPMI command is only for IPv4
839                     // management. Modifying IPv6 state is done using
840                     // a completely different Set LAN Configuration
841                     // subcommand.
842                     channelCall<setEthProp<bool>>(channel, "DHCP4", true);
843                     return responseSuccess();
844                 case IPSrc::Unspecified:
845                 case IPSrc::Static:
846                     channelCall<setEthProp<bool>>(channel, "DHCP4", false);
847                     return responseSuccess();
848                 case IPSrc::BIOS:
849                 case IPSrc::BMC:
850                     return responseInvalidFieldRequest();
851             }
852             return response(ccParamNotSupported);
853         }
854         case LanParam::MAC:
855         {
856             auto mac = unpackT<stdplus::EtherAddr>(req);
857             unpackFinal(req);
858             channelCall<setMACProperty>(channel, mac);
859             return responseSuccess();
860         }
861         case LanParam::SubnetMask:
862         {
863             if (channelCall<getEthProp<bool>>(channel, "DHCP4"))
864             {
865                 return responseCommandNotAvailable();
866             }
867             auto pfx = stdplus::maskToPfx(unpackT<stdplus::In4Addr>(req));
868             unpackFinal(req);
869             channelCall<reconfigureIfAddr4>(channel, std::nullopt, pfx);
870             return responseSuccess();
871         }
872         case LanParam::Gateway1:
873         {
874             if (channelCall<getEthProp<bool>>(channel, "DHCP4"))
875             {
876                 return responseCommandNotAvailable();
877             }
878             auto gateway = unpackT<stdplus::In4Addr>(req);
879             unpackFinal(req);
880             channelCall<setGatewayProperty<AF_INET>>(channel, gateway);
881             return responseSuccess();
882         }
883         case LanParam::Gateway1MAC:
884         {
885             auto gatewayMAC = unpackT<stdplus::EtherAddr>(req);
886             unpackFinal(req);
887             channelCall<reconfigureGatewayMAC<AF_INET>>(channel, gatewayMAC);
888             return responseSuccess();
889         }
890         case LanParam::VLANId:
891         {
892             uint12_t vlanData;
893             uint3_t rsvd;
894             bool vlanEnable;
895 
896             if (req.unpack(vlanData, rsvd, vlanEnable) != 0)
897             {
898                 return responseReqDataLenInvalid();
899             }
900             unpackFinal(req);
901 
902             if (rsvd)
903             {
904                 return responseInvalidFieldRequest();
905             }
906 
907             uint16_t vlan = static_cast<uint16_t>(vlanData);
908 
909             if (!vlanEnable)
910             {
911                 lastDisabledVlan[channel] = vlan;
912                 vlan = 0;
913             }
914             else if (vlan == 0 || vlan == VLAN_VALUE_MASK)
915             {
916                 return responseInvalidFieldRequest();
917             }
918 
919             channelCall<reconfigureVLAN>(channel, vlan);
920             return responseSuccess();
921         }
922         case LanParam::CiphersuiteSupport:
923         case LanParam::CiphersuiteEntries:
924         case LanParam::IPFamilySupport:
925         {
926             req.trailingOk = true;
927             return response(ccParamReadOnly);
928         }
929         case LanParam::IPFamilyEnables:
930         {
931             uint8_t enables;
932             if (req.unpack(enables) != 0)
933             {
934                 return responseReqDataLenInvalid();
935             }
936             unpackFinal(req);
937             switch (static_cast<IPFamilyEnables>(enables))
938             {
939                 case IPFamilyEnables::DualStack:
940                     return responseSuccess();
941                 case IPFamilyEnables::IPv4Only:
942                 case IPFamilyEnables::IPv6Only:
943                     return response(ccParamNotSupported);
944             }
945             return response(ccParamNotSupported);
946         }
947         case LanParam::IPv6Status:
948         {
949             req.trailingOk = true;
950             return response(ccParamReadOnly);
951         }
952         case LanParam::IPv6StaticAddresses:
953         {
954             uint8_t set;
955             uint7_t rsvd;
956             bool enabled;
957             uint8_t prefix;
958             uint8_t status;
959             if (req.unpack(set, rsvd, enabled) != 0)
960             {
961                 return responseReqDataLenInvalid();
962             }
963             auto ip = unpackT<stdplus::In6Addr>(req);
964             if (req.unpack(prefix, status) != 0)
965             {
966                 return responseReqDataLenInvalid();
967             }
968             unpackFinal(req);
969             if (rsvd)
970             {
971                 return responseInvalidFieldRequest();
972             }
973             if (enabled)
974             {
975                 if (prefix < MIN_IPV6_PREFIX_LENGTH ||
976                     prefix > MAX_IPV6_PREFIX_LENGTH)
977                 {
978                     return responseParmOutOfRange();
979                 }
980                 channelCall<reconfigureIfAddr6>(channel, set, ip, prefix);
981             }
982             else
983             {
984                 channelCall<deconfigureIfAddr6>(channel, set);
985             }
986             return responseSuccess();
987         }
988         case LanParam::IPv6DynamicAddresses:
989         {
990             req.trailingOk = true;
991             return response(ccParamReadOnly);
992         }
993         case LanParam::IPv6RouterControl:
994         {
995             std::bitset<8> control;
996             constexpr uint8_t reservedRACCBits = 0xfc;
997             if (req.unpack(control) != 0)
998             {
999                 return responseReqDataLenInvalid();
1000             }
1001             unpackFinal(req);
1002             if (std::bitset<8> expected(
1003                     control & std::bitset<8>(reservedRACCBits));
1004                 expected.any())
1005             {
1006                 return response(ccParamNotSupported);
1007             }
1008 
1009             bool enableRA = control[IPv6RouterControlFlag::Dynamic];
1010             channelCall<setEthProp<bool>>(channel, "IPv6AcceptRA", enableRA);
1011             channelCall<setEthProp<bool>>(channel, "DHCP6", enableRA);
1012             return responseSuccess();
1013         }
1014         case LanParam::IPv6StaticRouter1IP:
1015         {
1016             auto gateway = unpackT<stdplus::In6Addr>(req);
1017             unpackFinal(req);
1018             channelCall<setGatewayProperty<AF_INET6>>(channel, gateway);
1019             return responseSuccess();
1020         }
1021         case LanParam::IPv6StaticRouter1MAC:
1022         {
1023             auto mac = unpackT<stdplus::EtherAddr>(req);
1024             unpackFinal(req);
1025             channelCall<reconfigureGatewayMAC<AF_INET6>>(channel, mac);
1026             return responseSuccess();
1027         }
1028         case LanParam::IPv6StaticRouter1PrefixLength:
1029         {
1030             uint8_t prefix;
1031             if (req.unpack(prefix) != 0)
1032             {
1033                 return responseReqDataLenInvalid();
1034             }
1035             unpackFinal(req);
1036             if (prefix != 0)
1037             {
1038                 return responseInvalidFieldRequest();
1039             }
1040             return responseSuccess();
1041         }
1042         case LanParam::IPv6StaticRouter1PrefixValue:
1043         {
1044             unpackT<stdplus::In6Addr>(req);
1045             unpackFinal(req);
1046             // Accept any prefix value since our prefix length has to be 0
1047             return responseSuccess();
1048         }
1049         case LanParam::cipherSuitePrivilegeLevels:
1050         {
1051             uint8_t rsvd;
1052             std::array<uint4_t, ipmi::maxCSRecords> cipherSuitePrivs;
1053 
1054             if (req.unpack(rsvd, cipherSuitePrivs))
1055             {
1056                 return responseReqDataLenInvalid();
1057             }
1058             unpackFinal(req);
1059 
1060             if (rsvd)
1061             {
1062                 return responseInvalidFieldRequest();
1063             }
1064 
1065             uint8_t resp =
1066                 getCipherConfigObject(csPrivFileName, csPrivDefaultFileName)
1067                     .setCSPrivilegeLevels(channel, cipherSuitePrivs);
1068             if (!resp)
1069             {
1070                 return responseSuccess();
1071             }
1072             else
1073             {
1074                 req.trailingOk = true;
1075                 return response(resp);
1076             }
1077         }
1078     }
1079 
1080     if (parameter >= oemCmdStart)
1081     {
1082         return setLanOem(channel, parameter, req);
1083     }
1084 
1085     req.trailingOk = true;
1086     return response(ccParamNotSupported);
1087 }
1088 
1089 RspType<> setLan(Context::ptr ctx, uint4_t channelBits, uint4_t reserved1,
1090                  uint8_t parameter, message::Payload& req)
1091 {
1092     try
1093     {
1094         return setLanInt(ctx, channelBits, reserved1, parameter, req);
1095     }
1096     catch (ipmi::Cc cc)
1097     {
1098         return response(cc);
1099     }
1100     catch (const sdbusplus::exception_t& e)
1101     {
1102         if (std::string_view{InvalidArgument::errName} == e.name())
1103         {
1104             return responseInvalidFieldRequest();
1105         }
1106         throw;
1107     }
1108 }
1109 
1110 RspType<message::Payload> getLan(Context::ptr ctx, uint4_t channelBits,
1111                                  uint3_t reserved, bool revOnly,
1112                                  uint8_t parameter, uint8_t set, uint8_t block)
1113 {
1114     message::Payload ret;
1115     constexpr uint8_t current_revision = 0x11;
1116     ret.pack(current_revision);
1117 
1118     if (revOnly)
1119     {
1120         return responseSuccess(std::move(ret));
1121     }
1122 
1123     const uint8_t channel = convertCurrentChannelNum(
1124         static_cast<uint8_t>(channelBits), ctx->channel);
1125     if (reserved || !isValidChannel(channel))
1126     {
1127         lg2::error("Get Lan - Invalid field in request");
1128         return responseInvalidFieldRequest();
1129     }
1130 
1131     if (!isLanChannel(channel).value_or(false))
1132     {
1133         lg2::error("Set Lan - Not a LAN channel");
1134         return responseInvalidFieldRequest();
1135     }
1136 
1137     static std::vector<uint8_t> cipherList;
1138     static bool listInit = false;
1139     if (!listInit)
1140     {
1141         try
1142         {
1143             cipherList = cipher::getCipherList();
1144             listInit = true;
1145         }
1146         catch (const std::exception& e)
1147         {}
1148     }
1149 
1150     switch (static_cast<LanParam>(parameter))
1151     {
1152         case LanParam::SetStatus:
1153         {
1154             SetStatus status;
1155             try
1156             {
1157                 status = setStatus.at(channel);
1158             }
1159             catch (const std::out_of_range&)
1160             {
1161                 status = SetStatus::Complete;
1162             }
1163             ret.pack(types::enum_cast<uint2_t>(status), uint6_t{});
1164             return responseSuccess(std::move(ret));
1165         }
1166         case LanParam::AuthSupport:
1167         {
1168             std::bitset<6> support;
1169             ret.pack(support, uint2_t{});
1170             return responseSuccess(std::move(ret));
1171         }
1172         case LanParam::AuthEnables:
1173         {
1174             std::bitset<6> enables;
1175             ret.pack(enables, uint2_t{}); // Callback
1176             ret.pack(enables, uint2_t{}); // User
1177             ret.pack(enables, uint2_t{}); // Operator
1178             ret.pack(enables, uint2_t{}); // Admin
1179             ret.pack(enables, uint2_t{}); // OEM
1180             return responseSuccess(std::move(ret));
1181         }
1182         case LanParam::IP:
1183         {
1184             auto ifaddr = channelCall<getIfAddr4>(channel);
1185             stdplus::In4Addr addr{};
1186             if (ifaddr)
1187             {
1188                 addr = ifaddr->address;
1189             }
1190             ret.pack(stdplus::raw::asView<char>(addr));
1191             return responseSuccess(std::move(ret));
1192         }
1193         case LanParam::IPSrc:
1194         {
1195             auto src = channelCall<getEthProp<bool>>(channel, "DHCP4")
1196                            ? IPSrc::DHCP
1197                            : IPSrc::Static;
1198             ret.pack(types::enum_cast<uint4_t>(src), uint4_t{});
1199             return responseSuccess(std::move(ret));
1200         }
1201         case LanParam::MAC:
1202         {
1203             auto mac = channelCall<getMACProperty>(channel);
1204             ret.pack(stdplus::raw::asView<char>(mac));
1205             return responseSuccess(std::move(ret));
1206         }
1207         case LanParam::SubnetMask:
1208         {
1209             auto ifaddr = channelCall<getIfAddr4>(channel);
1210             uint8_t prefix = AddrFamily<AF_INET>::defaultPrefix;
1211             if (ifaddr)
1212             {
1213                 prefix = ifaddr->prefix;
1214             }
1215             auto netmask = stdplus::pfxToMask<stdplus::In4Addr>(prefix);
1216             ret.pack(stdplus::raw::asView<char>(netmask));
1217             return responseSuccess(std::move(ret));
1218         }
1219         case LanParam::Gateway1:
1220         {
1221             auto gateway = channelCall<getGatewayProperty<AF_INET>>(channel);
1222             ret.pack(stdplus::raw::asView<char>(
1223                 gateway.value_or(stdplus::In4Addr{})));
1224             return responseSuccess(std::move(ret));
1225         }
1226         case LanParam::Gateway1MAC:
1227         {
1228             stdplus::EtherAddr mac{};
1229             auto neighbor = channelCall<getGatewayNeighbor<AF_INET>>(channel);
1230             if (neighbor)
1231             {
1232                 mac = neighbor->mac;
1233             }
1234             ret.pack(stdplus::raw::asView<char>(mac));
1235             return responseSuccess(std::move(ret));
1236         }
1237         case LanParam::VLANId:
1238         {
1239             uint16_t vlan = channelCall<getVLANProperty>(channel);
1240             if (vlan != 0)
1241             {
1242                 vlan |= VLAN_ENABLE_FLAG;
1243             }
1244             else
1245             {
1246                 vlan = lastDisabledVlan[channel];
1247             }
1248             ret.pack(vlan);
1249             return responseSuccess(std::move(ret));
1250         }
1251         case LanParam::CiphersuiteSupport:
1252         {
1253             if (getChannelSessionSupport(channel) ==
1254                 EChannelSessSupported::none)
1255             {
1256                 return responseInvalidFieldRequest();
1257             }
1258             if (!listInit)
1259             {
1260                 return responseUnspecifiedError();
1261             }
1262             ret.pack(static_cast<uint8_t>(cipherList.size() - 1));
1263             return responseSuccess(std::move(ret));
1264         }
1265         case LanParam::CiphersuiteEntries:
1266         {
1267             if (getChannelSessionSupport(channel) ==
1268                 EChannelSessSupported::none)
1269             {
1270                 return responseInvalidFieldRequest();
1271             }
1272             if (!listInit)
1273             {
1274                 return responseUnspecifiedError();
1275             }
1276             ret.pack(cipherList);
1277             return responseSuccess(std::move(ret));
1278         }
1279         case LanParam::IPFamilySupport:
1280         {
1281             std::bitset<8> support;
1282             support[IPFamilySupportFlag::IPv6Only] = 0;
1283             support[IPFamilySupportFlag::DualStack] = 1;
1284             support[IPFamilySupportFlag::IPv6Alerts] = 1;
1285             ret.pack(support);
1286             return responseSuccess(std::move(ret));
1287         }
1288         case LanParam::IPFamilyEnables:
1289         {
1290             ret.pack(static_cast<uint8_t>(IPFamilyEnables::DualStack));
1291             return responseSuccess(std::move(ret));
1292         }
1293         case LanParam::IPv6Status:
1294         {
1295             ret.pack(MAX_IPV6_STATIC_ADDRESSES);
1296             ret.pack(MAX_IPV6_DYNAMIC_ADDRESSES);
1297             std::bitset<8> support;
1298             support[IPv6StatusFlag::DHCP] = 1;
1299             support[IPv6StatusFlag::SLAAC] = 1;
1300             ret.pack(support);
1301             return responseSuccess(std::move(ret));
1302         }
1303         case LanParam::IPv6StaticAddresses:
1304         {
1305             if (set >= MAX_IPV6_STATIC_ADDRESSES)
1306             {
1307                 return responseParmOutOfRange();
1308             }
1309             getLanIPv6Address(ret, channel, set, originsV6Static);
1310             return responseSuccess(std::move(ret));
1311         }
1312         case LanParam::IPv6DynamicAddresses:
1313         {
1314             if (set >= MAX_IPV6_DYNAMIC_ADDRESSES)
1315             {
1316                 return responseParmOutOfRange();
1317             }
1318             getLanIPv6Address(ret, channel, set, originsV6Dynamic);
1319             return responseSuccess(std::move(ret));
1320         }
1321         case LanParam::IPv6RouterControl:
1322         {
1323             std::bitset<8> control;
1324             control[IPv6RouterControlFlag::Dynamic] =
1325                 channelCall<getEthProp<bool>>(channel, "IPv6AcceptRA");
1326             control[IPv6RouterControlFlag::Static] = 1;
1327             ret.pack(control);
1328             return responseSuccess(std::move(ret));
1329         }
1330         case LanParam::IPv6StaticRouter1IP:
1331         {
1332             stdplus::In6Addr gateway{};
1333             if (!channelCall<getEthProp<bool>>(channel, "IPv6AcceptRA"))
1334             {
1335                 gateway =
1336                     channelCall<getGatewayProperty<AF_INET6>>(channel).value_or(
1337                         stdplus::In6Addr{});
1338             }
1339             ret.pack(stdplus::raw::asView<char>(gateway));
1340             return responseSuccess(std::move(ret));
1341         }
1342         case LanParam::IPv6StaticRouter1MAC:
1343         {
1344             stdplus::EtherAddr mac{};
1345             auto neighbor = channelCall<getGatewayNeighbor<AF_INET6>>(channel);
1346             if (neighbor)
1347             {
1348                 mac = neighbor->mac;
1349             }
1350             ret.pack(stdplus::raw::asView<char>(mac));
1351             return responseSuccess(std::move(ret));
1352         }
1353         case LanParam::IPv6StaticRouter1PrefixLength:
1354         {
1355             ret.pack(uint8_t{0});
1356             return responseSuccess(std::move(ret));
1357         }
1358         case LanParam::IPv6StaticRouter1PrefixValue:
1359         {
1360             ret.pack(stdplus::raw::asView<char>(stdplus::In6Addr{}));
1361             return responseSuccess(std::move(ret));
1362         }
1363         case LanParam::cipherSuitePrivilegeLevels:
1364         {
1365             std::array<uint4_t, ipmi::maxCSRecords> csPrivilegeLevels;
1366 
1367             uint8_t resp =
1368                 getCipherConfigObject(csPrivFileName, csPrivDefaultFileName)
1369                     .getCSPrivilegeLevels(channel, csPrivilegeLevels);
1370             if (!resp)
1371             {
1372                 constexpr uint8_t reserved1 = 0x00;
1373                 ret.pack(reserved1, csPrivilegeLevels);
1374                 return responseSuccess(std::move(ret));
1375             }
1376             else
1377             {
1378                 return response(resp);
1379             }
1380         }
1381     }
1382 
1383     if (parameter >= oemCmdStart)
1384     {
1385         return getLanOem(channel, parameter, set, block);
1386     }
1387 
1388     return response(ccParamNotSupported);
1389 }
1390 
1391 constexpr const char* solInterface = "xyz.openbmc_project.Ipmi.SOL";
1392 constexpr const char* solPath = "/xyz/openbmc_project/ipmi/sol/";
1393 constexpr const uint16_t solDefaultPort = 623;
1394 
1395 RspType<> setSolConfParams(Context::ptr ctx, uint4_t channelBits,
1396                            uint4_t /*reserved*/, uint8_t parameter,
1397                            message::Payload& req)
1398 {
1399     const uint8_t channel = convertCurrentChannelNum(
1400         static_cast<uint8_t>(channelBits), ctx->channel);
1401 
1402     if (!isValidChannel(channel))
1403     {
1404         lg2::error("Set Sol Config - Invalid channel in request");
1405         return responseInvalidFieldRequest();
1406     }
1407 
1408     std::string solService{};
1409     std::string solPathWitheEthName = solPath + ipmi::getChannelName(channel);
1410 
1411     if (ipmi::getService(ctx, solInterface, solPathWitheEthName, solService))
1412     {
1413         lg2::error("Set Sol Config - Invalid solInterface, service: {SERVICE}, "
1414                    "object path: {OBJPATH}, interface: {INTERFACE}",
1415                    "SERVICE", solService, "OBJPATH", solPathWitheEthName,
1416                    "INTERFACE", solInterface);
1417         return responseInvalidFieldRequest();
1418     }
1419 
1420     switch (static_cast<SolConfParam>(parameter))
1421     {
1422         case SolConfParam::Progress:
1423         {
1424             uint8_t progress;
1425             if (req.unpack(progress) != 0 || !req.fullyUnpacked())
1426             {
1427                 return responseReqDataLenInvalid();
1428             }
1429 
1430             if (ipmi::setDbusProperty(ctx, solService, solPathWitheEthName,
1431                                       solInterface, "Progress", progress))
1432             {
1433                 return responseUnspecifiedError();
1434             }
1435             break;
1436         }
1437         case SolConfParam::Enable:
1438         {
1439             bool enable;
1440             uint7_t reserved2;
1441 
1442             if (req.unpack(enable, reserved2) != 0 || !req.fullyUnpacked())
1443             {
1444                 return responseReqDataLenInvalid();
1445             }
1446 
1447             if (ipmi::setDbusProperty(ctx, solService, solPathWitheEthName,
1448                                       solInterface, "Enable", enable))
1449             {
1450                 return responseUnspecifiedError();
1451             }
1452             break;
1453         }
1454         case SolConfParam::Authentication:
1455         {
1456             uint4_t privilegeBits{};
1457             uint2_t reserved2{};
1458             bool forceAuth = false;
1459             bool forceEncrypt = false;
1460 
1461             if (req.unpack(privilegeBits, reserved2, forceAuth, forceEncrypt) !=
1462                     0 ||
1463                 !req.fullyUnpacked())
1464             {
1465                 return responseReqDataLenInvalid();
1466             }
1467 
1468             uint8_t privilege = static_cast<uint8_t>(privilegeBits);
1469             if (privilege < static_cast<uint8_t>(Privilege::User) ||
1470                 privilege > static_cast<uint8_t>(Privilege::Oem))
1471             {
1472                 return ipmi::responseInvalidFieldRequest();
1473             }
1474 
1475             if (ipmi::setDbusProperty(ctx, solService, solPathWitheEthName,
1476                                       solInterface, "Privilege", privilege))
1477             {
1478                 return responseUnspecifiedError();
1479             }
1480 
1481             if (ipmi::setDbusProperty(ctx, solService, solPathWitheEthName,
1482                                       solInterface, "ForceEncryption",
1483                                       forceEncrypt))
1484             {
1485                 return responseUnspecifiedError();
1486             }
1487 
1488             if (ipmi::setDbusProperty(ctx, solService, solPathWitheEthName,
1489                                       solInterface, "ForceAuthentication",
1490                                       forceAuth))
1491             {
1492                 return responseUnspecifiedError();
1493             }
1494             break;
1495         }
1496         case SolConfParam::Accumulate:
1497         {
1498             uint8_t interval;
1499             uint8_t threshold;
1500             if (req.unpack(interval, threshold) != 0 || !req.fullyUnpacked())
1501             {
1502                 return responseReqDataLenInvalid();
1503             }
1504 
1505             if (threshold == 0)
1506             {
1507                 return responseInvalidFieldRequest();
1508             }
1509 
1510             if (ipmi::setDbusProperty(ctx, solService, solPathWitheEthName,
1511                                       solInterface, "AccumulateIntervalMS",
1512                                       interval))
1513             {
1514                 return responseUnspecifiedError();
1515             }
1516 
1517             if (ipmi::setDbusProperty(ctx, solService, solPathWitheEthName,
1518                                       solInterface, "Threshold", threshold))
1519             {
1520                 return responseUnspecifiedError();
1521             }
1522             break;
1523         }
1524         case SolConfParam::Retry:
1525         {
1526             uint3_t countBits;
1527             uint5_t reserved2;
1528             uint8_t interval;
1529 
1530             if (req.unpack(countBits, reserved2, interval) != 0 ||
1531                 !req.fullyUnpacked())
1532             {
1533                 return responseReqDataLenInvalid();
1534             }
1535 
1536             uint8_t count = static_cast<uint8_t>(countBits);
1537             if (ipmi::setDbusProperty(ctx, solService, solPathWitheEthName,
1538                                       solInterface, "RetryCount", count))
1539             {
1540                 return responseUnspecifiedError();
1541             }
1542 
1543             if (ipmi::setDbusProperty(ctx, solService, solPathWitheEthName,
1544                                       solInterface, "RetryIntervalMS",
1545                                       interval))
1546             {
1547                 return responseUnspecifiedError();
1548             }
1549             break;
1550         }
1551         case SolConfParam::Port:
1552         {
1553             return response(ipmiCCWriteReadParameter);
1554         }
1555         case SolConfParam::NonVbitrate:
1556         case SolConfParam::Vbitrate:
1557         case SolConfParam::Channel:
1558         default:
1559             return response(ipmiCCParamNotSupported);
1560     }
1561     return responseSuccess();
1562 }
1563 
1564 RspType<message::Payload> getSolConfParams(
1565     Context::ptr ctx, uint4_t channelBits, uint3_t /*reserved*/, bool revOnly,
1566     uint8_t parameter, uint8_t /*set*/, uint8_t /*block*/)
1567 {
1568     message::Payload ret;
1569     constexpr uint8_t current_revision = 0x11;
1570     ret.pack(current_revision);
1571     if (revOnly)
1572     {
1573         return responseSuccess(std::move(ret));
1574     }
1575 
1576     const uint8_t channel = convertCurrentChannelNum(
1577         static_cast<uint8_t>(channelBits), ctx->channel);
1578 
1579     if (!isValidChannel(channel))
1580     {
1581         lg2::error("Get Sol Config - Invalid channel in request");
1582         return responseInvalidFieldRequest();
1583     }
1584 
1585     std::string solService{};
1586     std::string solPathWitheEthName = solPath + ipmi::getChannelName(channel);
1587 
1588     if (ipmi::getService(ctx, solInterface, solPathWitheEthName, solService))
1589     {
1590         lg2::error("Set Sol Config - Invalid solInterface, service: {SERVICE}, "
1591                    "object path: {OBJPATH}, interface: {INTERFACE}",
1592                    "SERVICE", solService, "OBJPATH", solPathWitheEthName,
1593                    "INTERFACE", solInterface);
1594         return responseInvalidFieldRequest();
1595     }
1596 
1597     switch (static_cast<SolConfParam>(parameter))
1598     {
1599         case SolConfParam::Progress:
1600         {
1601             uint8_t progress;
1602             if (ipmi::getDbusProperty(ctx, solService, solPathWitheEthName,
1603                                       solInterface, "Progress", progress))
1604             {
1605                 return responseUnspecifiedError();
1606             }
1607             ret.pack(progress);
1608             return responseSuccess(std::move(ret));
1609         }
1610         case SolConfParam::Enable:
1611         {
1612             bool enable{};
1613             if (ipmi::getDbusProperty(ctx, solService, solPathWitheEthName,
1614                                       solInterface, "Enable", enable))
1615             {
1616                 return responseUnspecifiedError();
1617             }
1618             ret.pack(enable, uint7_t{});
1619             return responseSuccess(std::move(ret));
1620         }
1621         case SolConfParam::Authentication:
1622         {
1623             // 4bits, cast when pack
1624             uint8_t privilege;
1625             bool forceAuth = false;
1626             bool forceEncrypt = false;
1627 
1628             if (ipmi::getDbusProperty(ctx, solService, solPathWitheEthName,
1629                                       solInterface, "Privilege", privilege))
1630             {
1631                 return responseUnspecifiedError();
1632             }
1633 
1634             if (ipmi::getDbusProperty(ctx, solService, solPathWitheEthName,
1635                                       solInterface, "ForceAuthentication",
1636                                       forceAuth))
1637             {
1638                 return responseUnspecifiedError();
1639             }
1640 
1641             if (ipmi::getDbusProperty(ctx, solService, solPathWitheEthName,
1642                                       solInterface, "ForceEncryption",
1643                                       forceEncrypt))
1644             {
1645                 return responseUnspecifiedError();
1646             }
1647             ret.pack(uint4_t{privilege}, uint2_t{}, forceAuth, forceEncrypt);
1648             return responseSuccess(std::move(ret));
1649         }
1650         case SolConfParam::Accumulate:
1651         {
1652             uint8_t interval{}, threshold{};
1653 
1654             if (ipmi::getDbusProperty(ctx, solService, solPathWitheEthName,
1655                                       solInterface, "AccumulateIntervalMS",
1656                                       interval))
1657             {
1658                 return responseUnspecifiedError();
1659             }
1660 
1661             if (ipmi::getDbusProperty(ctx, solService, solPathWitheEthName,
1662                                       solInterface, "Threshold", threshold))
1663             {
1664                 return responseUnspecifiedError();
1665             }
1666             ret.pack(interval, threshold);
1667             return responseSuccess(std::move(ret));
1668         }
1669         case SolConfParam::Retry:
1670         {
1671             // 3bits, cast when cast
1672             uint8_t count{};
1673             uint8_t interval{};
1674 
1675             if (ipmi::getDbusProperty(ctx, solService, solPathWitheEthName,
1676                                       solInterface, "RetryCount", count))
1677             {
1678                 return responseUnspecifiedError();
1679             }
1680 
1681             if (ipmi::getDbusProperty(ctx, solService, solPathWitheEthName,
1682                                       solInterface, "RetryIntervalMS",
1683                                       interval))
1684             {
1685                 return responseUnspecifiedError();
1686             }
1687             ret.pack(uint3_t{count}, uint5_t{}, interval);
1688             return responseSuccess(std::move(ret));
1689         }
1690         case SolConfParam::Port:
1691         {
1692             auto port = solDefaultPort;
1693             ret.pack(static_cast<uint16_t>(port));
1694             return responseSuccess(std::move(ret));
1695         }
1696         case SolConfParam::Channel:
1697         {
1698             ret.pack(channel);
1699             return responseSuccess(std::move(ret));
1700         }
1701         case SolConfParam::NonVbitrate:
1702         {
1703             uint64_t baudRate;
1704             uint8_t encodedBitRate = 0;
1705             if (ipmi::getDbusProperty(
1706                     ctx, "xyz.openbmc_project.Console.default",
1707                     "/xyz/openbmc_project/console/default",
1708                     "xyz.openbmc_project.Console.UART", "Baud", baudRate))
1709             {
1710                 return ipmi::responseUnspecifiedError();
1711             }
1712             switch (baudRate)
1713             {
1714                 case 9600:
1715                     encodedBitRate = 0x06;
1716                     break;
1717                 case 19200:
1718                     encodedBitRate = 0x07;
1719                     break;
1720                 case 38400:
1721                     encodedBitRate = 0x08;
1722                     break;
1723                 case 57600:
1724                     encodedBitRate = 0x09;
1725                     break;
1726                 case 115200:
1727                     encodedBitRate = 0x0a;
1728                     break;
1729                 default:
1730                     break;
1731             }
1732             ret.pack(encodedBitRate);
1733             return responseSuccess(std::move(ret));
1734         }
1735         case SolConfParam::Vbitrate:
1736         default:
1737             return response(ipmiCCParamNotSupported);
1738     }
1739 
1740     return response(ccParamNotSupported);
1741 }
1742 
1743 } // namespace transport
1744 } // namespace ipmi
1745 
1746 void register_netfn_transport_functions() __attribute__((constructor));
1747 
1748 void register_netfn_transport_functions()
1749 {
1750     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnTransport,
1751                           ipmi::transport::cmdSetLanConfigParameters,
1752                           ipmi::Privilege::Admin, ipmi::transport::setLan);
1753     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnTransport,
1754                           ipmi::transport::cmdGetLanConfigParameters,
1755                           ipmi::Privilege::Operator, ipmi::transport::getLan);
1756     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnTransport,
1757                           ipmi::transport::cmdSetSolConfigParameters,
1758                           ipmi::Privilege::Admin,
1759                           ipmi::transport::setSolConfParams);
1760     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnTransport,
1761                           ipmi::transport::cmdGetSolConfigParameters,
1762                           ipmi::Privilege::User,
1763                           ipmi::transport::getSolConfParams);
1764 }
1765