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