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