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> maybeGetChannelParams(sdbusplus::bus_t& bus, 69 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), 176 params(params), intf(intf), 177 objs(getAllDbusObjects(bus, params.logicalPath, intf, "")) 178 {} 179 180 class iterator : public ObjectTree::const_iterator 181 { 182 public: 183 using value_type = PropertiesCache::value_type; 184 185 iterator(ObjectTree::const_iterator it, ObjectLookupCache& container) : 186 ObjectTree::const_iterator(it), container(container), 187 ret(container.cache.end()) 188 {} 189 value_type& operator*() 190 { 191 ret = container.get(ObjectTree::const_iterator::operator*().first); 192 return *ret; 193 } 194 value_type* operator->() 195 { 196 return &operator*(); 197 } 198 199 private: 200 ObjectLookupCache& container; 201 PropertiesCache::iterator ret; 202 }; 203 204 iterator begin() noexcept 205 { 206 return iterator(objs.begin(), *this); 207 } 208 209 iterator end() noexcept 210 { 211 return iterator(objs.end(), *this); 212 } 213 214 private: 215 sdbusplus::bus_t& bus; 216 const ChannelParams& params; 217 const char* const intf; 218 const ObjectTree objs; 219 PropertiesCache cache; 220 221 /** @brief Gets a cached copy of the object properties if possible 222 * Otherwise performs a query on DBus to look them up 223 * 224 * @param[in] path - The object path to lookup 225 * @return An iterator for the specified object path + properties 226 */ 227 PropertiesCache::iterator get(const std::string& path) 228 { 229 auto it = cache.find(path); 230 if (it != cache.end()) 231 { 232 return it; 233 } 234 auto properties = getAllDbusProperties(bus, params.service, path, intf); 235 return cache.insert({path, std::move(properties)}).first; 236 } 237 }; 238 239 /** @brief Searches the ip object lookup cache for an address matching 240 * the input parameters. NOTE: The index lacks stability across address 241 * changes since the network daemon has no notion of stable indicies. 242 * 243 * @param[in] bus - The bus object used for lookups 244 * @param[in] params - The parameters for the channel 245 * @param[in] idx - The index of the desired address on the interface 246 * @param[in] origins - The allowed origins for the address objects 247 * @param[in] ips - The object lookup cache holding all of the address info 248 * @return The address and prefix if it was found 249 */ 250 template <int family> 251 std::optional<IfAddr<family>> findIfAddr( 252 [[maybe_unused]] sdbusplus::bus_t& bus, 253 [[maybe_unused]] const ChannelParams& params, uint8_t idx, 254 const std::unordered_set< 255 sdbusplus::server::xyz::openbmc_project::network::IP::AddressOrigin>& 256 origins, 257 ObjectLookupCache& ips) 258 { 259 for (const auto& [path, properties] : ips) 260 { 261 std::optional<typename AddrFamily<family>::addr> addr; 262 try 263 { 264 addr.emplace(stdplus::fromStr<typename AddrFamily<family>::addr>( 265 std::get<std::string>(properties.at("Address")))); 266 } 267 catch (...) 268 { 269 continue; 270 } 271 272 sdbusplus::server::xyz::openbmc_project::network::IP::AddressOrigin 273 origin = sdbusplus::server::xyz::openbmc_project::network::IP:: 274 convertAddressOriginFromString( 275 std::get<std::string>(properties.at("Origin"))); 276 if (origins.find(origin) == origins.end()) 277 { 278 continue; 279 } 280 281 if (idx > 0) 282 { 283 idx--; 284 continue; 285 } 286 287 IfAddr<family> ifaddr; 288 ifaddr.path = path; 289 ifaddr.address = *addr; 290 ifaddr.prefix = std::get<uint8_t>(properties.at("PrefixLength")); 291 ifaddr.origin = origin; 292 return ifaddr; 293 } 294 295 return std::nullopt; 296 } 297 /** @brief Trivial helper around findIfAddr that simplifies calls 298 * for one off lookups. Don't use this if you intend to do multiple 299 * lookups at a time. 300 * 301 * @param[in] bus - The bus object used for lookups 302 * @param[in] params - The parameters for the channel 303 * @param[in] idx - The index of the desired address on the interface 304 * @param[in] origins - The allowed origins for the address objects 305 * @return The address and prefix if it was found 306 */ 307 template <int family> 308 auto getIfAddr( 309 sdbusplus::bus_t& bus, const ChannelParams& params, uint8_t idx, 310 const std::unordered_set< 311 sdbusplus::server::xyz::openbmc_project::network::IP::AddressOrigin>& 312 origins) 313 { 314 ObjectLookupCache ips(bus, params, INTF_IP); 315 return findIfAddr<family>(bus, params, idx, origins, ips); 316 } 317 318 /** @brief Reconfigures the IPv6 address info configured for the interface 319 * 320 * @param[in] bus - The bus object used for lookups 321 * @param[in] params - The parameters for the channel 322 * @param[in] idx - The address index to operate on 323 * @param[in] address - The new address 324 * @param[in] prefix - The new address prefix 325 */ 326 void reconfigureIfAddr6(sdbusplus::bus_t& bus, const ChannelParams& params, 327 uint8_t idx, stdplus::In6Addr address, uint8_t prefix); 328 329 /** @brief Retrieves the current gateway for the address family on the system 330 * NOTE: The gateway is per channel instead of the system wide one. 331 * 332 * @param[in] bus - The bus object used for lookups 333 * @param[in] params - The parameters for the channel 334 * @return An address representing the gateway address if it exists 335 */ 336 template <int family> 337 std::optional<typename AddrFamily<family>::addr> 338 getGatewayProperty(sdbusplus::bus_t& bus, const ChannelParams& params) 339 { 340 auto objPath = "/xyz/openbmc_project/network/" + params.ifname; 341 auto gatewayStr = std::get<std::string>( 342 getDbusProperty(bus, params.service, objPath, INTF_ETHERNET, 343 AddrFamily<family>::propertyGateway)); 344 if (gatewayStr.empty()) 345 { 346 return std::nullopt; 347 } 348 return stdplus::fromStr<typename AddrFamily<family>::addr>(gatewayStr); 349 } 350 351 template <int family> 352 std::optional<IfNeigh<family>> 353 findStaticNeighbor(sdbusplus::bus_t&, const ChannelParams&, 354 typename AddrFamily<family>::addr ip, 355 ObjectLookupCache& neighbors) 356 { 357 using sdbusplus::server::xyz::openbmc_project::network::Neighbor; 358 const auto state = 359 sdbusplus::common::xyz::openbmc_project::network::convertForMessage( 360 Neighbor::State::Permanent); 361 for (const auto& [path, neighbor] : neighbors) 362 { 363 std::optional<typename AddrFamily<family>::addr> neighIP; 364 try 365 { 366 neighIP.emplace(stdplus::fromStr<typename AddrFamily<family>::addr>( 367 std::get<std::string>(neighbor.at("IPAddress")))); 368 } 369 catch (...) 370 { 371 continue; 372 } 373 if (*neighIP != ip) 374 { 375 continue; 376 } 377 if (state != std::get<std::string>(neighbor.at("State"))) 378 { 379 continue; 380 } 381 382 IfNeigh<family> ret; 383 ret.path = path; 384 ret.ip = ip; 385 ret.mac = stdplus::fromStr<stdplus::EtherAddr>( 386 std::get<std::string>(neighbor.at("MACAddress"))); 387 return ret; 388 } 389 390 return std::nullopt; 391 } 392 393 template <int family> 394 void createNeighbor(sdbusplus::bus_t& bus, const ChannelParams& params, 395 typename AddrFamily<family>::addr address, 396 stdplus::EtherAddr mac) 397 { 398 auto newreq = bus.new_method_call(params.service.c_str(), 399 params.logicalPath.c_str(), 400 INTF_NEIGHBOR_CREATE_STATIC, "Neighbor"); 401 stdplus::ToStrHandle<stdplus::ToStr<stdplus::EtherAddr>> macToStr; 402 stdplus::ToStrHandle<stdplus::ToStr<typename AddrFamily<family>::addr>> 403 addrToStr; 404 newreq.append(addrToStr(address), macToStr(mac)); 405 bus.call_noreply(newreq); 406 } 407 408 /** @brief Deletes the dbus object. Ignores empty objects or objects that are 409 * missing from the bus. 410 * 411 * @param[in] bus - The bus object used for lookups 412 * @param[in] service - The name of the service 413 * @param[in] path - The path of the object to delete 414 */ 415 void deleteObjectIfExists(sdbusplus::bus_t& bus, const std::string& service, 416 const std::string& path); 417 418 /** @brief Sets the value for the default gateway of the channel 419 * 420 * @param[in] bus - The bus object used for lookups 421 * @param[in] params - The parameters for the channel 422 * @param[in] gateway - Gateway address to apply 423 */ 424 template <int family> 425 void setGatewayProperty(sdbusplus::bus_t& bus, const ChannelParams& params, 426 typename AddrFamily<family>::addr address) 427 { 428 // Save the old gateway MAC address if it exists so we can recreate it 429 auto gateway = getGatewayProperty<family>(bus, params); 430 std::optional<IfNeigh<family>> neighbor; 431 if (gateway) 432 { 433 ObjectLookupCache neighbors(bus, params, INTF_NEIGHBOR); 434 neighbor = findStaticNeighbor<family>(bus, params, *gateway, neighbors); 435 } 436 437 auto objPath = "/xyz/openbmc_project/network/" + params.ifname; 438 setDbusProperty(bus, params.service, objPath, INTF_ETHERNET, 439 AddrFamily<family>::propertyGateway, 440 stdplus::toStr(address)); 441 442 // Restore the gateway MAC if we had one 443 if (neighbor) 444 { 445 deleteObjectIfExists(bus, params.service, neighbor->path); 446 createNeighbor<family>(bus, params, address, neighbor->mac); 447 } 448 } 449 450 } // namespace transport 451 } // namespace ipmi 452