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