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