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