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 RspType<> setLan(Context::ptr ctx, uint4_t channelBits, uint4_t reserved1,
847                  uint8_t parameter, message::Payload& req)
848 {
849     const uint8_t channel = convertCurrentChannelNum(
850         static_cast<uint8_t>(channelBits), ctx->channel);
851     if (reserved1 || !isValidChannel(channel))
852     {
853         log<level::ERR>("Set Lan - Invalid field in request");
854         req.trailingOk = true;
855         return responseInvalidFieldRequest();
856     }
857 
858     switch (static_cast<LanParam>(parameter))
859     {
860         case LanParam::SetStatus:
861         {
862             uint2_t flag;
863             uint6_t rsvd;
864             if (req.unpack(flag, rsvd) != 0 || !req.fullyUnpacked())
865             {
866                 return responseReqDataLenInvalid();
867             }
868             if (rsvd)
869             {
870                 return responseInvalidFieldRequest();
871             }
872             auto status = static_cast<SetStatus>(static_cast<uint8_t>(flag));
873             switch (status)
874             {
875                 case SetStatus::Complete:
876                 {
877                     getSetStatus(channel) = status;
878                     return responseSuccess();
879                 }
880                 case SetStatus::InProgress:
881                 {
882                     auto& storedStatus = getSetStatus(channel);
883                     if (storedStatus == SetStatus::InProgress)
884                     {
885                         return response(ccParamSetLocked);
886                     }
887                     storedStatus = status;
888                     return responseSuccess();
889                 }
890                 case SetStatus::Commit:
891                     if (getSetStatus(channel) != SetStatus::InProgress)
892                     {
893                         return responseInvalidFieldRequest();
894                     }
895                     return responseSuccess();
896             }
897             return response(ccParamNotSupported);
898         }
899         case LanParam::AuthSupport:
900         {
901             req.trailingOk = true;
902             return response(ccParamReadOnly);
903         }
904         case LanParam::AuthEnables:
905         {
906             req.trailingOk = true;
907             return response(ccParamReadOnly);
908         }
909         case LanParam::IP:
910         {
911             EthernetInterface::DHCPConf dhcp =
912                 channelCall<getDHCPProperty>(channel);
913             if ((dhcp == EthernetInterface::DHCPConf::v4) ||
914                 (dhcp == EthernetInterface::DHCPConf::both))
915             {
916                 return responseCommandNotAvailable();
917             }
918             in_addr ip;
919             std::array<uint8_t, sizeof(ip)> bytes;
920             if (req.unpack(bytes) != 0 || !req.fullyUnpacked())
921             {
922                 return responseReqDataLenInvalid();
923             }
924             copyInto(ip, bytes);
925             channelCall<reconfigureIfAddr4>(channel, ip, std::nullopt);
926             return responseSuccess();
927         }
928         case LanParam::IPSrc:
929         {
930             uint4_t flag;
931             uint4_t rsvd;
932             if (req.unpack(flag, rsvd) != 0 || !req.fullyUnpacked())
933             {
934                 return responseReqDataLenInvalid();
935             }
936             if (rsvd)
937             {
938                 return responseInvalidFieldRequest();
939             }
940             switch (static_cast<IPSrc>(static_cast<uint8_t>(flag)))
941             {
942                 case IPSrc::DHCP:
943                 {
944                     // The IPSrc IPMI command is only for IPv4
945                     // management. Modifying IPv6 state is done using
946                     // a completely different Set LAN Configuration
947                     // subcommand.
948                     channelCall<setDHCPv4Property>(
949                         channel, EthernetInterface::DHCPConf::v4);
950                     return responseSuccess();
951                 }
952                 case IPSrc::Unspecified:
953                 case IPSrc::Static:
954                 {
955                     channelCall<setDHCPv4Property>(
956                         channel, EthernetInterface::DHCPConf::none);
957                     return responseSuccess();
958                 }
959                 case IPSrc::BIOS:
960                 case IPSrc::BMC:
961                 {
962                     return responseInvalidFieldRequest();
963                 }
964             }
965             return response(ccParamNotSupported);
966         }
967         case LanParam::MAC:
968         {
969             ether_addr mac;
970             std::array<uint8_t, sizeof(mac)> bytes;
971             if (req.unpack(bytes) != 0 || !req.fullyUnpacked())
972             {
973                 return responseReqDataLenInvalid();
974             }
975             copyInto(mac, bytes);
976 
977             if (!isValidMACAddress(mac))
978             {
979                 return responseInvalidFieldRequest();
980             }
981             channelCall<setMACProperty>(channel, mac);
982             return responseSuccess();
983         }
984         case LanParam::SubnetMask:
985         {
986             EthernetInterface::DHCPConf dhcp =
987                 channelCall<getDHCPProperty>(channel);
988             if ((dhcp == EthernetInterface::DHCPConf::v4) ||
989                 (dhcp == EthernetInterface::DHCPConf::both))
990             {
991                 return responseCommandNotAvailable();
992             }
993             in_addr netmask;
994             std::array<uint8_t, sizeof(netmask)> bytes;
995             if (req.unpack(bytes) != 0 || !req.fullyUnpacked())
996             {
997                 return responseReqDataLenInvalid();
998             }
999             copyInto(netmask, bytes);
1000             uint8_t prefix = netmaskToPrefix(netmask);
1001             if (prefix < MIN_IPV4_PREFIX_LENGTH)
1002             {
1003                 return responseInvalidFieldRequest();
1004             }
1005             channelCall<reconfigureIfAddr4>(channel, std::nullopt, prefix);
1006             return responseSuccess();
1007         }
1008         case LanParam::Gateway1:
1009         {
1010             EthernetInterface::DHCPConf dhcp =
1011                 channelCall<getDHCPProperty>(channel);
1012             if ((dhcp == EthernetInterface::DHCPConf::v4) ||
1013                 (dhcp == EthernetInterface::DHCPConf::both))
1014             {
1015                 return responseCommandNotAvailable();
1016             }
1017             in_addr gateway;
1018             std::array<uint8_t, sizeof(gateway)> bytes;
1019             if (req.unpack(bytes) != 0 || !req.fullyUnpacked())
1020             {
1021                 return responseReqDataLenInvalid();
1022             }
1023             copyInto(gateway, bytes);
1024             channelCall<setGatewayProperty<AF_INET>>(channel, gateway);
1025             return responseSuccess();
1026         }
1027         case LanParam::Gateway1MAC:
1028         {
1029             ether_addr gatewayMAC;
1030             std::array<uint8_t, sizeof(gatewayMAC)> bytes;
1031             if (req.unpack(bytes) != 0 || !req.fullyUnpacked())
1032             {
1033                 return responseReqDataLenInvalid();
1034             }
1035             copyInto(gatewayMAC, bytes);
1036             channelCall<reconfigureGatewayMAC<AF_INET>>(channel, gatewayMAC);
1037             return responseSuccess();
1038         }
1039         case LanParam::VLANId:
1040         {
1041             uint12_t vlanData = 0;
1042             uint3_t reserved = 0;
1043             bool vlanEnable = 0;
1044 
1045             if (req.unpack(vlanData) || req.unpack(reserved) ||
1046                 req.unpack(vlanEnable) || !req.fullyUnpacked())
1047             {
1048                 return responseReqDataLenInvalid();
1049             }
1050 
1051             if (reserved)
1052             {
1053                 return responseInvalidFieldRequest();
1054             }
1055 
1056             uint16_t vlan = static_cast<uint16_t>(vlanData);
1057 
1058             if (!vlanEnable)
1059             {
1060                 lastDisabledVlan[channel] = vlan;
1061                 vlan = 0;
1062             }
1063             else if (vlan == 0 || vlan == VLAN_VALUE_MASK)
1064             {
1065                 return responseInvalidFieldRequest();
1066             }
1067 
1068             channelCall<reconfigureVLAN>(channel, vlan);
1069             return responseSuccess();
1070         }
1071         case LanParam::CiphersuiteSupport:
1072         case LanParam::CiphersuiteEntries:
1073         case LanParam::IPFamilySupport:
1074         {
1075             req.trailingOk = true;
1076             return response(ccParamReadOnly);
1077         }
1078         case LanParam::IPFamilyEnables:
1079         {
1080             uint8_t enables;
1081             if (req.unpack(enables) != 0 || !req.fullyUnpacked())
1082             {
1083                 return responseReqDataLenInvalid();
1084             }
1085             switch (static_cast<IPFamilyEnables>(enables))
1086             {
1087                 case IPFamilyEnables::DualStack:
1088                     return responseSuccess();
1089                 case IPFamilyEnables::IPv4Only:
1090                 case IPFamilyEnables::IPv6Only:
1091                     return response(ccParamNotSupported);
1092             }
1093             return response(ccParamNotSupported);
1094         }
1095         case LanParam::IPv6Status:
1096         {
1097             req.trailingOk = true;
1098             return response(ccParamReadOnly);
1099         }
1100         case LanParam::IPv6StaticAddresses:
1101         {
1102             uint8_t set;
1103             uint7_t rsvd;
1104             bool enabled;
1105             in6_addr ip;
1106             std::array<uint8_t, sizeof(ip)> ipbytes;
1107             uint8_t prefix;
1108             uint8_t status;
1109             if (req.unpack(set, rsvd, enabled, ipbytes, prefix, status) != 0 ||
1110                 !req.fullyUnpacked())
1111             {
1112                 return responseReqDataLenInvalid();
1113             }
1114             if (rsvd)
1115             {
1116                 return responseInvalidFieldRequest();
1117             }
1118             copyInto(ip, ipbytes);
1119             if (enabled)
1120             {
1121                 if (prefix < MIN_IPV6_PREFIX_LENGTH ||
1122                     prefix > MAX_IPV6_PREFIX_LENGTH)
1123                 {
1124                     return responseParmOutOfRange();
1125                 }
1126                 try
1127                 {
1128                     channelCall<reconfigureIfAddr6>(channel, set, ip, prefix);
1129                 }
1130                 catch (const sdbusplus::exception_t& e)
1131                 {
1132                     if (std::string_view err{
1133                             "xyz.openbmc_project.Common.Error.InvalidArgument"};
1134                         err == e.name())
1135                     {
1136                         return responseInvalidFieldRequest();
1137                     }
1138                     else
1139                     {
1140                         throw;
1141                     }
1142                 }
1143             }
1144             else
1145             {
1146                 channelCall<deconfigureIfAddr6>(channel, set);
1147             }
1148             return responseSuccess();
1149         }
1150         case LanParam::IPv6DynamicAddresses:
1151         {
1152             req.trailingOk = true;
1153             return response(ccParamReadOnly);
1154         }
1155         case LanParam::IPv6RouterControl:
1156         {
1157             std::bitset<8> control;
1158             constexpr uint8_t reservedRACCBits = 0xfc;
1159             if (req.unpack(control) != 0 || !req.fullyUnpacked())
1160             {
1161                 return responseReqDataLenInvalid();
1162             }
1163             if (std::bitset<8> expected(control &
1164                                         std::bitset<8>(reservedRACCBits));
1165                 expected.any())
1166             {
1167                 return response(ccParamNotSupported);
1168             }
1169 
1170             bool enableRA = control[IPv6RouterControlFlag::Dynamic];
1171             channelCall<setIPv6AcceptRA>(channel, enableRA);
1172             return responseSuccess();
1173         }
1174         case LanParam::IPv6StaticRouter1IP:
1175         {
1176             in6_addr gateway;
1177             std::array<uint8_t, sizeof(gateway)> bytes;
1178             if (req.unpack(bytes) != 0 || !req.fullyUnpacked())
1179             {
1180                 return responseReqDataLenInvalid();
1181             }
1182             copyInto(gateway, bytes);
1183             channelCall<setGatewayProperty<AF_INET6>>(channel, gateway);
1184             return responseSuccess();
1185         }
1186         case LanParam::IPv6StaticRouter1MAC:
1187         {
1188             ether_addr mac;
1189             std::array<uint8_t, sizeof(mac)> bytes;
1190             if (req.unpack(bytes) != 0 || !req.fullyUnpacked())
1191             {
1192                 return responseReqDataLenInvalid();
1193             }
1194             copyInto(mac, bytes);
1195             channelCall<reconfigureGatewayMAC<AF_INET6>>(channel, mac);
1196             return responseSuccess();
1197         }
1198         case LanParam::IPv6StaticRouter1PrefixLength:
1199         {
1200             uint8_t prefix;
1201             if (req.unpack(prefix) != 0 || !req.fullyUnpacked())
1202             {
1203                 return responseReqDataLenInvalid();
1204             }
1205             if (prefix != 0)
1206             {
1207                 return responseInvalidFieldRequest();
1208             }
1209             return responseSuccess();
1210         }
1211         case LanParam::IPv6StaticRouter1PrefixValue:
1212         {
1213             std::array<uint8_t, sizeof(in6_addr)> bytes;
1214             if (req.unpack(bytes) != 0 || !req.fullyUnpacked())
1215             {
1216                 return responseReqDataLenInvalid();
1217             }
1218             // Accept any prefix value since our prefix length has to be 0
1219             return responseSuccess();
1220         }
1221         case LanParam::cipherSuitePrivilegeLevels:
1222         {
1223             uint8_t reserved;
1224             std::array<uint4_t, ipmi::maxCSRecords> cipherSuitePrivs;
1225 
1226             if (req.unpack(reserved, cipherSuitePrivs) || !req.fullyUnpacked())
1227             {
1228                 return responseReqDataLenInvalid();
1229             }
1230 
1231             if (reserved)
1232             {
1233                 return responseInvalidFieldRequest();
1234             }
1235 
1236             uint8_t resp = getCipherConfigObject(csPrivFileName,
1237                                                  csPrivDefaultFileName)
1238                                .setCSPrivilegeLevels(channel, cipherSuitePrivs);
1239             if (!resp)
1240             {
1241                 return responseSuccess();
1242             }
1243             else
1244             {
1245                 req.trailingOk = true;
1246                 return response(resp);
1247             }
1248         }
1249     }
1250 
1251     if ((parameter >= oemCmdStart) && (parameter <= oemCmdEnd))
1252     {
1253         return setLanOem(channel, parameter, req);
1254     }
1255 
1256     req.trailingOk = true;
1257     return response(ccParamNotSupported);
1258 }
1259 
1260 RspType<message::Payload> getLan(Context::ptr ctx, uint4_t channelBits,
1261                                  uint3_t reserved, bool revOnly,
1262                                  uint8_t parameter, uint8_t set, uint8_t block)
1263 {
1264     message::Payload ret;
1265     constexpr uint8_t current_revision = 0x11;
1266     ret.pack(current_revision);
1267 
1268     if (revOnly)
1269     {
1270         return responseSuccess(std::move(ret));
1271     }
1272 
1273     const uint8_t channel = convertCurrentChannelNum(
1274         static_cast<uint8_t>(channelBits), ctx->channel);
1275     if (reserved || !isValidChannel(channel))
1276     {
1277         log<level::ERR>("Get Lan - Invalid field in request");
1278         return responseInvalidFieldRequest();
1279     }
1280 
1281     static std::vector<uint8_t> cipherList;
1282     static bool listInit = false;
1283     if (!listInit)
1284     {
1285         try
1286         {
1287             cipherList = cipher::getCipherList();
1288             listInit = true;
1289         }
1290         catch (const std::exception& e)
1291         {}
1292     }
1293 
1294     switch (static_cast<LanParam>(parameter))
1295     {
1296         case LanParam::SetStatus:
1297         {
1298             SetStatus status;
1299             try
1300             {
1301                 status = setStatus.at(channel);
1302             }
1303             catch (const std::out_of_range&)
1304             {
1305                 status = SetStatus::Complete;
1306             }
1307             ret.pack(types::enum_cast<uint2_t>(status), uint6_t{});
1308             return responseSuccess(std::move(ret));
1309         }
1310         case LanParam::AuthSupport:
1311         {
1312             std::bitset<6> support;
1313             ret.pack(support, uint2_t{});
1314             return responseSuccess(std::move(ret));
1315         }
1316         case LanParam::AuthEnables:
1317         {
1318             std::bitset<6> enables;
1319             ret.pack(enables, uint2_t{}); // Callback
1320             ret.pack(enables, uint2_t{}); // User
1321             ret.pack(enables, uint2_t{}); // Operator
1322             ret.pack(enables, uint2_t{}); // Admin
1323             ret.pack(enables, uint2_t{}); // OEM
1324             return responseSuccess(std::move(ret));
1325         }
1326         case LanParam::IP:
1327         {
1328             auto ifaddr = channelCall<getIfAddr4>(channel);
1329             in_addr addr{};
1330             if (ifaddr)
1331             {
1332                 addr = ifaddr->address;
1333             }
1334             ret.pack(dataRef(addr));
1335             return responseSuccess(std::move(ret));
1336         }
1337         case LanParam::IPSrc:
1338         {
1339             auto src = IPSrc::Static;
1340             EthernetInterface::DHCPConf dhcp =
1341                 channelCall<getDHCPProperty>(channel);
1342             if ((dhcp == EthernetInterface::DHCPConf::v4) ||
1343                 (dhcp == EthernetInterface::DHCPConf::both))
1344             {
1345                 src = IPSrc::DHCP;
1346             }
1347             ret.pack(types::enum_cast<uint4_t>(src), uint4_t{});
1348             return responseSuccess(std::move(ret));
1349         }
1350         case LanParam::MAC:
1351         {
1352             ether_addr mac = channelCall<getMACProperty>(channel);
1353             ret.pack(dataRef(mac));
1354             return responseSuccess(std::move(ret));
1355         }
1356         case LanParam::SubnetMask:
1357         {
1358             auto ifaddr = channelCall<getIfAddr4>(channel);
1359             uint8_t prefix = AddrFamily<AF_INET>::defaultPrefix;
1360             if (ifaddr)
1361             {
1362                 prefix = ifaddr->prefix;
1363             }
1364             in_addr netmask = prefixToNetmask(prefix);
1365             ret.pack(dataRef(netmask));
1366             return responseSuccess(std::move(ret));
1367         }
1368         case LanParam::Gateway1:
1369         {
1370             auto gateway =
1371                 channelCall<getGatewayProperty<AF_INET>>(channel).value_or(
1372                     in_addr{});
1373             ret.pack(dataRef(gateway));
1374             return responseSuccess(std::move(ret));
1375         }
1376         case LanParam::Gateway1MAC:
1377         {
1378             ether_addr mac{};
1379             auto neighbor = channelCall<getGatewayNeighbor<AF_INET>>(channel);
1380             if (neighbor)
1381             {
1382                 mac = neighbor->mac;
1383             }
1384             ret.pack(dataRef(mac));
1385             return responseSuccess(std::move(ret));
1386         }
1387         case LanParam::VLANId:
1388         {
1389             uint16_t vlan = channelCall<getVLANProperty>(channel);
1390             if (vlan != 0)
1391             {
1392                 vlan |= VLAN_ENABLE_FLAG;
1393             }
1394             else
1395             {
1396                 vlan = lastDisabledVlan[channel];
1397             }
1398             ret.pack(vlan);
1399             return responseSuccess(std::move(ret));
1400         }
1401         case LanParam::CiphersuiteSupport:
1402         {
1403             if (getChannelSessionSupport(channel) ==
1404                 EChannelSessSupported::none)
1405             {
1406                 return responseInvalidFieldRequest();
1407             }
1408             if (!listInit)
1409             {
1410                 return responseUnspecifiedError();
1411             }
1412             ret.pack(static_cast<uint8_t>(cipherList.size() - 1));
1413             return responseSuccess(std::move(ret));
1414         }
1415         case LanParam::CiphersuiteEntries:
1416         {
1417             if (getChannelSessionSupport(channel) ==
1418                 EChannelSessSupported::none)
1419             {
1420                 return responseInvalidFieldRequest();
1421             }
1422             if (!listInit)
1423             {
1424                 return responseUnspecifiedError();
1425             }
1426             ret.pack(cipherList);
1427             return responseSuccess(std::move(ret));
1428         }
1429         case LanParam::IPFamilySupport:
1430         {
1431             std::bitset<8> support;
1432             support[IPFamilySupportFlag::IPv6Only] = 0;
1433             support[IPFamilySupportFlag::DualStack] = 1;
1434             support[IPFamilySupportFlag::IPv6Alerts] = 1;
1435             ret.pack(support);
1436             return responseSuccess(std::move(ret));
1437         }
1438         case LanParam::IPFamilyEnables:
1439         {
1440             ret.pack(static_cast<uint8_t>(IPFamilyEnables::DualStack));
1441             return responseSuccess(std::move(ret));
1442         }
1443         case LanParam::IPv6Status:
1444         {
1445             ret.pack(MAX_IPV6_STATIC_ADDRESSES);
1446             ret.pack(MAX_IPV6_DYNAMIC_ADDRESSES);
1447             std::bitset<8> support;
1448             support[IPv6StatusFlag::DHCP] = 1;
1449             support[IPv6StatusFlag::SLAAC] = 1;
1450             ret.pack(support);
1451             return responseSuccess(std::move(ret));
1452         }
1453         case LanParam::IPv6StaticAddresses:
1454         {
1455             if (set >= MAX_IPV6_STATIC_ADDRESSES)
1456             {
1457                 return responseParmOutOfRange();
1458             }
1459             getLanIPv6Address(ret, channel, set, originsV6Static);
1460             return responseSuccess(std::move(ret));
1461         }
1462         case LanParam::IPv6DynamicAddresses:
1463         {
1464             if (set >= MAX_IPV6_DYNAMIC_ADDRESSES)
1465             {
1466                 return responseParmOutOfRange();
1467             }
1468             getLanIPv6Address(ret, channel, set, originsV6Dynamic);
1469             return responseSuccess(std::move(ret));
1470         }
1471         case LanParam::IPv6RouterControl:
1472         {
1473             std::bitset<8> control;
1474             control[IPv6RouterControlFlag::Dynamic] =
1475                 channelCall<getIPv6AcceptRA>(channel);
1476             control[IPv6RouterControlFlag::Static] = 1;
1477             ret.pack(control);
1478             return responseSuccess(std::move(ret));
1479         }
1480         case LanParam::IPv6StaticRouter1IP:
1481         {
1482             in6_addr gateway{};
1483             EthernetInterface::DHCPConf dhcp =
1484                 channelCall<getDHCPProperty>(channel);
1485             if ((dhcp == EthernetInterface::DHCPConf::v4) ||
1486                 (dhcp == EthernetInterface::DHCPConf::none))
1487             {
1488                 gateway =
1489                     channelCall<getGatewayProperty<AF_INET6>>(channel).value_or(
1490                         in6_addr{});
1491             }
1492             ret.pack(dataRef(gateway));
1493             return responseSuccess(std::move(ret));
1494         }
1495         case LanParam::IPv6StaticRouter1MAC:
1496         {
1497             ether_addr mac{};
1498             auto neighbor = channelCall<getGatewayNeighbor<AF_INET6>>(channel);
1499             if (neighbor)
1500             {
1501                 mac = neighbor->mac;
1502             }
1503             ret.pack(dataRef(mac));
1504             return responseSuccess(std::move(ret));
1505         }
1506         case LanParam::IPv6StaticRouter1PrefixLength:
1507         {
1508             ret.pack(UINT8_C(0));
1509             return responseSuccess(std::move(ret));
1510         }
1511         case LanParam::IPv6StaticRouter1PrefixValue:
1512         {
1513             in6_addr prefix{};
1514             ret.pack(dataRef(prefix));
1515             return responseSuccess(std::move(ret));
1516         }
1517         case LanParam::cipherSuitePrivilegeLevels:
1518         {
1519             std::array<uint4_t, ipmi::maxCSRecords> csPrivilegeLevels;
1520 
1521             uint8_t resp =
1522                 getCipherConfigObject(csPrivFileName, csPrivDefaultFileName)
1523                     .getCSPrivilegeLevels(channel, csPrivilegeLevels);
1524             if (!resp)
1525             {
1526                 constexpr uint8_t reserved1 = 0x00;
1527                 ret.pack(reserved1, csPrivilegeLevels);
1528                 return responseSuccess(std::move(ret));
1529             }
1530             else
1531             {
1532                 return response(resp);
1533             }
1534         }
1535     }
1536 
1537     if ((parameter >= oemCmdStart) && (parameter <= oemCmdEnd))
1538     {
1539         return getLanOem(channel, parameter, set, block);
1540     }
1541 
1542     return response(ccParamNotSupported);
1543 }
1544 
1545 constexpr const char* solInterface = "xyz.openbmc_project.Ipmi.SOL";
1546 constexpr const char* solPath = "/xyz/openbmc_project/ipmi/sol/";
1547 constexpr const uint16_t solDefaultPort = 623;
1548 
1549 RspType<> setSolConfParams(Context::ptr ctx, uint4_t channelBits,
1550                            uint4_t /*reserved*/, uint8_t parameter,
1551                            message::Payload& req)
1552 {
1553     const uint8_t channel = convertCurrentChannelNum(
1554         static_cast<uint8_t>(channelBits), ctx->channel);
1555 
1556     if (!isValidChannel(channel))
1557     {
1558         log<level::ERR>("Set Sol Config - Invalid channel in request");
1559         return responseInvalidFieldRequest();
1560     }
1561 
1562     std::string solService{};
1563     std::string solPathWitheEthName = solPath + ipmi::getChannelName(channel);
1564 
1565     if (ipmi::getService(ctx, solInterface, solPathWitheEthName, solService))
1566     {
1567         log<level::ERR>("Set Sol Config - Invalid solInterface",
1568                         entry("SERVICE=%s", solService.c_str()),
1569                         entry("OBJPATH=%s", solPathWitheEthName.c_str()),
1570                         entry("INTERFACE=%s", solInterface));
1571         return responseInvalidFieldRequest();
1572     }
1573 
1574     switch (static_cast<SolConfParam>(parameter))
1575     {
1576         case SolConfParam::Progress:
1577         {
1578             uint8_t progress;
1579             if (req.unpack(progress) != 0 || !req.fullyUnpacked())
1580             {
1581                 return responseReqDataLenInvalid();
1582             }
1583 
1584             if (ipmi::setDbusProperty(ctx, solService, solPathWitheEthName,
1585                                       solInterface, "Progress", progress))
1586             {
1587                 return responseUnspecifiedError();
1588             }
1589             break;
1590         }
1591         case SolConfParam::Enable:
1592         {
1593             bool enable;
1594             uint7_t reserved2;
1595 
1596             if (req.unpack(enable, reserved2) != 0 || !req.fullyUnpacked())
1597             {
1598                 return responseReqDataLenInvalid();
1599             }
1600 
1601             if (ipmi::setDbusProperty(ctx, solService, solPathWitheEthName,
1602                                       solInterface, "Enable", enable))
1603             {
1604                 return responseUnspecifiedError();
1605             }
1606             break;
1607         }
1608         case SolConfParam::Authentication:
1609         {
1610             uint4_t privilegeBits{};
1611             uint2_t reserved2{};
1612             bool forceAuth = false;
1613             bool forceEncrypt = false;
1614 
1615             if (req.unpack(privilegeBits, reserved2, forceAuth, forceEncrypt) !=
1616                     0 ||
1617                 !req.fullyUnpacked())
1618             {
1619                 return responseReqDataLenInvalid();
1620             }
1621 
1622             uint8_t privilege = static_cast<uint8_t>(privilegeBits);
1623             if (privilege < static_cast<uint8_t>(Privilege::User) ||
1624                 privilege > static_cast<uint8_t>(Privilege::Oem))
1625             {
1626                 return ipmi::responseInvalidFieldRequest();
1627             }
1628 
1629             if (ipmi::setDbusProperty(ctx, solService, solPathWitheEthName,
1630                                       solInterface, "Privilege", privilege))
1631             {
1632                 return responseUnspecifiedError();
1633             }
1634 
1635             if (ipmi::setDbusProperty(ctx, solService, solPathWitheEthName,
1636                                       solInterface, "ForceEncryption",
1637                                       forceEncrypt))
1638             {
1639                 return responseUnspecifiedError();
1640             }
1641 
1642             if (ipmi::setDbusProperty(ctx, solService, solPathWitheEthName,
1643                                       solInterface, "ForceAuthentication",
1644                                       forceAuth))
1645             {
1646                 return responseUnspecifiedError();
1647             }
1648             break;
1649         }
1650         case SolConfParam::Accumulate:
1651         {
1652             uint8_t interval;
1653             uint8_t threshold;
1654             if (req.unpack(interval, threshold) != 0 || !req.fullyUnpacked())
1655             {
1656                 return responseReqDataLenInvalid();
1657             }
1658 
1659             if (threshold == 0)
1660             {
1661                 return responseInvalidFieldRequest();
1662             }
1663 
1664             if (ipmi::setDbusProperty(ctx, solService, solPathWitheEthName,
1665                                       solInterface, "AccumulateIntervalMS",
1666                                       interval))
1667             {
1668                 return responseUnspecifiedError();
1669             }
1670 
1671             if (ipmi::setDbusProperty(ctx, solService, solPathWitheEthName,
1672                                       solInterface, "Threshold", threshold))
1673             {
1674                 return responseUnspecifiedError();
1675             }
1676             break;
1677         }
1678         case SolConfParam::Retry:
1679         {
1680             uint3_t countBits;
1681             uint5_t reserved2;
1682             uint8_t interval;
1683 
1684             if (req.unpack(countBits, reserved2, interval) != 0 ||
1685                 !req.fullyUnpacked())
1686             {
1687                 return responseReqDataLenInvalid();
1688             }
1689 
1690             uint8_t count = static_cast<uint8_t>(countBits);
1691             if (ipmi::setDbusProperty(ctx, solService, solPathWitheEthName,
1692                                       solInterface, "RetryCount", count))
1693             {
1694                 return responseUnspecifiedError();
1695             }
1696 
1697             if (ipmi::setDbusProperty(ctx, solService, solPathWitheEthName,
1698                                       solInterface, "RetryIntervalMS",
1699                                       interval))
1700             {
1701                 return responseUnspecifiedError();
1702             }
1703             break;
1704         }
1705         case SolConfParam::Port:
1706         {
1707             return response(ipmiCCWriteReadParameter);
1708         }
1709         case SolConfParam::NonVbitrate:
1710         case SolConfParam::Vbitrate:
1711         case SolConfParam::Channel:
1712         default:
1713             return response(ipmiCCParamNotSupported);
1714     }
1715     return responseSuccess();
1716 }
1717 
1718 RspType<message::Payload> getSolConfParams(Context::ptr ctx,
1719                                            uint4_t channelBits,
1720                                            uint3_t /*reserved*/, bool revOnly,
1721                                            uint8_t parameter, uint8_t /*set*/,
1722                                            uint8_t /*block*/)
1723 {
1724     message::Payload ret;
1725     constexpr uint8_t current_revision = 0x11;
1726     ret.pack(current_revision);
1727     if (revOnly)
1728     {
1729         return responseSuccess(std::move(ret));
1730     }
1731 
1732     const uint8_t channel = convertCurrentChannelNum(
1733         static_cast<uint8_t>(channelBits), ctx->channel);
1734 
1735     if (!isValidChannel(channel))
1736     {
1737         log<level::ERR>("Get Sol Config - Invalid channel in request");
1738         return responseInvalidFieldRequest();
1739     }
1740 
1741     std::string solService{};
1742     std::string solPathWitheEthName = solPath + ipmi::getChannelName(channel);
1743 
1744     if (ipmi::getService(ctx, solInterface, solPathWitheEthName, solService))
1745     {
1746         log<level::ERR>("Set Sol Config - Invalid solInterface",
1747                         entry("SERVICE=%s", solService.c_str()),
1748                         entry("OBJPATH=%s", solPathWitheEthName.c_str()),
1749                         entry("INTERFACE=%s", solInterface));
1750         return responseInvalidFieldRequest();
1751     }
1752 
1753     switch (static_cast<SolConfParam>(parameter))
1754     {
1755         case SolConfParam::Progress:
1756         {
1757             uint8_t progress;
1758             if (ipmi::getDbusProperty(ctx, solService, solPathWitheEthName,
1759                                       solInterface, "Progress", progress))
1760             {
1761                 return responseUnspecifiedError();
1762             }
1763             ret.pack(progress);
1764             return responseSuccess(std::move(ret));
1765         }
1766         case SolConfParam::Enable:
1767         {
1768             bool enable{};
1769             if (ipmi::getDbusProperty(ctx, solService, solPathWitheEthName,
1770                                       solInterface, "Enable", enable))
1771             {
1772                 return responseUnspecifiedError();
1773             }
1774             ret.pack(enable, uint7_t{});
1775             return responseSuccess(std::move(ret));
1776         }
1777         case SolConfParam::Authentication:
1778         {
1779             // 4bits, cast when pack
1780             uint8_t privilege;
1781             bool forceAuth = false;
1782             bool forceEncrypt = false;
1783 
1784             if (ipmi::getDbusProperty(ctx, solService, solPathWitheEthName,
1785                                       solInterface, "Privilege", privilege))
1786             {
1787                 return responseUnspecifiedError();
1788             }
1789 
1790             if (ipmi::getDbusProperty(ctx, solService, solPathWitheEthName,
1791                                       solInterface, "ForceAuthentication",
1792                                       forceAuth))
1793             {
1794                 return responseUnspecifiedError();
1795             }
1796 
1797             if (ipmi::getDbusProperty(ctx, solService, solPathWitheEthName,
1798                                       solInterface, "ForceEncryption",
1799                                       forceEncrypt))
1800             {
1801                 return responseUnspecifiedError();
1802             }
1803             ret.pack(uint4_t{privilege}, uint2_t{}, forceAuth, forceEncrypt);
1804             return responseSuccess(std::move(ret));
1805         }
1806         case SolConfParam::Accumulate:
1807         {
1808             uint8_t interval{}, threshold{};
1809 
1810             if (ipmi::getDbusProperty(ctx, solService, solPathWitheEthName,
1811                                       solInterface, "AccumulateIntervalMS",
1812                                       interval))
1813             {
1814                 return responseUnspecifiedError();
1815             }
1816 
1817             if (ipmi::getDbusProperty(ctx, solService, solPathWitheEthName,
1818                                       solInterface, "Threshold", threshold))
1819             {
1820                 return responseUnspecifiedError();
1821             }
1822             ret.pack(interval, threshold);
1823             return responseSuccess(std::move(ret));
1824         }
1825         case SolConfParam::Retry:
1826         {
1827             // 3bits, cast when cast
1828             uint8_t count{};
1829             uint8_t interval{};
1830 
1831             if (ipmi::getDbusProperty(ctx, solService, solPathWitheEthName,
1832                                       solInterface, "RetryCount", count))
1833             {
1834                 return responseUnspecifiedError();
1835             }
1836 
1837             if (ipmi::getDbusProperty(ctx, solService, solPathWitheEthName,
1838                                       solInterface, "RetryIntervalMS",
1839                                       interval))
1840             {
1841                 return responseUnspecifiedError();
1842             }
1843             ret.pack(uint3_t{count}, uint5_t{}, interval);
1844             return responseSuccess(std::move(ret));
1845         }
1846         case SolConfParam::Port:
1847         {
1848             auto port = solDefaultPort;
1849             ret.pack(static_cast<uint16_t>(port));
1850             return responseSuccess(std::move(ret));
1851         }
1852         case SolConfParam::Channel:
1853         {
1854             ret.pack(channel);
1855             return responseSuccess(std::move(ret));
1856         }
1857         case SolConfParam::NonVbitrate:
1858         {
1859             uint64_t baudRate;
1860             uint8_t encodedBitRate = 0;
1861             if (ipmi::getDbusProperty(
1862                     ctx, "xyz.openbmc_project.Console.default",
1863                     "/xyz/openbmc_project/console/default",
1864                     "xyz.openbmc_project.Console.UART", "Baud", baudRate))
1865             {
1866                 return ipmi::responseUnspecifiedError();
1867             }
1868             switch (baudRate)
1869             {
1870                 case 9600:
1871                     encodedBitRate = 0x06;
1872                     break;
1873                 case 19200:
1874                     encodedBitRate = 0x07;
1875                     break;
1876                 case 38400:
1877                     encodedBitRate = 0x08;
1878                     break;
1879                 case 57600:
1880                     encodedBitRate = 0x09;
1881                     break;
1882                 case 115200:
1883                     encodedBitRate = 0x0a;
1884                     break;
1885                 default:
1886                     break;
1887             }
1888             ret.pack(encodedBitRate);
1889             return responseSuccess(std::move(ret));
1890         }
1891         case SolConfParam::Vbitrate:
1892         default:
1893             return response(ipmiCCParamNotSupported);
1894     }
1895 
1896     return response(ccParamNotSupported);
1897 }
1898 
1899 } // namespace transport
1900 } // namespace ipmi
1901 
1902 void register_netfn_transport_functions() __attribute__((constructor));
1903 
1904 void register_netfn_transport_functions()
1905 {
1906     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnTransport,
1907                           ipmi::transport::cmdSetLanConfigParameters,
1908                           ipmi::Privilege::Admin, ipmi::transport::setLan);
1909     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnTransport,
1910                           ipmi::transport::cmdGetLanConfigParameters,
1911                           ipmi::Privilege::Operator, ipmi::transport::getLan);
1912     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnTransport,
1913                           ipmi::transport::cmdSetSolConfigParameters,
1914                           ipmi::Privilege::Admin,
1915                           ipmi::transport::setSolConfParams);
1916     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnTransport,
1917                           ipmi::transport::cmdGetSolConfigParameters,
1918                           ipmi::Privilege::User,
1919                           ipmi::transport::getSolConfParams);
1920 }
1921