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