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