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