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