transporthandler: Support Gateway MAC
Adds support for setting the MAC address of the gateway. Most of the
interesting code in this change is around saving / restoring the gateway
MAC address when the gateway or interface change.
Change-Id: I85b7c665c44af4f030f51456be355f3eb11ab2fc
Signed-off-by: William A. Kennington III <wak@google.com>
diff --git a/transporthandler.cpp b/transporthandler.cpp
index b48cbd0..777171c 100644
--- a/transporthandler.cpp
+++ b/transporthandler.cpp
@@ -31,6 +31,7 @@
#include <vector>
#include <xyz/openbmc_project/Common/error.hpp>
#include <xyz/openbmc_project/Network/IP/server.hpp>
+#include <xyz/openbmc_project/Network/Neighbor/server.hpp>
using phosphor::logging::commit;
using phosphor::logging::elog;
@@ -39,6 +40,7 @@
using phosphor::logging::log;
using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
using sdbusplus::xyz::openbmc_project::Network::server::IP;
+using sdbusplus::xyz::openbmc_project::Network::server::Neighbor;
namespace cipher
{
@@ -97,6 +99,9 @@
constexpr auto INTF_IP = "xyz.openbmc_project.Network.IP";
constexpr auto INTF_IP_CREATE = "xyz.openbmc_project.Network.IP.Create";
constexpr auto INTF_MAC = "xyz.openbmc_project.Network.MACAddress";
+constexpr auto INTF_NEIGHBOR = "xyz.openbmc_project.Network.Neighbor";
+constexpr auto INTF_NEIGHBOR_CREATE_STATIC =
+ "xyz.openbmc_project.Network.Neighbor.CreateStatic";
constexpr auto INTF_VLAN = "xyz.openbmc_project.Network.VLAN";
constexpr auto INTF_VLAN_CREATE = "xyz.openbmc_project.Network.VLAN.Create";
@@ -133,6 +138,15 @@
uint8_t prefix;
};
+/** @brief Interface Neighbor configuration parameters */
+template <int family>
+struct IfNeigh
+{
+ std::string path;
+ typename AddrFamily<family>::addr ip;
+ ether_addr mac;
+};
+
/** @brief IPMI LAN Parameters */
enum class LanParam : uint8_t
{
@@ -144,6 +158,7 @@
MAC = 5,
SubnetMask = 6,
Gateway1 = 12,
+ Gateway1MAC = 13,
VLANId = 20,
CiphersuiteSupport = 22,
CiphersuiteEntries = 23,
@@ -167,6 +182,19 @@
Commit = 2,
};
+/** @brief A trivial helper used to determine if two PODs are equal
+ *
+ * @params[in] a - The first object to compare
+ * @params[in] b - The second object to compare
+ * @return True if the objects are the same bytewise
+ */
+template <typename T>
+bool equal(const T& a, const T& b)
+{
+ static_assert(std::is_trivially_copyable_v<T>);
+ return std::memcmp(&a, &b, sizeof(T)) == 0;
+}
+
/** @brief Copies bytes from an array into a trivially copyable container
*
* @params[out] t - The container receiving the data
@@ -485,21 +513,6 @@
return stringToAddr<family>(gatewayStr.c_str());
}
-/** @brief Sets the system wide value for the default gateway
- *
- * @param[in] bus - The bus object used for lookups
- * @param[in] params - The parameters for the channel
- * @param[in] gateway - Gateway address to apply
- */
-template <int family>
-void setGatewayProperty(sdbusplus::bus::bus& bus, const ChannelParams& params,
- const typename AddrFamily<family>::addr& address)
-{
- setDbusProperty(bus, params.service, PATH_SYSTEMCONFIG, INTF_SYSTEMCONFIG,
- AddrFamily<family>::propertyGateway,
- addrToString<family>(address));
-}
-
/** @brief A lazy lookup mechanism for iterating over object properties stored
* in DBus. This will only perform the object lookup when needed, and
* retains a cache of previous lookups to speed up future iterations.
@@ -747,6 +760,131 @@
prefix.value_or(fallbackPrefix));
}
+template <int family>
+std::optional<IfNeigh<family>>
+ findStaticNeighbor(sdbusplus::bus::bus& bus, const ChannelParams& params,
+ const typename AddrFamily<family>::addr& ip,
+ ObjectLookupCache& neighbors)
+{
+ const auto state =
+ sdbusplus::xyz::openbmc_project::Network::server::convertForMessage(
+ Neighbor::State::Permanent);
+ for (const auto& [path, neighbor] : neighbors)
+ {
+ const auto& ipStr = std::get<std::string>(neighbor.at("IPAddress"));
+ auto neighIP = maybeStringToAddr<family>(ipStr.c_str());
+ if (!neighIP)
+ {
+ continue;
+ }
+ if (!equal(*neighIP, ip))
+ {
+ continue;
+ }
+ if (state != std::get<std::string>(neighbor.at("State")))
+ {
+ continue;
+ }
+
+ IfNeigh<family> ret;
+ ret.path = path;
+ ret.ip = ip;
+ const auto& macStr = std::get<std::string>(neighbor.at("MACAddress"));
+ ret.mac = stringToMAC(macStr.c_str());
+ return std::move(ret);
+ }
+
+ return std::nullopt;
+}
+
+template <int family>
+void createNeighbor(sdbusplus::bus::bus& bus, const ChannelParams& params,
+ const typename AddrFamily<family>::addr& address,
+ const ether_addr& mac)
+{
+ auto newreq =
+ bus.new_method_call(params.service.c_str(), params.logicalPath.c_str(),
+ INTF_NEIGHBOR_CREATE_STATIC, "Neighbor");
+ std::string macStr = ether_ntoa(&mac);
+ newreq.append(addrToString<family>(address), macStr);
+ bus.call_noreply(newreq);
+}
+
+/** @brief Sets the system wide value for the default gateway
+ *
+ * @param[in] bus - The bus object used for lookups
+ * @param[in] params - The parameters for the channel
+ * @param[in] gateway - Gateway address to apply
+ */
+template <int family>
+void setGatewayProperty(sdbusplus::bus::bus& bus, const ChannelParams& params,
+ const typename AddrFamily<family>::addr& address)
+{
+ // Save the old gateway MAC address if it exists so we can recreate it
+ auto gateway = getGatewayProperty<family>(bus, params);
+ std::optional<IfNeigh<family>> neighbor;
+ if (gateway)
+ {
+ ObjectLookupCache neighbors(bus, params, INTF_NEIGHBOR);
+ neighbor = findStaticNeighbor<family>(bus, params, *gateway, neighbors);
+ }
+
+ setDbusProperty(bus, params.service, PATH_SYSTEMCONFIG, INTF_SYSTEMCONFIG,
+ AddrFamily<family>::propertyGateway,
+ addrToString<family>(address));
+
+ // Restore the gateway MAC if we had one
+ if (neighbor)
+ {
+ deleteObjectIfExists(bus, params.service, neighbor->path);
+ createNeighbor<family>(bus, params, address, neighbor->mac);
+ }
+}
+
+template <int family>
+std::optional<IfNeigh<family>> findGatewayNeighbor(sdbusplus::bus::bus& bus,
+ const ChannelParams& params,
+ ObjectLookupCache& neighbors)
+{
+ auto gateway = getGatewayProperty<family>(bus, params);
+ if (!gateway)
+ {
+ return std::nullopt;
+ }
+
+ return findStaticNeighbor<family>(bus, params, *gateway, neighbors);
+}
+
+template <int family>
+std::optional<IfNeigh<family>> getGatewayNeighbor(sdbusplus::bus::bus& bus,
+ const ChannelParams& params)
+{
+ ObjectLookupCache neighbors(bus, params, INTF_NEIGHBOR);
+ return findGatewayNeighbor<family>(bus, params, neighbors);
+}
+
+template <int family>
+void reconfigureGatewayMAC(sdbusplus::bus::bus& bus,
+ const ChannelParams& params, const ether_addr& mac)
+{
+ auto gateway = getGatewayProperty<family>(bus, params);
+ if (!gateway)
+ {
+ log<level::ERR>("Tried to set Gateway MAC without Gateway");
+ elog<InternalFailure>();
+ }
+
+ ObjectLookupCache neighbors(bus, params, INTF_NEIGHBOR);
+ auto neighbor =
+ findStaticNeighbor<family>(bus, params, *gateway, neighbors);
+ if (neighbor)
+ {
+ deleteObjectIfExists(bus, params.service, neighbor->path);
+ }
+
+ createNeighbor<family>(bus, params, *gateway, mac);
+}
+
/** @brief Gets the vlan ID configured on the interface
*
* @param[in] bus - The bus object used for lookups
@@ -847,6 +985,8 @@
ObjectLookupCache ips(bus, params, INTF_IP);
auto ifaddr4 = findIfAddr<AF_INET>(bus, params, 0, originsV4, ips);
auto dhcp = getDHCPProperty(bus, params);
+ ObjectLookupCache neighbors(bus, params, INTF_NEIGHBOR);
+ auto neighbor4 = findGatewayNeighbor<AF_INET>(bus, params, neighbors);
deconfigureChannel(bus, params);
createVLAN(bus, params, vlan);
@@ -857,6 +997,10 @@
{
createIfAddr<AF_INET>(bus, params, ifaddr4->address, ifaddr4->prefix);
}
+ if (neighbor4)
+ {
+ createNeighbor<AF_INET>(bus, params, neighbor4->ip, neighbor4->mac);
+ }
}
/** @brief Turns a prefix into a netmask
@@ -1055,6 +1199,18 @@
channelCall<setGatewayProperty<AF_INET>>(channel, gateway);
return responseSuccess();
}
+ case LanParam::Gateway1MAC:
+ {
+ ether_addr gatewayMAC;
+ std::array<uint8_t, sizeof(gatewayMAC)> bytes;
+ if (req.unpack(bytes) != 0 || !req.fullyUnpacked())
+ {
+ return responseReqDataLenInvalid();
+ }
+ copyInto(gatewayMAC, bytes);
+ channelCall<reconfigureGatewayMAC<AF_INET>>(channel, gatewayMAC);
+ return responseSuccess();
+ }
case LanParam::VLANId:
{
uint16_t vlanData;
@@ -1193,6 +1349,17 @@
ret.pack(dataRef(gateway));
return responseSuccess(std::move(ret));
}
+ case LanParam::Gateway1MAC:
+ {
+ ether_addr mac{};
+ auto neighbor = channelCall<getGatewayNeighbor<AF_INET>>(channel);
+ if (neighbor)
+ {
+ mac = neighbor->mac;
+ }
+ ret.pack(dataRef(mac));
+ return responseSuccess(std::move(ret));
+ }
case LanParam::VLANId:
{
uint16_t vlan = channelCall<getVLANProperty>(channel);