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