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