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