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>
channelCall(uint8_t channel,Args &&...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 */
ObjectLookupCache(sdbusplus::bus_t & bus,const ChannelParams & params,const char * intf)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
iterator(ObjectTree::const_iterator it,ObjectLookupCache & container)185 iterator(ObjectTree::const_iterator it, ObjectLookupCache& container) :
186 ObjectTree::const_iterator(it), container(container),
187 ret(container.cache.end())
188 {}
operator *()189 value_type& operator*()
190 {
191 ret = container.get(ObjectTree::const_iterator::operator*().first);
192 return *ret;
193 }
operator ->()194 value_type* operator->()
195 {
196 return &operator*();
197 }
198
199 private:
200 ObjectLookupCache& container;
201 PropertiesCache::iterator ret;
202 };
203
begin()204 iterator begin() noexcept
205 {
206 return iterator(objs.begin(), *this);
207 }
208
end()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 */
get(const std::string & path)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>
findIfAddr(sdbusplus::bus_t & bus,const ChannelParams & params,uint8_t idx,const std::unordered_set<sdbusplus::server::xyz::openbmc_project::network::IP::AddressOrigin> & origins,ObjectLookupCache & ips)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>
getIfAddr(sdbusplus::bus_t & bus,const ChannelParams & params,uint8_t idx,const std::unordered_set<sdbusplus::server::xyz::openbmc_project::network::IP::AddressOrigin> & origins)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>
getGatewayProperty(sdbusplus::bus_t & bus,const ChannelParams & params)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>>
findStaticNeighbor(sdbusplus::bus_t &,const ChannelParams &,typename AddrFamily<family>::addr ip,ObjectLookupCache & neighbors)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>
createNeighbor(sdbusplus::bus_t & bus,const ChannelParams & params,typename AddrFamily<family>::addr address,stdplus::EtherAddr mac)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>
setGatewayProperty(sdbusplus::bus_t & bus,const ChannelParams & params,typename AddrFamily<family>::addr address)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