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