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