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