xref: /openbmc/phosphor-host-ipmid/transporthandler.cpp (revision 69b4c2811a262094addab680c925e63dd3468440)
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  
getCipherList()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
ifnameInPath(std::string_view ifname,std::string_view path)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  
maybeGetChannelParams(sdbusplus::bus_t & bus,uint8_t channel)74  std::optional<ChannelParams> maybeGetChannelParams(sdbusplus::bus_t& bus,
75                                                     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  
getChannelParams(sdbusplus::bus_t & bus,uint8_t channel)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>
getEthProp(sdbusplus::bus_t & bus,const ChannelParams & params,const std::string & prop)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>
setEthProp(sdbusplus::bus_t & bus,const ChannelParams & params,const std::string & prop,const T & 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   */
getMACProperty(sdbusplus::bus_t & bus,const ChannelParams & params)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   */
setMACProperty(sdbusplus::bus_t & bus,const ChannelParams & params,stdplus::EtherAddr mac)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  
deleteObjectIfExists(sdbusplus::bus_t & bus,const std::string & service,const std::string & path)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>
createIfAddr(sdbusplus::bus_t & bus,const ChannelParams & params,typename AddrFamily<family>::addr address,uint8_t prefix)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   */
getIfAddr4(sdbusplus::bus_t & bus,const ChannelParams & params)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 (ifaddr4 && 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   */
reconfigureIfAddr4(sdbusplus::bus_t & bus,const ChannelParams & params,std::optional<stdplus::In4Addr> address,std::optional<uint8_t> prefix)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      auto addr = stdplus::In4Addr{};
305      if (ifaddr)
306      {
307          addr = ifaddr->address;
308          fallbackPrefix = ifaddr->prefix;
309          deleteObjectIfExists(bus, params.service, ifaddr->path);
310      }
311      addr = address.value_or(addr);
312      if (addr != stdplus::In4Addr{})
313      {
314          createIfAddr<AF_INET>(bus, params, addr,
315                                prefix.value_or(fallbackPrefix));
316      }
317  }
318  
319  template <int family>
findGatewayNeighbor(sdbusplus::bus_t & bus,const ChannelParams & params,ObjectLookupCache & neighbors)320  std::optional<IfNeigh<family>> findGatewayNeighbor(sdbusplus::bus_t& bus,
321                                                     const ChannelParams& params,
322                                                     ObjectLookupCache& neighbors)
323  {
324      auto gateway = getGatewayProperty<family>(bus, params);
325      if (!gateway)
326      {
327          return std::nullopt;
328      }
329  
330      return findStaticNeighbor<family>(bus, params, *gateway, neighbors);
331  }
332  
333  template <int family>
getGatewayNeighbor(sdbusplus::bus_t & bus,const ChannelParams & params)334  std::optional<IfNeigh<family>> getGatewayNeighbor(sdbusplus::bus_t& bus,
335                                                    const ChannelParams& params)
336  {
337      ObjectLookupCache neighbors(bus, params, INTF_NEIGHBOR);
338      return findGatewayNeighbor<family>(bus, params, neighbors);
339  }
340  
341  template <int family>
reconfigureGatewayMAC(sdbusplus::bus_t & bus,const ChannelParams & params,stdplus::EtherAddr mac)342  void reconfigureGatewayMAC(sdbusplus::bus_t& bus, const ChannelParams& params,
343                             stdplus::EtherAddr mac)
344  {
345      auto gateway = getGatewayProperty<family>(bus, params);
346      if (!gateway)
347      {
348          lg2::error("Tried to set Gateway MAC without Gateway");
349          elog<InternalFailure>();
350      }
351  
352      ObjectLookupCache neighbors(bus, params, INTF_NEIGHBOR);
353      auto neighbor =
354          findStaticNeighbor<family>(bus, params, *gateway, neighbors);
355      if (neighbor)
356      {
357          deleteObjectIfExists(bus, params.service, neighbor->path);
358      }
359  
360      createNeighbor<family>(bus, params, *gateway, mac);
361  }
362  
363  /** @brief Deconfigures the IPv6 address info configured for the interface
364   *
365   *  @param[in] bus     - The bus object used for lookups
366   *  @param[in] params  - The parameters for the channel
367   *  @param[in] idx     - The address index to operate on
368   */
deconfigureIfAddr6(sdbusplus::bus_t & bus,const ChannelParams & params,uint8_t idx)369  void deconfigureIfAddr6(sdbusplus::bus_t& bus, const ChannelParams& params,
370                          uint8_t idx)
371  {
372      auto ifaddr = getIfAddr<AF_INET6>(bus, params, idx, originsV6Static);
373      if (ifaddr)
374      {
375          deleteObjectIfExists(bus, params.service, ifaddr->path);
376      }
377  }
378  
379  /** @brief Reconfigures the IPv6 address info configured for the interface
380   *
381   *  @param[in] bus     - The bus object used for lookups
382   *  @param[in] params  - The parameters for the channel
383   *  @param[in] idx     - The address index to operate on
384   *  @param[in] address - The new address
385   *  @param[in] prefix  - The new address prefix
386   */
reconfigureIfAddr6(sdbusplus::bus_t & bus,const ChannelParams & params,uint8_t idx,stdplus::In6Addr address,uint8_t prefix)387  void reconfigureIfAddr6(sdbusplus::bus_t& bus, const ChannelParams& params,
388                          uint8_t idx, stdplus::In6Addr address, uint8_t prefix)
389  {
390      deconfigureIfAddr6(bus, params, idx);
391      createIfAddr<AF_INET6>(bus, params, address, prefix);
392  }
393  
394  /** @brief Converts the AddressOrigin into an IPv6Source
395   *
396   *  @param[in] origin - The DBus Address Origin to convert
397   *  @return The IPv6Source version of the origin
398   */
originToSourceType(IP::AddressOrigin origin)399  IPv6Source originToSourceType(IP::AddressOrigin origin)
400  {
401      switch (origin)
402      {
403          case IP::AddressOrigin::Static:
404              return IPv6Source::Static;
405          case IP::AddressOrigin::DHCP:
406              return IPv6Source::DHCP;
407          case IP::AddressOrigin::SLAAC:
408              return IPv6Source::SLAAC;
409          default:
410          {
411              auto originStr = sdbusplus::common::xyz::openbmc_project::network::
412                  convertForMessage(origin);
413              lg2::error("Invalid IP::AddressOrigin conversion to IPv6Source, "
414                         "origin: {ORIGIN}",
415                         "ORIGIN", originStr);
416              elog<InternalFailure>();
417          }
418      }
419  }
420  
421  /** @brief Packs the IPMI message response with IPv6 address data
422   *
423   *  @param[out] ret     - The IPMI response payload to be packed
424   *  @param[in]  channel - The channel id corresponding to an ethernet interface
425   *  @param[in]  set     - The set selector for determining address index
426   *  @param[in]  origins - Set of valid origins for address filtering
427   */
getLanIPv6Address(message::Payload & ret,uint8_t channel,uint8_t set,const std::unordered_set<IP::AddressOrigin> & origins)428  void getLanIPv6Address(message::Payload& ret, uint8_t channel, uint8_t set,
429                         const std::unordered_set<IP::AddressOrigin>& origins)
430  {
431      auto source = IPv6Source::Static;
432      bool enabled = false;
433      stdplus::In6Addr addr{};
434      uint8_t prefix{};
435      auto status = IPv6AddressStatus::Disabled;
436  
437      auto ifaddr = channelCall<getIfAddr<AF_INET6>>(channel, set, origins);
438      if (ifaddr)
439      {
440          source = originToSourceType(ifaddr->origin);
441          enabled = (origins == originsV6Static);
442          addr = ifaddr->address;
443          prefix = ifaddr->prefix;
444          status = IPv6AddressStatus::Active;
445      }
446  
447      ret.pack(set);
448      ret.pack(types::enum_cast<uint4_t>(source), uint3_t{}, enabled);
449      ret.pack(stdplus::raw::asView<char>(addr));
450      ret.pack(prefix);
451      ret.pack(types::enum_cast<uint8_t>(status));
452  }
453  
454  /** @brief Gets the vlan ID configured on the interface
455   *
456   *  @param[in] bus    - The bus object used for lookups
457   *  @param[in] params - The parameters for the channel
458   *  @return VLAN id or the standard 0 for no VLAN
459   */
getVLANProperty(sdbusplus::bus_t & bus,const ChannelParams & params)460  uint16_t getVLANProperty(sdbusplus::bus_t& bus, const ChannelParams& params)
461  {
462      // VLAN devices will always have a separate logical object
463      if (params.ifPath == params.logicalPath)
464      {
465          return 0;
466      }
467  
468      auto vlan = std::get<uint32_t>(getDbusProperty(
469          bus, params.service, params.logicalPath, INTF_VLAN, "Id"));
470      if ((vlan & VLAN_VALUE_MASK) != vlan)
471      {
472          lg2::error("networkd returned an invalid vlan: {VLAN} "
473                     "(CH={CHANNEL}, IF={IFNAME})",
474                     "CHANNEL", params.id, "IFNAME", params.ifname, "VLAN", vlan);
475          elog<InternalFailure>();
476      }
477      return vlan;
478  }
479  
480  /** @brief Deletes all of the possible configuration parameters for a channel
481   *
482   *  @param[in] bus    - The bus object used for lookups
483   *  @param[in] params - The parameters for the channel
484   */
deconfigureChannel(sdbusplus::bus_t & bus,ChannelParams & params)485  void deconfigureChannel(sdbusplus::bus_t& bus, ChannelParams& params)
486  {
487      // Delete all objects associated with the interface
488      ObjectTree objs =
489          ipmi::getSubTree(bus, std::vector<std::string>{DELETE_INTERFACE},
490                           std::string{PATH_ROOT});
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   */
createVLAN(sdbusplus::bus_t & bus,ChannelParams & params,uint16_t vlan)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   */
reconfigureVLAN(sdbusplus::bus_t & bus,ChannelParams & params,uint16_t vlan)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 =
560              findIfAddr<AF_INET6>(bus, params, i, originsV6Static, 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   */
getSetStatus(uint8_t channel)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>
unpackT(message::Payload & req)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 */
unpackFinal(message::Payload & req)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  
setLanOem(uint8_t,uint8_t,message::Payload & req)685  RspType<> setLanOem(uint8_t, uint8_t, message::Payload& req)
686  {
687      req.trailingOk = true;
688      return response(ccParamNotSupported);
689  }
690  
getLanOem(uint8_t,uint8_t,uint8_t,uint8_t)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   **/
isLanChannel(uint8_t channel)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  
setLanInt(Context::ptr ctx,uint4_t channelBits,uint4_t reserved1,uint8_t parameter,message::Payload & req)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          lg2::error("Set Lan - Invalid field in request");
726          req.trailingOk = true;
727          return responseInvalidFieldRequest();
728      }
729  
730      if (!isLanChannel(channel).value_or(false))
731      {
732          lg2::error("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              else if (vlan == 0 || vlan == VLAN_VALUE_MASK)
892              {
893                  return responseInvalidFieldRequest();
894              }
895  
896              channelCall<reconfigureVLAN>(channel, vlan);
897              return responseSuccess();
898          }
899          case LanParam::CiphersuiteSupport:
900          case LanParam::CiphersuiteEntries:
901          case LanParam::IPFamilySupport:
902          {
903              req.trailingOk = true;
904              return response(ccParamReadOnly);
905          }
906          case LanParam::IPFamilyEnables:
907          {
908              uint8_t enables;
909              if (req.unpack(enables) != 0)
910              {
911                  return responseReqDataLenInvalid();
912              }
913              unpackFinal(req);
914              switch (static_cast<IPFamilyEnables>(enables))
915              {
916                  case IPFamilyEnables::DualStack:
917                      return responseSuccess();
918                  case IPFamilyEnables::IPv4Only:
919                  case IPFamilyEnables::IPv6Only:
920                      return response(ccParamNotSupported);
921              }
922              return response(ccParamNotSupported);
923          }
924          case LanParam::IPv6Status:
925          {
926              req.trailingOk = true;
927              return response(ccParamReadOnly);
928          }
929          case LanParam::IPv6StaticAddresses:
930          {
931              uint8_t set;
932              uint7_t rsvd;
933              bool enabled;
934              uint8_t prefix;
935              uint8_t status;
936              if (req.unpack(set, rsvd, enabled) != 0)
937              {
938                  return responseReqDataLenInvalid();
939              }
940              auto ip = unpackT<stdplus::In6Addr>(req);
941              if (req.unpack(prefix, status) != 0)
942              {
943                  return responseReqDataLenInvalid();
944              }
945              unpackFinal(req);
946              if (rsvd)
947              {
948                  return responseInvalidFieldRequest();
949              }
950              if (enabled)
951              {
952                  if (prefix < MIN_IPV6_PREFIX_LENGTH ||
953                      prefix > MAX_IPV6_PREFIX_LENGTH)
954                  {
955                      return responseParmOutOfRange();
956                  }
957                  channelCall<reconfigureIfAddr6>(channel, set, ip, prefix);
958              }
959              else
960              {
961                  channelCall<deconfigureIfAddr6>(channel, set);
962              }
963              return responseSuccess();
964          }
965          case LanParam::IPv6DynamicAddresses:
966          {
967              req.trailingOk = true;
968              return response(ccParamReadOnly);
969          }
970          case LanParam::IPv6RouterControl:
971          {
972              std::bitset<8> control;
973              constexpr uint8_t reservedRACCBits = 0xfc;
974              if (req.unpack(control) != 0)
975              {
976                  return responseReqDataLenInvalid();
977              }
978              unpackFinal(req);
979              if (std::bitset<8> expected(
980                      control & std::bitset<8>(reservedRACCBits));
981                  expected.any())
982              {
983                  return response(ccParamNotSupported);
984              }
985  
986              bool enableRA = control[IPv6RouterControlFlag::Dynamic];
987              channelCall<setEthProp<bool>>(channel, "IPv6AcceptRA", enableRA);
988              channelCall<setEthProp<bool>>(channel, "DHCP6", enableRA);
989              return responseSuccess();
990          }
991          case LanParam::IPv6StaticRouter1IP:
992          {
993              auto gateway = unpackT<stdplus::In6Addr>(req);
994              unpackFinal(req);
995              channelCall<setGatewayProperty<AF_INET6>>(channel, gateway);
996              return responseSuccess();
997          }
998          case LanParam::IPv6StaticRouter1MAC:
999          {
1000              auto mac = unpackT<stdplus::EtherAddr>(req);
1001              unpackFinal(req);
1002              channelCall<reconfigureGatewayMAC<AF_INET6>>(channel, mac);
1003              return responseSuccess();
1004          }
1005          case LanParam::IPv6StaticRouter1PrefixLength:
1006          {
1007              uint8_t prefix;
1008              if (req.unpack(prefix) != 0)
1009              {
1010                  return responseReqDataLenInvalid();
1011              }
1012              unpackFinal(req);
1013              if (prefix != 0)
1014              {
1015                  return responseInvalidFieldRequest();
1016              }
1017              return responseSuccess();
1018          }
1019          case LanParam::IPv6StaticRouter1PrefixValue:
1020          {
1021              unpackT<stdplus::In6Addr>(req);
1022              unpackFinal(req);
1023              // Accept any prefix value since our prefix length has to be 0
1024              return responseSuccess();
1025          }
1026          case LanParam::cipherSuitePrivilegeLevels:
1027          {
1028              uint8_t rsvd;
1029              std::array<uint4_t, ipmi::maxCSRecords> cipherSuitePrivs;
1030  
1031              if (req.unpack(rsvd, cipherSuitePrivs))
1032              {
1033                  return responseReqDataLenInvalid();
1034              }
1035              unpackFinal(req);
1036  
1037              if (rsvd)
1038              {
1039                  return responseInvalidFieldRequest();
1040              }
1041  
1042              uint8_t resp =
1043                  getCipherConfigObject(csPrivFileName, csPrivDefaultFileName)
1044                      .setCSPrivilegeLevels(channel, cipherSuitePrivs);
1045              if (!resp)
1046              {
1047                  return responseSuccess();
1048              }
1049              else
1050              {
1051                  req.trailingOk = true;
1052                  return response(resp);
1053              }
1054          }
1055      }
1056  
1057      if (parameter >= oemCmdStart)
1058      {
1059          return setLanOem(channel, parameter, req);
1060      }
1061  
1062      req.trailingOk = true;
1063      return response(ccParamNotSupported);
1064  }
1065  
setLan(Context::ptr ctx,uint4_t channelBits,uint4_t reserved1,uint8_t parameter,message::Payload & req)1066  RspType<> setLan(Context::ptr ctx, uint4_t channelBits, uint4_t reserved1,
1067                   uint8_t parameter, message::Payload& req)
1068  {
1069      try
1070      {
1071          return setLanInt(ctx, channelBits, reserved1, parameter, req);
1072      }
1073      catch (ipmi::Cc cc)
1074      {
1075          return response(cc);
1076      }
1077      catch (const sdbusplus::exception_t& e)
1078      {
1079          if (std::string_view{InvalidArgument::errName} == e.name())
1080          {
1081              return responseInvalidFieldRequest();
1082          }
1083          throw;
1084      }
1085  }
1086  
getLan(Context::ptr ctx,uint4_t channelBits,uint3_t reserved,bool revOnly,uint8_t parameter,uint8_t set,uint8_t block)1087  RspType<message::Payload> getLan(Context::ptr ctx, uint4_t channelBits,
1088                                   uint3_t reserved, bool revOnly,
1089                                   uint8_t parameter, uint8_t set, uint8_t block)
1090  {
1091      message::Payload ret;
1092      constexpr uint8_t current_revision = 0x11;
1093      ret.pack(current_revision);
1094  
1095      if (revOnly)
1096      {
1097          return responseSuccess(std::move(ret));
1098      }
1099  
1100      const uint8_t channel = convertCurrentChannelNum(
1101          static_cast<uint8_t>(channelBits), ctx->channel);
1102      if (reserved || !isValidChannel(channel))
1103      {
1104          lg2::error("Get Lan - Invalid field in request");
1105          return responseInvalidFieldRequest();
1106      }
1107  
1108      if (!isLanChannel(channel).value_or(false))
1109      {
1110          lg2::error("Set Lan - Not a LAN channel");
1111          return responseInvalidFieldRequest();
1112      }
1113  
1114      static std::vector<uint8_t> cipherList;
1115      static bool listInit = false;
1116      if (!listInit)
1117      {
1118          try
1119          {
1120              cipherList = cipher::getCipherList();
1121              listInit = true;
1122          }
1123          catch (const std::exception& e)
1124          {}
1125      }
1126  
1127      switch (static_cast<LanParam>(parameter))
1128      {
1129          case LanParam::SetStatus:
1130          {
1131              SetStatus status;
1132              try
1133              {
1134                  status = setStatus.at(channel);
1135              }
1136              catch (const std::out_of_range&)
1137              {
1138                  status = SetStatus::Complete;
1139              }
1140              ret.pack(types::enum_cast<uint2_t>(status), uint6_t{});
1141              return responseSuccess(std::move(ret));
1142          }
1143          case LanParam::AuthSupport:
1144          {
1145              std::bitset<6> support;
1146              ret.pack(support, uint2_t{});
1147              return responseSuccess(std::move(ret));
1148          }
1149          case LanParam::AuthEnables:
1150          {
1151              std::bitset<6> enables;
1152              ret.pack(enables, uint2_t{}); // Callback
1153              ret.pack(enables, uint2_t{}); // User
1154              ret.pack(enables, uint2_t{}); // Operator
1155              ret.pack(enables, uint2_t{}); // Admin
1156              ret.pack(enables, uint2_t{}); // OEM
1157              return responseSuccess(std::move(ret));
1158          }
1159          case LanParam::IP:
1160          {
1161              auto ifaddr = channelCall<getIfAddr4>(channel);
1162              stdplus::In4Addr addr{};
1163              if (ifaddr)
1164              {
1165                  addr = ifaddr->address;
1166              }
1167              ret.pack(stdplus::raw::asView<char>(addr));
1168              return responseSuccess(std::move(ret));
1169          }
1170          case LanParam::IPSrc:
1171          {
1172              auto src = channelCall<getEthProp<bool>>(channel, "DHCP4")
1173                             ? IPSrc::DHCP
1174                             : IPSrc::Static;
1175              ret.pack(types::enum_cast<uint4_t>(src), uint4_t{});
1176              return responseSuccess(std::move(ret));
1177          }
1178          case LanParam::MAC:
1179          {
1180              auto mac = channelCall<getMACProperty>(channel);
1181              ret.pack(stdplus::raw::asView<char>(mac));
1182              return responseSuccess(std::move(ret));
1183          }
1184          case LanParam::SubnetMask:
1185          {
1186              auto ifaddr = channelCall<getIfAddr4>(channel);
1187              uint8_t prefix = AddrFamily<AF_INET>::defaultPrefix;
1188              if (ifaddr)
1189              {
1190                  prefix = ifaddr->prefix;
1191              }
1192              auto netmask = stdplus::pfxToMask<stdplus::In4Addr>(prefix);
1193              ret.pack(stdplus::raw::asView<char>(netmask));
1194              return responseSuccess(std::move(ret));
1195          }
1196          case LanParam::Gateway1:
1197          {
1198              auto gateway = channelCall<getGatewayProperty<AF_INET>>(channel);
1199              ret.pack(stdplus::raw::asView<char>(
1200                  gateway.value_or(stdplus::In4Addr{})));
1201              return responseSuccess(std::move(ret));
1202          }
1203          case LanParam::Gateway1MAC:
1204          {
1205              stdplus::EtherAddr mac{};
1206              auto neighbor = channelCall<getGatewayNeighbor<AF_INET>>(channel);
1207              if (neighbor)
1208              {
1209                  mac = neighbor->mac;
1210              }
1211              ret.pack(stdplus::raw::asView<char>(mac));
1212              return responseSuccess(std::move(ret));
1213          }
1214          case LanParam::VLANId:
1215          {
1216              uint16_t vlan = channelCall<getVLANProperty>(channel);
1217              if (vlan != 0)
1218              {
1219                  vlan |= VLAN_ENABLE_FLAG;
1220              }
1221              else
1222              {
1223                  vlan = lastDisabledVlan[channel];
1224              }
1225              ret.pack(vlan);
1226              return responseSuccess(std::move(ret));
1227          }
1228          case LanParam::CiphersuiteSupport:
1229          {
1230              if (getChannelSessionSupport(channel) ==
1231                  EChannelSessSupported::none)
1232              {
1233                  return responseInvalidFieldRequest();
1234              }
1235              if (!listInit)
1236              {
1237                  return responseUnspecifiedError();
1238              }
1239              ret.pack(static_cast<uint8_t>(cipherList.size() - 1));
1240              return responseSuccess(std::move(ret));
1241          }
1242          case LanParam::CiphersuiteEntries:
1243          {
1244              if (getChannelSessionSupport(channel) ==
1245                  EChannelSessSupported::none)
1246              {
1247                  return responseInvalidFieldRequest();
1248              }
1249              if (!listInit)
1250              {
1251                  return responseUnspecifiedError();
1252              }
1253              ret.pack(cipherList);
1254              return responseSuccess(std::move(ret));
1255          }
1256          case LanParam::IPFamilySupport:
1257          {
1258              std::bitset<8> support;
1259              support[IPFamilySupportFlag::IPv6Only] = 0;
1260              support[IPFamilySupportFlag::DualStack] = 1;
1261              support[IPFamilySupportFlag::IPv6Alerts] = 1;
1262              ret.pack(support);
1263              return responseSuccess(std::move(ret));
1264          }
1265          case LanParam::IPFamilyEnables:
1266          {
1267              ret.pack(static_cast<uint8_t>(IPFamilyEnables::DualStack));
1268              return responseSuccess(std::move(ret));
1269          }
1270          case LanParam::IPv6Status:
1271          {
1272              ret.pack(MAX_IPV6_STATIC_ADDRESSES);
1273              ret.pack(MAX_IPV6_DYNAMIC_ADDRESSES);
1274              std::bitset<8> support;
1275              support[IPv6StatusFlag::DHCP] = 1;
1276              support[IPv6StatusFlag::SLAAC] = 1;
1277              ret.pack(support);
1278              return responseSuccess(std::move(ret));
1279          }
1280          case LanParam::IPv6StaticAddresses:
1281          {
1282              if (set >= MAX_IPV6_STATIC_ADDRESSES)
1283              {
1284                  return responseParmOutOfRange();
1285              }
1286              getLanIPv6Address(ret, channel, set, originsV6Static);
1287              return responseSuccess(std::move(ret));
1288          }
1289          case LanParam::IPv6DynamicAddresses:
1290          {
1291              if (set >= MAX_IPV6_DYNAMIC_ADDRESSES)
1292              {
1293                  return responseParmOutOfRange();
1294              }
1295              getLanIPv6Address(ret, channel, set, originsV6Dynamic);
1296              return responseSuccess(std::move(ret));
1297          }
1298          case LanParam::IPv6RouterControl:
1299          {
1300              std::bitset<8> control;
1301              control[IPv6RouterControlFlag::Dynamic] =
1302                  channelCall<getEthProp<bool>>(channel, "IPv6AcceptRA");
1303              control[IPv6RouterControlFlag::Static] = 1;
1304              ret.pack(control);
1305              return responseSuccess(std::move(ret));
1306          }
1307          case LanParam::IPv6StaticRouter1IP:
1308          {
1309              stdplus::In6Addr gateway{};
1310              if (!channelCall<getEthProp<bool>>(channel, "IPv6AcceptRA"))
1311              {
1312                  gateway =
1313                      channelCall<getGatewayProperty<AF_INET6>>(channel).value_or(
1314                          stdplus::In6Addr{});
1315              }
1316              ret.pack(stdplus::raw::asView<char>(gateway));
1317              return responseSuccess(std::move(ret));
1318          }
1319          case LanParam::IPv6StaticRouter1MAC:
1320          {
1321              stdplus::EtherAddr mac{};
1322              auto neighbor = channelCall<getGatewayNeighbor<AF_INET6>>(channel);
1323              if (neighbor)
1324              {
1325                  mac = neighbor->mac;
1326              }
1327              ret.pack(stdplus::raw::asView<char>(mac));
1328              return responseSuccess(std::move(ret));
1329          }
1330          case LanParam::IPv6StaticRouter1PrefixLength:
1331          {
1332              ret.pack(uint8_t{0});
1333              return responseSuccess(std::move(ret));
1334          }
1335          case LanParam::IPv6StaticRouter1PrefixValue:
1336          {
1337              ret.pack(stdplus::raw::asView<char>(stdplus::In6Addr{}));
1338              return responseSuccess(std::move(ret));
1339          }
1340          case LanParam::cipherSuitePrivilegeLevels:
1341          {
1342              std::array<uint4_t, ipmi::maxCSRecords> csPrivilegeLevels;
1343  
1344              uint8_t resp =
1345                  getCipherConfigObject(csPrivFileName, csPrivDefaultFileName)
1346                      .getCSPrivilegeLevels(channel, csPrivilegeLevels);
1347              if (!resp)
1348              {
1349                  constexpr uint8_t reserved1 = 0x00;
1350                  ret.pack(reserved1, csPrivilegeLevels);
1351                  return responseSuccess(std::move(ret));
1352              }
1353              else
1354              {
1355                  return response(resp);
1356              }
1357          }
1358      }
1359  
1360      if (parameter >= oemCmdStart)
1361      {
1362          return getLanOem(channel, parameter, set, block);
1363      }
1364  
1365      return response(ccParamNotSupported);
1366  }
1367  
1368  constexpr const char* solInterface = "xyz.openbmc_project.Ipmi.SOL";
1369  constexpr const char* solPath = "/xyz/openbmc_project/ipmi/sol/";
1370  constexpr const uint16_t solDefaultPort = 623;
1371  
setSolConfParams(Context::ptr ctx,uint4_t channelBits,uint4_t,uint8_t parameter,message::Payload & req)1372  RspType<> setSolConfParams(Context::ptr ctx, uint4_t channelBits,
1373                             uint4_t /*reserved*/, uint8_t parameter,
1374                             message::Payload& req)
1375  {
1376      const uint8_t channel = convertCurrentChannelNum(
1377          static_cast<uint8_t>(channelBits), ctx->channel);
1378  
1379      if (!isValidChannel(channel))
1380      {
1381          lg2::error("Set Sol Config - Invalid channel in request");
1382          return responseInvalidFieldRequest();
1383      }
1384  
1385      std::string solService{};
1386      std::string solPathWitheEthName = solPath + ipmi::getChannelName(channel);
1387  
1388      if (ipmi::getService(ctx, solInterface, solPathWitheEthName, solService))
1389      {
1390          lg2::error("Set Sol Config - Invalid solInterface, service: {SERVICE}, "
1391                     "object path: {OBJPATH}, interface: {INTERFACE}",
1392                     "SERVICE", solService, "OBJPATH", solPathWitheEthName,
1393                     "INTERFACE", solInterface);
1394          return responseInvalidFieldRequest();
1395      }
1396  
1397      switch (static_cast<SolConfParam>(parameter))
1398      {
1399          case SolConfParam::Progress:
1400          {
1401              uint8_t progress;
1402              if (req.unpack(progress) != 0 || !req.fullyUnpacked())
1403              {
1404                  return responseReqDataLenInvalid();
1405              }
1406  
1407              if (ipmi::setDbusProperty(ctx, solService, solPathWitheEthName,
1408                                        solInterface, "Progress", progress))
1409              {
1410                  return responseUnspecifiedError();
1411              }
1412              break;
1413          }
1414          case SolConfParam::Enable:
1415          {
1416              bool enable;
1417              uint7_t reserved2;
1418  
1419              if (req.unpack(enable, reserved2) != 0 || !req.fullyUnpacked())
1420              {
1421                  return responseReqDataLenInvalid();
1422              }
1423  
1424              if (ipmi::setDbusProperty(ctx, solService, solPathWitheEthName,
1425                                        solInterface, "Enable", enable))
1426              {
1427                  return responseUnspecifiedError();
1428              }
1429              break;
1430          }
1431          case SolConfParam::Authentication:
1432          {
1433              uint4_t privilegeBits{};
1434              uint2_t reserved2{};
1435              bool forceAuth = false;
1436              bool forceEncrypt = false;
1437  
1438              if (req.unpack(privilegeBits, reserved2, forceAuth, forceEncrypt) !=
1439                      0 ||
1440                  !req.fullyUnpacked())
1441              {
1442                  return responseReqDataLenInvalid();
1443              }
1444  
1445              uint8_t privilege = static_cast<uint8_t>(privilegeBits);
1446              if (privilege < static_cast<uint8_t>(Privilege::User) ||
1447                  privilege > static_cast<uint8_t>(Privilege::Oem))
1448              {
1449                  return ipmi::responseInvalidFieldRequest();
1450              }
1451  
1452              if (ipmi::setDbusProperty(ctx, solService, solPathWitheEthName,
1453                                        solInterface, "Privilege", privilege))
1454              {
1455                  return responseUnspecifiedError();
1456              }
1457  
1458              if (ipmi::setDbusProperty(ctx, solService, solPathWitheEthName,
1459                                        solInterface, "ForceEncryption",
1460                                        forceEncrypt))
1461              {
1462                  return responseUnspecifiedError();
1463              }
1464  
1465              if (ipmi::setDbusProperty(ctx, solService, solPathWitheEthName,
1466                                        solInterface, "ForceAuthentication",
1467                                        forceAuth))
1468              {
1469                  return responseUnspecifiedError();
1470              }
1471              break;
1472          }
1473          case SolConfParam::Accumulate:
1474          {
1475              uint8_t interval;
1476              uint8_t threshold;
1477              if (req.unpack(interval, threshold) != 0 || !req.fullyUnpacked())
1478              {
1479                  return responseReqDataLenInvalid();
1480              }
1481  
1482              if (threshold == 0)
1483              {
1484                  return responseInvalidFieldRequest();
1485              }
1486  
1487              if (ipmi::setDbusProperty(ctx, solService, solPathWitheEthName,
1488                                        solInterface, "AccumulateIntervalMS",
1489                                        interval))
1490              {
1491                  return responseUnspecifiedError();
1492              }
1493  
1494              if (ipmi::setDbusProperty(ctx, solService, solPathWitheEthName,
1495                                        solInterface, "Threshold", threshold))
1496              {
1497                  return responseUnspecifiedError();
1498              }
1499              break;
1500          }
1501          case SolConfParam::Retry:
1502          {
1503              uint3_t countBits;
1504              uint5_t reserved2;
1505              uint8_t interval;
1506  
1507              if (req.unpack(countBits, reserved2, interval) != 0 ||
1508                  !req.fullyUnpacked())
1509              {
1510                  return responseReqDataLenInvalid();
1511              }
1512  
1513              uint8_t count = static_cast<uint8_t>(countBits);
1514              if (ipmi::setDbusProperty(ctx, solService, solPathWitheEthName,
1515                                        solInterface, "RetryCount", count))
1516              {
1517                  return responseUnspecifiedError();
1518              }
1519  
1520              if (ipmi::setDbusProperty(ctx, solService, solPathWitheEthName,
1521                                        solInterface, "RetryIntervalMS",
1522                                        interval))
1523              {
1524                  return responseUnspecifiedError();
1525              }
1526              break;
1527          }
1528          case SolConfParam::Port:
1529          {
1530              return response(ipmiCCWriteReadParameter);
1531          }
1532          case SolConfParam::NonVbitrate:
1533          case SolConfParam::Vbitrate:
1534          case SolConfParam::Channel:
1535          default:
1536              return response(ipmiCCParamNotSupported);
1537      }
1538      return responseSuccess();
1539  }
1540  
getSolConfParams(Context::ptr ctx,uint4_t channelBits,uint3_t,bool revOnly,uint8_t parameter,uint8_t,uint8_t)1541  RspType<message::Payload> getSolConfParams(
1542      Context::ptr ctx, uint4_t channelBits, uint3_t /*reserved*/, bool revOnly,
1543      uint8_t parameter, uint8_t /*set*/, uint8_t /*block*/)
1544  {
1545      message::Payload ret;
1546      constexpr uint8_t current_revision = 0x11;
1547      ret.pack(current_revision);
1548      if (revOnly)
1549      {
1550          return responseSuccess(std::move(ret));
1551      }
1552  
1553      const uint8_t channel = convertCurrentChannelNum(
1554          static_cast<uint8_t>(channelBits), ctx->channel);
1555  
1556      if (!isValidChannel(channel))
1557      {
1558          lg2::error("Get Sol Config - Invalid channel in request");
1559          return responseInvalidFieldRequest();
1560      }
1561  
1562      std::string solService{};
1563      std::string solPathWitheEthName = solPath + ipmi::getChannelName(channel);
1564  
1565      if (ipmi::getService(ctx, solInterface, solPathWitheEthName, solService))
1566      {
1567          lg2::error("Set Sol Config - Invalid solInterface, service: {SERVICE}, "
1568                     "object path: {OBJPATH}, interface: {INTERFACE}",
1569                     "SERVICE", solService, "OBJPATH", solPathWitheEthName,
1570                     "INTERFACE", solInterface);
1571          return responseInvalidFieldRequest();
1572      }
1573  
1574      switch (static_cast<SolConfParam>(parameter))
1575      {
1576          case SolConfParam::Progress:
1577          {
1578              uint8_t progress;
1579              if (ipmi::getDbusProperty(ctx, solService, solPathWitheEthName,
1580                                        solInterface, "Progress", progress))
1581              {
1582                  return responseUnspecifiedError();
1583              }
1584              ret.pack(progress);
1585              return responseSuccess(std::move(ret));
1586          }
1587          case SolConfParam::Enable:
1588          {
1589              bool enable{};
1590              if (ipmi::getDbusProperty(ctx, solService, solPathWitheEthName,
1591                                        solInterface, "Enable", enable))
1592              {
1593                  return responseUnspecifiedError();
1594              }
1595              ret.pack(enable, uint7_t{});
1596              return responseSuccess(std::move(ret));
1597          }
1598          case SolConfParam::Authentication:
1599          {
1600              // 4bits, cast when pack
1601              uint8_t privilege;
1602              bool forceAuth = false;
1603              bool forceEncrypt = false;
1604  
1605              if (ipmi::getDbusProperty(ctx, solService, solPathWitheEthName,
1606                                        solInterface, "Privilege", privilege))
1607              {
1608                  return responseUnspecifiedError();
1609              }
1610  
1611              if (ipmi::getDbusProperty(ctx, solService, solPathWitheEthName,
1612                                        solInterface, "ForceAuthentication",
1613                                        forceAuth))
1614              {
1615                  return responseUnspecifiedError();
1616              }
1617  
1618              if (ipmi::getDbusProperty(ctx, solService, solPathWitheEthName,
1619                                        solInterface, "ForceEncryption",
1620                                        forceEncrypt))
1621              {
1622                  return responseUnspecifiedError();
1623              }
1624              ret.pack(uint4_t{privilege}, uint2_t{}, forceAuth, forceEncrypt);
1625              return responseSuccess(std::move(ret));
1626          }
1627          case SolConfParam::Accumulate:
1628          {
1629              uint8_t interval{}, threshold{};
1630  
1631              if (ipmi::getDbusProperty(ctx, solService, solPathWitheEthName,
1632                                        solInterface, "AccumulateIntervalMS",
1633                                        interval))
1634              {
1635                  return responseUnspecifiedError();
1636              }
1637  
1638              if (ipmi::getDbusProperty(ctx, solService, solPathWitheEthName,
1639                                        solInterface, "Threshold", threshold))
1640              {
1641                  return responseUnspecifiedError();
1642              }
1643              ret.pack(interval, threshold);
1644              return responseSuccess(std::move(ret));
1645          }
1646          case SolConfParam::Retry:
1647          {
1648              // 3bits, cast when cast
1649              uint8_t count{};
1650              uint8_t interval{};
1651  
1652              if (ipmi::getDbusProperty(ctx, solService, solPathWitheEthName,
1653                                        solInterface, "RetryCount", count))
1654              {
1655                  return responseUnspecifiedError();
1656              }
1657  
1658              if (ipmi::getDbusProperty(ctx, solService, solPathWitheEthName,
1659                                        solInterface, "RetryIntervalMS",
1660                                        interval))
1661              {
1662                  return responseUnspecifiedError();
1663              }
1664              ret.pack(uint3_t{count}, uint5_t{}, interval);
1665              return responseSuccess(std::move(ret));
1666          }
1667          case SolConfParam::Port:
1668          {
1669              auto port = solDefaultPort;
1670              ret.pack(static_cast<uint16_t>(port));
1671              return responseSuccess(std::move(ret));
1672          }
1673          case SolConfParam::Channel:
1674          {
1675              ret.pack(channel);
1676              return responseSuccess(std::move(ret));
1677          }
1678          case SolConfParam::NonVbitrate:
1679          {
1680              uint64_t baudRate;
1681              uint8_t encodedBitRate = 0;
1682              if (ipmi::getDbusProperty(
1683                      ctx, "xyz.openbmc_project.Console.default",
1684                      "/xyz/openbmc_project/console/default",
1685                      "xyz.openbmc_project.Console.UART", "Baud", baudRate))
1686              {
1687                  return ipmi::responseUnspecifiedError();
1688              }
1689              switch (baudRate)
1690              {
1691                  case 9600:
1692                      encodedBitRate = 0x06;
1693                      break;
1694                  case 19200:
1695                      encodedBitRate = 0x07;
1696                      break;
1697                  case 38400:
1698                      encodedBitRate = 0x08;
1699                      break;
1700                  case 57600:
1701                      encodedBitRate = 0x09;
1702                      break;
1703                  case 115200:
1704                      encodedBitRate = 0x0a;
1705                      break;
1706                  default:
1707                      break;
1708              }
1709              ret.pack(encodedBitRate);
1710              return responseSuccess(std::move(ret));
1711          }
1712          case SolConfParam::Vbitrate:
1713          default:
1714              return response(ipmiCCParamNotSupported);
1715      }
1716  
1717      return response(ccParamNotSupported);
1718  }
1719  
1720  } // namespace transport
1721  } // namespace ipmi
1722  
1723  void registerNetFnTransportFunctions() __attribute__((constructor));
1724  
registerNetFnTransportFunctions()1725  void registerNetFnTransportFunctions()
1726  {
1727      ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnTransport,
1728                            ipmi::transport::cmdSetLanConfigParameters,
1729                            ipmi::Privilege::Admin, ipmi::transport::setLan);
1730      ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnTransport,
1731                            ipmi::transport::cmdGetLanConfigParameters,
1732                            ipmi::Privilege::Operator, ipmi::transport::getLan);
1733      ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnTransport,
1734                            ipmi::transport::cmdSetSolConfigParameters,
1735                            ipmi::Privilege::Admin,
1736                            ipmi::transport::setSolConfParams);
1737      ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnTransport,
1738                            ipmi::transport::cmdGetSolConfigParameters,
1739                            ipmi::Privilege::User,
1740                            ipmi::transport::getSolConfParams);
1741  }
1742