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