1 #pragma once 2 3 #include "app/channel.hpp" 4 #include "transportconstants.hpp" 5 #include "user_channel/cipher_mgmt.hpp" 6 7 #include <ipmid/api-types.hpp> 8 #include <ipmid/api.hpp> 9 #include <ipmid/message.hpp> 10 #include <ipmid/message/types.hpp> 11 #include <ipmid/types.hpp> 12 #include <ipmid/utils.hpp> 13 #include <phosphor-logging/elog-errors.hpp> 14 #include <phosphor-logging/elog.hpp> 15 #include <phosphor-logging/log.hpp> 16 #include <sdbusplus/bus.hpp> 17 #include <sdbusplus/exception.hpp> 18 #include <stdplus/net/addr/ether.hpp> 19 #include <stdplus/net/addr/ip.hpp> 20 #include <stdplus/str/conv.hpp> 21 #include <stdplus/zstring_view.hpp> 22 #include <user_channel/channel_layer.hpp> 23 #include <xyz/openbmc_project/Common/error.hpp> 24 #include <xyz/openbmc_project/Network/EthernetInterface/server.hpp> 25 #include <xyz/openbmc_project/Network/IP/server.hpp> 26 #include <xyz/openbmc_project/Network/Neighbor/server.hpp> 27 28 #include <cinttypes> 29 #include <functional> 30 #include <optional> 31 #include <string> 32 #include <string_view> 33 #include <unordered_map> 34 #include <unordered_set> 35 #include <utility> 36 37 namespace ipmi 38 { 39 namespace transport 40 { 41 42 /** @brief The dbus parameters for the interface corresponding to a channel 43 * This helps reduce the number of mapper lookups we need for each 44 * query and simplifies finding the VLAN interface if needed. 45 */ 46 struct ChannelParams 47 { 48 /** @brief The channel ID */ 49 int id; 50 /** @brief channel name for the interface */ 51 std::string ifname; 52 /** @brief Name of the service on the bus */ 53 std::string service; 54 /** @brief Lower level adapter path that is guaranteed to not be a VLAN */ 55 std::string ifPath; 56 /** @brief Logical adapter path used for address assignment */ 57 std::string logicalPath; 58 }; 59 60 /** @brief Determines the ethernet interface name corresponding to a channel 61 * Tries to map a VLAN object first so that the address information 62 * is accurate. Otherwise it gets the standard ethernet interface. 63 * 64 * @param[in] bus - The bus object used for lookups 65 * @param[in] channel - The channel id corresponding to an ethernet interface 66 * @return Ethernet interface service and object path if it exists 67 */ 68 std::optional<ChannelParams> 69 maybeGetChannelParams(sdbusplus::bus_t& bus, uint8_t channel); 70 71 /** @brief A trivial helper around maybeGetChannelParams() that throws an 72 * exception when it is unable to acquire parameters for the channel. 73 * 74 * @param[in] bus - The bus object used for lookups 75 * @param[in] channel - The channel id corresponding to an ethernet interface 76 * @return Ethernet interface service and object path 77 */ 78 ChannelParams getChannelParams(sdbusplus::bus_t& bus, uint8_t channel); 79 80 /** @brief Trivializes using parameter getter functions by providing a bus 81 * and channel parameters automatically. 82 * 83 * @param[in] channel - The channel id corresponding to an ethernet interface 84 * ... 85 */ 86 template <auto func, typename... Args> 87 auto channelCall(uint8_t channel, Args&&... args) 88 { 89 sdbusplus::bus_t bus(ipmid_get_sd_bus_connection()); 90 auto params = getChannelParams(bus, channel); 91 return std::invoke(func, bus, params, std::forward<Args>(args)...); 92 } 93 94 /** @brief Generic paramters for different address families */ 95 template <int family> 96 struct AddrFamily 97 {}; 98 99 /** @brief Parameter specialization for IPv4 */ 100 template <> 101 struct AddrFamily<AF_INET> 102 { 103 using addr = stdplus::In4Addr; 104 static constexpr auto protocol = 105 sdbusplus::server::xyz::openbmc_project::network::IP::Protocol::IPv4; 106 static constexpr size_t maxStrLen = INET6_ADDRSTRLEN; 107 static constexpr uint8_t defaultPrefix = 32; 108 static constexpr char propertyGateway[] = "DefaultGateway"; 109 }; 110 111 /** @brief Parameter specialization for IPv6 */ 112 template <> 113 struct AddrFamily<AF_INET6> 114 { 115 using addr = stdplus::In6Addr; 116 static constexpr auto protocol = 117 sdbusplus::server::xyz::openbmc_project::network::IP::Protocol::IPv6; 118 static constexpr size_t maxStrLen = INET6_ADDRSTRLEN; 119 static constexpr uint8_t defaultPrefix = 128; 120 static constexpr char propertyGateway[] = "DefaultGateway6"; 121 }; 122 123 /** @brief Interface Neighbor configuration parameters */ 124 template <int family> 125 struct IfNeigh 126 { 127 std::string path; 128 typename AddrFamily<family>::addr ip; 129 stdplus::EtherAddr mac; 130 }; 131 132 /** @brief Interface IP Address configuration parameters */ 133 template <int family> 134 struct IfAddr 135 { 136 std::string path; 137 typename AddrFamily<family>::addr address; 138 sdbusplus::server::xyz::openbmc_project::network::IP::AddressOrigin origin; 139 uint8_t prefix; 140 }; 141 142 /** @brief Valid address origins for IPv6 */ 143 static inline const std::unordered_set< 144 sdbusplus::server::xyz::openbmc_project::network::IP::AddressOrigin> 145 originsV6Static = {sdbusplus::server::xyz::openbmc_project::network::IP:: 146 AddressOrigin::Static}; 147 static inline const std::unordered_set< 148 sdbusplus::server::xyz::openbmc_project::network::IP::AddressOrigin> 149 originsV6Dynamic = { 150 sdbusplus::server::xyz::openbmc_project::network::IP::AddressOrigin:: 151 DHCP, 152 sdbusplus::server::xyz::openbmc_project::network::IP::AddressOrigin:: 153 SLAAC, 154 }; 155 156 /** @brief A lazy lookup mechanism for iterating over object properties stored 157 * in DBus. This will only perform the object lookup when needed, and 158 * retains a cache of previous lookups to speed up future iterations. 159 */ 160 class ObjectLookupCache 161 { 162 public: 163 using PropertiesCache = std::unordered_map<std::string, PropertyMap>; 164 165 /** @brief Creates a new ObjectLookupCache for the interface on the bus 166 * NOTE: The inputs to this object must outlive the object since 167 * they are only referenced by it. 168 * 169 * @param[in] bus - The bus object used for lookups 170 * @param[in] params - The parameters for the channel 171 * @param[in] intf - The interface we are looking up 172 */ 173 ObjectLookupCache(sdbusplus::bus_t& bus, const ChannelParams& params, 174 const char* intf) : 175 bus(bus), params(params), intf(intf), 176 objs(getAllDbusObjects(bus, params.logicalPath, intf, "")) 177 {} 178 179 class iterator : public ObjectTree::const_iterator 180 { 181 public: 182 using value_type = PropertiesCache::value_type; 183 184 iterator(ObjectTree::const_iterator it, ObjectLookupCache& container) : 185 ObjectTree::const_iterator(it), container(container), 186 ret(container.cache.end()) 187 {} 188 value_type& operator*() 189 { 190 ret = container.get(ObjectTree::const_iterator::operator*().first); 191 return *ret; 192 } 193 value_type* operator->() 194 { 195 return &operator*(); 196 } 197 198 private: 199 ObjectLookupCache& container; 200 PropertiesCache::iterator ret; 201 }; 202 203 iterator begin() noexcept 204 { 205 return iterator(objs.begin(), *this); 206 } 207 208 iterator end() noexcept 209 { 210 return iterator(objs.end(), *this); 211 } 212 213 private: 214 sdbusplus::bus_t& bus; 215 const ChannelParams& params; 216 const char* const intf; 217 const ObjectTree objs; 218 PropertiesCache cache; 219 220 /** @brief Gets a cached copy of the object properties if possible 221 * Otherwise performs a query on DBus to look them up 222 * 223 * @param[in] path - The object path to lookup 224 * @return An iterator for the specified object path + properties 225 */ 226 PropertiesCache::iterator get(const std::string& path) 227 { 228 auto it = cache.find(path); 229 if (it != cache.end()) 230 { 231 return it; 232 } 233 auto properties = getAllDbusProperties(bus, params.service, path, intf); 234 return cache.insert({path, std::move(properties)}).first; 235 } 236 }; 237 238 /** @brief Searches the ip object lookup cache for an address matching 239 * the input parameters. NOTE: The index lacks stability across address 240 * changes since the network daemon has no notion of stable indicies. 241 * 242 * @param[in] bus - The bus object used for lookups 243 * @param[in] params - The parameters for the channel 244 * @param[in] idx - The index of the desired address on the interface 245 * @param[in] origins - The allowed origins for the address objects 246 * @param[in] ips - The object lookup cache holding all of the address info 247 * @return The address and prefix if it was found 248 */ 249 template <int family> 250 std::optional<IfAddr<family>> findIfAddr( 251 [[maybe_unused]] sdbusplus::bus_t& bus, 252 [[maybe_unused]] const ChannelParams& params, uint8_t idx, 253 const std::unordered_set< 254 sdbusplus::server::xyz::openbmc_project::network::IP::AddressOrigin>& 255 origins, 256 ObjectLookupCache& ips) 257 { 258 for (const auto& [path, properties] : ips) 259 { 260 std::optional<typename AddrFamily<family>::addr> addr; 261 try 262 { 263 addr.emplace(stdplus::fromStr<typename AddrFamily<family>::addr>( 264 std::get<std::string>(properties.at("Address")))); 265 } 266 catch (...) 267 { 268 continue; 269 } 270 271 sdbusplus::server::xyz::openbmc_project::network::IP::AddressOrigin 272 origin = sdbusplus::server::xyz::openbmc_project::network::IP:: 273 convertAddressOriginFromString( 274 std::get<std::string>(properties.at("Origin"))); 275 if (origins.find(origin) == origins.end()) 276 { 277 continue; 278 } 279 280 if (idx > 0) 281 { 282 idx--; 283 continue; 284 } 285 286 IfAddr<family> ifaddr; 287 ifaddr.path = path; 288 ifaddr.address = *addr; 289 ifaddr.prefix = std::get<uint8_t>(properties.at("PrefixLength")); 290 ifaddr.origin = origin; 291 return ifaddr; 292 } 293 294 return std::nullopt; 295 } 296 /** @brief Trivial helper around findIfAddr that simplifies calls 297 * for one off lookups. Don't use this if you intend to do multiple 298 * lookups at a time. 299 * 300 * @param[in] bus - The bus object used for lookups 301 * @param[in] params - The parameters for the channel 302 * @param[in] idx - The index of the desired address on the interface 303 * @param[in] origins - The allowed origins for the address objects 304 * @return The address and prefix if it was found 305 */ 306 template <int family> 307 auto getIfAddr( 308 sdbusplus::bus_t& bus, const ChannelParams& params, uint8_t idx, 309 const std::unordered_set< 310 sdbusplus::server::xyz::openbmc_project::network::IP::AddressOrigin>& 311 origins) 312 { 313 ObjectLookupCache ips(bus, params, INTF_IP); 314 return findIfAddr<family>(bus, params, idx, origins, ips); 315 } 316 317 /** @brief Reconfigures the IPv6 address info configured for the interface 318 * 319 * @param[in] bus - The bus object used for lookups 320 * @param[in] params - The parameters for the channel 321 * @param[in] idx - The address index to operate on 322 * @param[in] address - The new address 323 * @param[in] prefix - The new address prefix 324 */ 325 void reconfigureIfAddr6(sdbusplus::bus_t& bus, const ChannelParams& params, 326 uint8_t idx, stdplus::In6Addr address, uint8_t prefix); 327 328 /** @brief Retrieves the current gateway for the address family on the system 329 * NOTE: The gateway is per channel instead of the system wide one. 330 * 331 * @param[in] bus - The bus object used for lookups 332 * @param[in] params - The parameters for the channel 333 * @return An address representing the gateway address if it exists 334 */ 335 template <int family> 336 std::optional<typename AddrFamily<family>::addr> 337 getGatewayProperty(sdbusplus::bus_t& bus, const ChannelParams& params) 338 { 339 auto objPath = "/xyz/openbmc_project/network/" + params.ifname; 340 auto gatewayStr = std::get<std::string>( 341 getDbusProperty(bus, params.service, objPath, INTF_ETHERNET, 342 AddrFamily<family>::propertyGateway)); 343 if (gatewayStr.empty()) 344 { 345 return std::nullopt; 346 } 347 return stdplus::fromStr<typename AddrFamily<family>::addr>(gatewayStr); 348 } 349 350 template <int family> 351 std::optional<IfNeigh<family>> findStaticNeighbor( 352 sdbusplus::bus_t&, const ChannelParams&, 353 typename AddrFamily<family>::addr ip, ObjectLookupCache& neighbors) 354 { 355 using sdbusplus::server::xyz::openbmc_project::network::Neighbor; 356 const auto state = 357 sdbusplus::common::xyz::openbmc_project::network::convertForMessage( 358 Neighbor::State::Permanent); 359 for (const auto& [path, neighbor] : neighbors) 360 { 361 std::optional<typename AddrFamily<family>::addr> neighIP; 362 try 363 { 364 neighIP.emplace(stdplus::fromStr<typename AddrFamily<family>::addr>( 365 std::get<std::string>(neighbor.at("IPAddress")))); 366 } 367 catch (...) 368 { 369 continue; 370 } 371 if (*neighIP != ip) 372 { 373 continue; 374 } 375 if (state != std::get<std::string>(neighbor.at("State"))) 376 { 377 continue; 378 } 379 380 IfNeigh<family> ret; 381 ret.path = path; 382 ret.ip = ip; 383 ret.mac = stdplus::fromStr<stdplus::EtherAddr>( 384 std::get<std::string>(neighbor.at("MACAddress"))); 385 return ret; 386 } 387 388 return std::nullopt; 389 } 390 391 template <int family> 392 void createNeighbor(sdbusplus::bus_t& bus, const ChannelParams& params, 393 typename AddrFamily<family>::addr address, 394 stdplus::EtherAddr mac) 395 { 396 auto newreq = 397 bus.new_method_call(params.service.c_str(), params.logicalPath.c_str(), 398 INTF_NEIGHBOR_CREATE_STATIC, "Neighbor"); 399 stdplus::ToStrHandle<stdplus::ToStr<stdplus::EtherAddr>> macToStr; 400 stdplus::ToStrHandle<stdplus::ToStr<typename AddrFamily<family>::addr>> 401 addrToStr; 402 newreq.append(addrToStr(address), macToStr(mac)); 403 bus.call_noreply(newreq); 404 } 405 406 /** @brief Deletes the dbus object. Ignores empty objects or objects that are 407 * missing from the bus. 408 * 409 * @param[in] bus - The bus object used for lookups 410 * @param[in] service - The name of the service 411 * @param[in] path - The path of the object to delete 412 */ 413 void deleteObjectIfExists(sdbusplus::bus_t& bus, const std::string& service, 414 const std::string& path); 415 416 /** @brief Sets the value for the default gateway of the channel 417 * 418 * @param[in] bus - The bus object used for lookups 419 * @param[in] params - The parameters for the channel 420 * @param[in] gateway - Gateway address to apply 421 */ 422 template <int family> 423 void setGatewayProperty(sdbusplus::bus_t& bus, const ChannelParams& params, 424 typename AddrFamily<family>::addr address) 425 { 426 // Save the old gateway MAC address if it exists so we can recreate it 427 auto gateway = getGatewayProperty<family>(bus, params); 428 std::optional<IfNeigh<family>> neighbor; 429 if (gateway) 430 { 431 ObjectLookupCache neighbors(bus, params, INTF_NEIGHBOR); 432 neighbor = findStaticNeighbor<family>(bus, params, *gateway, neighbors); 433 } 434 435 auto objPath = "/xyz/openbmc_project/network/" + params.ifname; 436 setDbusProperty(bus, params.service, objPath, INTF_ETHERNET, 437 AddrFamily<family>::propertyGateway, 438 stdplus::toStr(address)); 439 440 // Restore the gateway MAC if we had one 441 if (neighbor) 442 { 443 deleteObjectIfExists(bus, params.service, neighbor->path); 444 createNeighbor<family>(bus, params, address, neighbor->mac); 445 } 446 } 447 448 } // namespace transport 449 } // namespace ipmi 450