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