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