Add VLAN device binding
IPMI net channel supports the VLAN. This patch will bind the service
to VLAN device if the VLANID is set in net channel.
Tested:
In all the steps, use following commands to check if ipmi overlan is
still working and lan channel info is correct.
ipmitool -I lanplus ... mc info
ipmitool -I lanplus ... lan print 1
1. Start the phosphor-ipmi-net@eth0.service and this service binds to
eth0 device
# Command to get the binding device
journalctl -u phosphor-ipmi-net@eth0.service -o verbose | grep \
INTERFACE
INTERFACE=eth0
2. Set the VLANID (123) for channel 1 (eth0) and restart the service.
The service is binded to eth0.123
# Command to set the channel 1 VLANID
ipmitool -I lanplus ... lan set 1 vlan id 123
# Command to restart
systemctl restart phosphor-ipmi-net@eth0.service
# Command to check the binding
journalctl -u phosphor-ipmi-net@eth0.service -o verbose | grep \
INTERFACE
INTERFACE=eth0.123
3. Disable the VLANID for channel 0 and restart the service.
The service is binded to eth0
# Command to disable the channel 1 VLANID
ipmitool -I lanplus ... lan set 1 vlan id off
# Command to restart
systemctl restart phosphor-ipmi-net@eth0.service
# Command to check the binding
journalctl -u phosphor-ipmi-net@eth0.service -o verbose | grep \
INTERFACE
INTERFACE=eth0
Limitation: Need to restart this service when the VLANID is changed.
This should be done in phosphor-host-ipmid.
Change-Id: I6c05aacf6b18cb1fa0d1cabe6ad36f0d683948d1
Signed-off-by: Alvin Wang <alvinwang@msn.com>
diff --git a/sd_event_loop.cpp b/sd_event_loop.cpp
index 7a647cc..2ad5d2e 100644
--- a/sd_event_loop.cpp
+++ b/sd_event_loop.cpp
@@ -11,6 +11,7 @@
#include <boost/asio/io_context.hpp>
#include <phosphor-logging/log.hpp>
#include <sdbusplus/asio/sd_event.hpp>
+#include <user_channel/channel_layer.hpp>
namespace eventloop
{
@@ -46,13 +47,117 @@
});
}
-int EventLoop::setupSocket(std::shared_ptr<sdbusplus::asio::connection>& bus,
- std::string iface, uint16_t reqPort)
+int EventLoop::getVLANID(const std::string channel)
{
- static constexpr const char* unboundIface = "rmcpp";
- if (iface == "")
+ int vlanid = 0;
+ if (channel.empty())
{
- iface = unboundIface;
+ return 0;
+ }
+
+ sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
+ // Enumerate all VLAN + ETHERNET interfaces
+ auto req = bus.new_method_call(MAPPER_BUS_NAME, MAPPER_OBJ, MAPPER_INTF,
+ "GetSubTree");
+ req.append(PATH_ROOT, 0,
+ std::vector<std::string>{INTF_VLAN, INTF_ETHERNET});
+ ObjectTree objs;
+ try
+ {
+ auto reply = bus.call(req);
+ reply.read(objs);
+ }
+ catch (std::exception& e)
+ {
+ log<level::ERR>("getVLANID: failed to execute/read GetSubTree");
+ return 0;
+ }
+
+ std::string ifService, logicalPath;
+ for (const auto& [path, impls] : objs)
+ {
+ if (path.find(channel) == path.npos)
+ {
+ continue;
+ }
+ for (const auto& [service, intfs] : impls)
+ {
+ bool vlan = false;
+ bool ethernet = false;
+ for (const auto& intf : intfs)
+ {
+ if (intf == INTF_VLAN)
+ {
+ vlan = true;
+ }
+ else if (intf == INTF_ETHERNET)
+ {
+ ethernet = true;
+ }
+ }
+ if (ifService.empty() && (vlan || ethernet))
+ {
+ ifService = service;
+ }
+ if (logicalPath.empty() && vlan)
+ {
+ logicalPath = path;
+ }
+ }
+ }
+
+ // VLAN devices will always have a separate logical object
+ if (logicalPath.empty())
+ {
+ return 0;
+ }
+
+ Value value;
+ auto method = bus.new_method_call(ifService.c_str(), logicalPath.c_str(),
+ PROP_INTF, METHOD_GET);
+ method.append(INTF_VLAN, "Id");
+ try
+ {
+ auto method_reply = bus.call(method);
+ method_reply.read(value);
+ }
+ catch (std::exception& e)
+ {
+ log<level::ERR>("getVLANID: failed to execute/read VLAN Id");
+ return 0;
+ }
+
+ vlanid = std::get<uint32_t>(value);
+ if ((vlanid & VLAN_VALUE_MASK) != vlanid)
+ {
+ log<level::ERR>("networkd returned an invalid vlan",
+ entry("VLAN=%", vlanid));
+ return 0;
+ }
+
+ return vlanid;
+}
+
+int EventLoop::setupSocket(std::shared_ptr<sdbusplus::asio::connection>& bus,
+ std::string channel, uint16_t reqPort)
+{
+ std::string iface = channel;
+ static constexpr const char* unboundIface = "rmcpp";
+ if (channel == "")
+ {
+ iface = channel = unboundIface;
+ }
+ else
+ {
+ // If VLANID of this channel is set, bind the socket to this
+ // VLAN logic device
+ auto vlanid = getVLANID(channel);
+ if (vlanid)
+ {
+ iface = iface + "." + std::to_string(vlanid);
+ log<level::DEBUG>("This channel has VLAN id",
+ entry("VLANID=%d", vlanid));
+ }
}
// Create our own socket if SysD did not supply one.
int listensFdCount = sd_listen_fds(0);
@@ -104,6 +209,8 @@
entry("ERROR=%s", strerror(errno)));
return EXIT_FAILURE;
}
+ log<level::INFO>("Bind to interfae",
+ entry("INTERFACE=%s", iface.c_str()));
}
// cannot be constexpr because it gets passed by address
const int option_enabled = 1;
@@ -114,7 +221,7 @@
&option_enabled, sizeof(option_enabled));
// set the dbus name
- std::string busName = "xyz.openbmc_project.Ipmi.Channel." + iface;
+ std::string busName = "xyz.openbmc_project.Ipmi.Channel." + channel;
try
{
bus->request_name(busName.c_str());
diff --git a/sd_event_loop.hpp b/sd_event_loop.hpp
index 54b2946..9729078 100644
--- a/sd_event_loop.hpp
+++ b/sd_event_loop.hpp
@@ -19,6 +19,23 @@
namespace eventloop
{
+using DbusObjectPath = std::string;
+using DbusService = std::string;
+using DbusInterface = std::string;
+using ObjectTree =
+ std::map<DbusObjectPath, std::map<DbusService, std::vector<DbusInterface>>>;
+using Value = std::variant<bool, uint8_t, int16_t, uint16_t, int32_t, uint32_t,
+ int64_t, uint64_t, double, std::string>;
+// VLANs are a 12-bit value
+constexpr uint16_t VLAN_VALUE_MASK = 0x0fff;
+constexpr auto MAPPER_BUS_NAME = "xyz.openbmc_project.ObjectMapper";
+constexpr auto MAPPER_OBJ = "/xyz/openbmc_project/object_mapper";
+constexpr auto MAPPER_INTF = "xyz.openbmc_project.ObjectMapper";
+constexpr auto PATH_ROOT = "/xyz/openbmc_project/network";
+constexpr auto INTF_VLAN = "xyz.openbmc_project.Network.VLAN";
+constexpr auto INTF_ETHERNET = "xyz.openbmc_project.Network.EthernetInterface";
+constexpr auto METHOD_GET = "Get";
+constexpr auto PROP_INTF = "org.freedesktop.DBus.Properties";
class EventLoop
{
@@ -54,6 +71,9 @@
/** @brief register the async handler for incoming udp packets */
void startRmcpReceive();
+ /** @brief get vlanid */
+ int getVLANID(const std::string channel);
+
/** @brief boost::asio io context to run with
*/
std::shared_ptr<boost::asio::io_context> io;