1 #pragma once
2 
3 #include "app/channel.hpp"
4 #include "user_channel/cipher_mgmt.hpp"
5 
6 #include <ipmid/api-types.hpp>
7 #include <ipmid/api.hpp>
8 #include <ipmid/message.hpp>
9 #include <ipmid/message/types.hpp>
10 #include <ipmid/types.hpp>
11 #include <ipmid/utils.hpp>
12 #include <phosphor-logging/elog-errors.hpp>
13 #include <phosphor-logging/elog.hpp>
14 #include <phosphor-logging/log.hpp>
15 #include <sdbusplus/bus.hpp>
16 #include <sdbusplus/exception.hpp>
17 #include <stdplus/net/addr/ether.hpp>
18 #include <stdplus/net/addr/ip.hpp>
19 #include <stdplus/str/conv.hpp>
20 #include <stdplus/zstring_view.hpp>
21 #include <user_channel/channel_layer.hpp>
22 #include <xyz/openbmc_project/Common/error.hpp>
23 #include <xyz/openbmc_project/Network/EthernetInterface/server.hpp>
24 #include <xyz/openbmc_project/Network/IP/server.hpp>
25 #include <xyz/openbmc_project/Network/Neighbor/server.hpp>
26 
27 #include <cinttypes>
28 #include <functional>
29 #include <optional>
30 #include <string>
31 #include <string_view>
32 #include <unordered_map>
33 #include <unordered_set>
34 #include <utility>
35 
36 namespace ipmi
37 {
38 namespace transport
39 {
40 
41 using stdplus::operator""_zsv;
42 
43 // D-Bus Network Daemon definitions
44 constexpr auto PATH_ROOT = "/xyz/openbmc_project/network"_zsv;
45 constexpr auto INTF_ETHERNET = "xyz.openbmc_project.Network.EthernetInterface";
46 constexpr auto INTF_IP = "xyz.openbmc_project.Network.IP";
47 constexpr auto INTF_IP_CREATE = "xyz.openbmc_project.Network.IP.Create";
48 constexpr auto INTF_MAC = "xyz.openbmc_project.Network.MACAddress";
49 constexpr auto INTF_NEIGHBOR = "xyz.openbmc_project.Network.Neighbor";
50 constexpr auto INTF_NEIGHBOR_CREATE_STATIC =
51     "xyz.openbmc_project.Network.Neighbor.CreateStatic";
52 constexpr auto INTF_VLAN = "xyz.openbmc_project.Network.VLAN";
53 constexpr auto INTF_VLAN_CREATE = "xyz.openbmc_project.Network.VLAN.Create";
54 
55 /** @brief IPMI LAN Parameters */
56 enum class LanParam : uint8_t
57 {
58     SetStatus = 0,
59     AuthSupport = 1,
60     AuthEnables = 2,
61     IP = 3,
62     IPSrc = 4,
63     MAC = 5,
64     SubnetMask = 6,
65     Gateway1 = 12,
66     Gateway1MAC = 13,
67     VLANId = 20,
68     CiphersuiteSupport = 22,
69     CiphersuiteEntries = 23,
70     cipherSuitePrivilegeLevels = 24,
71     IPFamilySupport = 50,
72     IPFamilyEnables = 51,
73     IPv6Status = 55,
74     IPv6StaticAddresses = 56,
75     IPv6DynamicAddresses = 59,
76     IPv6RouterControl = 64,
77     IPv6StaticRouter1IP = 65,
78     IPv6StaticRouter1MAC = 66,
79     IPv6StaticRouter1PrefixLength = 67,
80     IPv6StaticRouter1PrefixValue = 68,
81 };
82 
83 /** @brief IPMI IP Origin Types */
84 enum class IPSrc : uint8_t
85 {
86     Unspecified = 0,
87     Static = 1,
88     DHCP = 2,
89     BIOS = 3,
90     BMC = 4,
91 };
92 
93 /** @brief IPMI Set Status */
94 enum class SetStatus : uint8_t
95 {
96     Complete = 0,
97     InProgress = 1,
98     Commit = 2,
99 };
100 
101 /** @brief IPMI Family Suport Bits */
102 namespace IPFamilySupportFlag
103 {
104 constexpr uint8_t IPv6Only = 0;
105 constexpr uint8_t DualStack = 1;
106 constexpr uint8_t IPv6Alerts = 2;
107 } // namespace IPFamilySupportFlag
108 
109 /** @brief IPMI IPFamily Enables Flag */
110 enum class IPFamilyEnables : uint8_t
111 {
112     IPv4Only = 0,
113     IPv6Only = 1,
114     DualStack = 2,
115 };
116 
117 /** @brief IPMI IPv6 Dyanmic Status Bits */
118 namespace IPv6StatusFlag
119 {
120 constexpr uint8_t DHCP = 0;
121 constexpr uint8_t SLAAC = 1;
122 }; // namespace IPv6StatusFlag
123 
124 /** @brief IPMI IPv6 Source */
125 enum class IPv6Source : uint8_t
126 {
127     Static = 0,
128     SLAAC = 1,
129     DHCP = 2,
130 };
131 
132 /** @brief IPMI IPv6 Address Status */
133 enum class IPv6AddressStatus : uint8_t
134 {
135     Active = 0,
136     Disabled = 1,
137 };
138 
139 namespace IPv6RouterControlFlag
140 {
141 constexpr uint8_t Static = 0;
142 constexpr uint8_t Dynamic = 1;
143 }; // namespace IPv6RouterControlFlag
144 
145 // LAN Handler specific response codes
146 constexpr Cc ccParamNotSupported = 0x80;
147 constexpr Cc ccParamSetLocked = 0x81;
148 constexpr Cc ccParamReadOnly = 0x82;
149 
150 // VLANs are a 12-bit value
151 constexpr uint16_t VLAN_VALUE_MASK = 0x0fff;
152 constexpr uint16_t VLAN_ENABLE_FLAG = 0x8000;
153 
154 // Arbitrary v6 Address Limits to prevent too much output in ipmitool
155 constexpr uint8_t MAX_IPV6_STATIC_ADDRESSES = 15;
156 constexpr uint8_t MAX_IPV6_DYNAMIC_ADDRESSES = 15;
157 
158 // Prefix length limits of phosphor-networkd
159 constexpr uint8_t MIN_IPV6_PREFIX_LENGTH = 1;
160 constexpr uint8_t MAX_IPV6_PREFIX_LENGTH = 128;
161 
162 /** @brief The dbus parameters for the interface corresponding to a channel
163  *         This helps reduce the number of mapper lookups we need for each
164  *         query and simplifies finding the VLAN interface if needed.
165  */
166 struct ChannelParams
167 {
168     /** @brief The channel ID */
169     int id;
170     /** @brief channel name for the interface */
171     std::string ifname;
172     /** @brief Name of the service on the bus */
173     std::string service;
174     /** @brief Lower level adapter path that is guaranteed to not be a VLAN */
175     std::string ifPath;
176     /** @brief Logical adapter path used for address assignment */
177     std::string logicalPath;
178 };
179 
180 /** @brief Determines the ethernet interface name corresponding to a channel
181  *         Tries to map a VLAN object first so that the address information
182  *         is accurate. Otherwise it gets the standard ethernet interface.
183  *
184  *  @param[in] bus     - The bus object used for lookups
185  *  @param[in] channel - The channel id corresponding to an ethernet interface
186  *  @return Ethernet interface service and object path if it exists
187  */
188 std::optional<ChannelParams> maybeGetChannelParams(sdbusplus::bus_t& bus,
189                                                    uint8_t channel);
190 
191 /** @brief A trivial helper around maybeGetChannelParams() that throws an
192  *         exception when it is unable to acquire parameters for the channel.
193  *
194  *  @param[in] bus     - The bus object used for lookups
195  *  @param[in] channel - The channel id corresponding to an ethernet interface
196  *  @return Ethernet interface service and object path
197  */
198 ChannelParams getChannelParams(sdbusplus::bus_t& bus, uint8_t channel);
199 
200 /** @brief Trivializes using parameter getter functions by providing a bus
201  *         and channel parameters automatically.
202  *
203  *  @param[in] channel - The channel id corresponding to an ethernet interface
204  *  ...
205  */
206 template <auto func, typename... Args>
207 auto channelCall(uint8_t channel, Args&&... args)
208 {
209     sdbusplus::bus_t bus(ipmid_get_sd_bus_connection());
210     auto params = getChannelParams(bus, channel);
211     return std::invoke(func, bus, params, std::forward<Args>(args)...);
212 }
213 
214 /** @brief Generic paramters for different address families */
215 template <int family>
216 struct AddrFamily
217 {};
218 
219 /** @brief Parameter specialization for IPv4 */
220 template <>
221 struct AddrFamily<AF_INET>
222 {
223     using addr = stdplus::In4Addr;
224     static constexpr auto protocol =
225         sdbusplus::server::xyz::openbmc_project::network::IP::Protocol::IPv4;
226     static constexpr size_t maxStrLen = INET6_ADDRSTRLEN;
227     static constexpr uint8_t defaultPrefix = 32;
228     static constexpr char propertyGateway[] = "DefaultGateway";
229 };
230 
231 /** @brief Parameter specialization for IPv6 */
232 template <>
233 struct AddrFamily<AF_INET6>
234 {
235     using addr = stdplus::In6Addr;
236     static constexpr auto protocol =
237         sdbusplus::server::xyz::openbmc_project::network::IP::Protocol::IPv6;
238     static constexpr size_t maxStrLen = INET6_ADDRSTRLEN;
239     static constexpr uint8_t defaultPrefix = 128;
240     static constexpr char propertyGateway[] = "DefaultGateway6";
241 };
242 
243 /** @brief Interface Neighbor configuration parameters */
244 template <int family>
245 struct IfNeigh
246 {
247     std::string path;
248     typename AddrFamily<family>::addr ip;
249     stdplus::EtherAddr mac;
250 };
251 
252 /** @brief Interface IP Address configuration parameters */
253 template <int family>
254 struct IfAddr
255 {
256     std::string path;
257     typename AddrFamily<family>::addr address;
258     sdbusplus::server::xyz::openbmc_project::network::IP::AddressOrigin origin;
259     uint8_t prefix;
260 };
261 
262 /** @brief Valid address origins for IPv6 */
263 static inline const std::unordered_set<
264     sdbusplus::server::xyz::openbmc_project::network::IP::AddressOrigin>
265     originsV6Static = {sdbusplus::server::xyz::openbmc_project::network::IP::
266                            AddressOrigin::Static};
267 static inline const std::unordered_set<
268     sdbusplus::server::xyz::openbmc_project::network::IP::AddressOrigin>
269     originsV6Dynamic = {
270         sdbusplus::server::xyz::openbmc_project::network::IP::AddressOrigin::
271             DHCP,
272         sdbusplus::server::xyz::openbmc_project::network::IP::AddressOrigin::
273             SLAAC,
274 };
275 
276 /** @brief A lazy lookup mechanism for iterating over object properties stored
277  *         in DBus. This will only perform the object lookup when needed, and
278  *         retains a cache of previous lookups to speed up future iterations.
279  */
280 class ObjectLookupCache
281 {
282   public:
283     using PropertiesCache = std::unordered_map<std::string, PropertyMap>;
284 
285     /** @brief Creates a new ObjectLookupCache for the interface on the bus
286      *         NOTE: The inputs to this object must outlive the object since
287      *         they are only referenced by it.
288      *
289      *  @param[in] bus    - The bus object used for lookups
290      *  @param[in] params - The parameters for the channel
291      *  @param[in] intf   - The interface we are looking up
292      */
293     ObjectLookupCache(sdbusplus::bus_t& bus, const ChannelParams& params,
294                       const char* intf) :
295         bus(bus),
296         params(params), intf(intf),
297         objs(getAllDbusObjects(bus, params.logicalPath, intf, ""))
298     {}
299 
300     class iterator : public ObjectTree::const_iterator
301     {
302       public:
303         using value_type = PropertiesCache::value_type;
304 
305         iterator(ObjectTree::const_iterator it, ObjectLookupCache& container) :
306             ObjectTree::const_iterator(it), container(container),
307             ret(container.cache.end())
308         {}
309         value_type& operator*()
310         {
311             ret = container.get(ObjectTree::const_iterator::operator*().first);
312             return *ret;
313         }
314         value_type* operator->()
315         {
316             return &operator*();
317         }
318 
319       private:
320         ObjectLookupCache& container;
321         PropertiesCache::iterator ret;
322     };
323 
324     iterator begin() noexcept
325     {
326         return iterator(objs.begin(), *this);
327     }
328 
329     iterator end() noexcept
330     {
331         return iterator(objs.end(), *this);
332     }
333 
334   private:
335     sdbusplus::bus_t& bus;
336     const ChannelParams& params;
337     const char* const intf;
338     const ObjectTree objs;
339     PropertiesCache cache;
340 
341     /** @brief Gets a cached copy of the object properties if possible
342      *         Otherwise performs a query on DBus to look them up
343      *
344      *  @param[in] path - The object path to lookup
345      *  @return An iterator for the specified object path + properties
346      */
347     PropertiesCache::iterator get(const std::string& path)
348     {
349         auto it = cache.find(path);
350         if (it != cache.end())
351         {
352             return it;
353         }
354         auto properties = getAllDbusProperties(bus, params.service, path, intf);
355         return cache.insert({path, std::move(properties)}).first;
356     }
357 };
358 
359 /** @brief Searches the ip object lookup cache for an address matching
360  *         the input parameters. NOTE: The index lacks stability across address
361  *         changes since the network daemon has no notion of stable indicies.
362  *
363  *  @param[in] bus     - The bus object used for lookups
364  *  @param[in] params  - The parameters for the channel
365  *  @param[in] idx     - The index of the desired address on the interface
366  *  @param[in] origins - The allowed origins for the address objects
367  *  @param[in] ips     - The object lookup cache holding all of the address info
368  *  @return The address and prefix if it was found
369  */
370 template <int family>
371 std::optional<IfAddr<family>> findIfAddr(
372     [[maybe_unused]] sdbusplus::bus_t& bus,
373     [[maybe_unused]] const ChannelParams& params, uint8_t idx,
374     const std::unordered_set<
375         sdbusplus::server::xyz::openbmc_project::network::IP::AddressOrigin>&
376         origins,
377     ObjectLookupCache& ips)
378 {
379     for (const auto& [path, properties] : ips)
380     {
381         std::optional<typename AddrFamily<family>::addr> addr;
382         try
383         {
384             addr.emplace(stdplus::fromStr<typename AddrFamily<family>::addr>(
385                 std::get<std::string>(properties.at("Address"))));
386         }
387         catch (...)
388         {
389             continue;
390         }
391 
392         sdbusplus::server::xyz::openbmc_project::network::IP::AddressOrigin
393             origin = sdbusplus::server::xyz::openbmc_project::network::IP::
394                 convertAddressOriginFromString(
395                     std::get<std::string>(properties.at("Origin")));
396         if (origins.find(origin) == origins.end())
397         {
398             continue;
399         }
400 
401         if (idx > 0)
402         {
403             idx--;
404             continue;
405         }
406 
407         IfAddr<family> ifaddr;
408         ifaddr.path = path;
409         ifaddr.address = *addr;
410         ifaddr.prefix = std::get<uint8_t>(properties.at("PrefixLength"));
411         ifaddr.origin = origin;
412         return ifaddr;
413     }
414 
415     return std::nullopt;
416 }
417 /** @brief Trivial helper around findIfAddr that simplifies calls
418  *         for one off lookups. Don't use this if you intend to do multiple
419  *         lookups at a time.
420  *
421  *  @param[in] bus     - The bus object used for lookups
422  *  @param[in] params  - The parameters for the channel
423  *  @param[in] idx     - The index of the desired address on the interface
424  *  @param[in] origins - The allowed origins for the address objects
425  *  @return The address and prefix if it was found
426  */
427 template <int family>
428 auto getIfAddr(
429     sdbusplus::bus_t& bus, const ChannelParams& params, uint8_t idx,
430     const std::unordered_set<
431         sdbusplus::server::xyz::openbmc_project::network::IP::AddressOrigin>&
432         origins)
433 {
434     ObjectLookupCache ips(bus, params, INTF_IP);
435     return findIfAddr<family>(bus, params, idx, origins, ips);
436 }
437 
438 /** @brief Reconfigures the IPv6 address info configured for the interface
439  *
440  *  @param[in] bus     - The bus object used for lookups
441  *  @param[in] params  - The parameters for the channel
442  *  @param[in] idx     - The address index to operate on
443  *  @param[in] address - The new address
444  *  @param[in] prefix  - The new address prefix
445  */
446 void reconfigureIfAddr6(sdbusplus::bus_t& bus, const ChannelParams& params,
447                         uint8_t idx, stdplus::In6Addr address, uint8_t prefix);
448 
449 /** @brief Retrieves the current gateway for the address family on the system
450  *         NOTE: The gateway is per channel instead of the system wide one.
451  *
452  *  @param[in] bus    - The bus object used for lookups
453  *  @param[in] params - The parameters for the channel
454  *  @return An address representing the gateway address if it exists
455  */
456 template <int family>
457 std::optional<typename AddrFamily<family>::addr>
458     getGatewayProperty(sdbusplus::bus_t& bus, const ChannelParams& params)
459 {
460     auto objPath = "/xyz/openbmc_project/network/" + params.ifname;
461     auto gatewayStr = std::get<std::string>(
462         getDbusProperty(bus, params.service, objPath, INTF_ETHERNET,
463                         AddrFamily<family>::propertyGateway));
464     if (gatewayStr.empty())
465     {
466         return std::nullopt;
467     }
468     return stdplus::fromStr<typename AddrFamily<family>::addr>(gatewayStr);
469 }
470 
471 template <int family>
472 std::optional<IfNeigh<family>>
473     findStaticNeighbor(sdbusplus::bus_t&, const ChannelParams&,
474                        typename AddrFamily<family>::addr ip,
475                        ObjectLookupCache& neighbors)
476 {
477     using sdbusplus::server::xyz::openbmc_project::network::Neighbor;
478     const auto state =
479         sdbusplus::common::xyz::openbmc_project::network::convertForMessage(
480             Neighbor::State::Permanent);
481     for (const auto& [path, neighbor] : neighbors)
482     {
483         std::optional<typename AddrFamily<family>::addr> neighIP;
484         try
485         {
486             neighIP.emplace(stdplus::fromStr<typename AddrFamily<family>::addr>(
487                 std::get<std::string>(neighbor.at("IPAddress"))));
488         }
489         catch (...)
490         {
491             continue;
492         }
493         if (*neighIP != ip)
494         {
495             continue;
496         }
497         if (state != std::get<std::string>(neighbor.at("State")))
498         {
499             continue;
500         }
501 
502         IfNeigh<family> ret;
503         ret.path = path;
504         ret.ip = ip;
505         ret.mac = stdplus::fromStr<stdplus::EtherAddr>(
506             std::get<std::string>(neighbor.at("MACAddress")));
507         return ret;
508     }
509 
510     return std::nullopt;
511 }
512 
513 template <int family>
514 void createNeighbor(sdbusplus::bus_t& bus, const ChannelParams& params,
515                     typename AddrFamily<family>::addr address,
516                     stdplus::EtherAddr mac)
517 {
518     auto newreq = bus.new_method_call(params.service.c_str(),
519                                       params.logicalPath.c_str(),
520                                       INTF_NEIGHBOR_CREATE_STATIC, "Neighbor");
521     stdplus::ToStrHandle<stdplus::ToStr<stdplus::EtherAddr>> macToStr;
522     stdplus::ToStrHandle<stdplus::ToStr<typename AddrFamily<family>::addr>>
523         addrToStr;
524     newreq.append(addrToStr(address), macToStr(mac));
525     bus.call_noreply(newreq);
526 }
527 
528 /** @brief Deletes the dbus object. Ignores empty objects or objects that are
529  *         missing from the bus.
530  *
531  *  @param[in] bus     - The bus object used for lookups
532  *  @param[in] service - The name of the service
533  *  @param[in] path    - The path of the object to delete
534  */
535 void deleteObjectIfExists(sdbusplus::bus_t& bus, const std::string& service,
536                           const std::string& path);
537 
538 /** @brief Sets the value for the default gateway of the channel
539  *
540  *  @param[in] bus     - The bus object used for lookups
541  *  @param[in] params  - The parameters for the channel
542  *  @param[in] gateway - Gateway address to apply
543  */
544 template <int family>
545 void setGatewayProperty(sdbusplus::bus_t& bus, const ChannelParams& params,
546                         typename AddrFamily<family>::addr address)
547 {
548     // Save the old gateway MAC address if it exists so we can recreate it
549     auto gateway = getGatewayProperty<family>(bus, params);
550     std::optional<IfNeigh<family>> neighbor;
551     if (gateway)
552     {
553         ObjectLookupCache neighbors(bus, params, INTF_NEIGHBOR);
554         neighbor = findStaticNeighbor<family>(bus, params, *gateway, neighbors);
555     }
556 
557     auto objPath = "/xyz/openbmc_project/network/" + params.ifname;
558     setDbusProperty(bus, params.service, objPath, INTF_ETHERNET,
559                     AddrFamily<family>::propertyGateway,
560                     stdplus::toStr(address));
561 
562     // Restore the gateway MAC if we had one
563     if (neighbor)
564     {
565         deleteObjectIfExists(bus, params.service, neighbor->path);
566         createNeighbor<family>(bus, params, address, neighbor->mac);
567     }
568 }
569 
570 /** @enum SolConfParam
571  *
572  *  using for Set/Get SOL configuration parameters command.
573  */
574 enum class SolConfParam : uint8_t
575 {
576     Progress,       //!< Set In Progress.
577     Enable,         //!< SOL Enable.
578     Authentication, //!< SOL Authentication.
579     Accumulate,     //!< Character Accumulate Interval & Send Threshold.
580     Retry,          //!< SOL Retry.
581     NonVbitrate,    //!< SOL non-volatile bit rate.
582     Vbitrate,       //!< SOL volatile bit rate.
583     Channel,        //!< SOL payload channel.
584     Port,           //!< SOL payload port.
585 };
586 
587 constexpr uint8_t ipmiCCParamNotSupported = 0x80;
588 constexpr uint8_t ipmiCCWriteReadParameter = 0x82;
589 
590 } // namespace transport
591 } // namespace ipmi
592