19e801a2bSVernon Mauery #include "sd_event_loop.hpp"
29e801a2bSVernon Mauery
39e801a2bSVernon Mauery #include "main.hpp"
49e801a2bSVernon Mauery #include "message_handler.hpp"
59e801a2bSVernon Mauery
67b7f25f7SGeorge Liu #include <error.h>
72c15f0c3SDave Cobbley #include <netinet/in.h>
87fd26ddbSTom Joseph #include <sys/ioctl.h>
92c15f0c3SDave Cobbley #include <sys/socket.h>
107fd26ddbSTom Joseph #include <systemd/sd-daemon.h>
119e801a2bSVernon Mauery
12cbccb05aSVernon Mauery #include <boost/asio/io_context.hpp>
1307bb0951SEd Tanous #include <boost/asio/signal_set.hpp>
147b7f25f7SGeorge Liu #include <phosphor-logging/lg2.hpp>
158c0bf983SAlvin Wang #include <user_channel/channel_layer.hpp>
167fd26ddbSTom Joseph
177fd26ddbSTom Joseph namespace eventloop
187fd26ddbSTom Joseph {
197fd26ddbSTom Joseph
handleRmcpPacket()207a0142c5SVernon Mauery void EventLoop::handleRmcpPacket()
217fd26ddbSTom Joseph {
227fd26ddbSTom Joseph try
237fd26ddbSTom Joseph {
247a0142c5SVernon Mauery auto channelPtr = std::make_shared<udpsocket::Channel>(udpSocket);
257fd26ddbSTom Joseph
267fd26ddbSTom Joseph // Initialize the Message Handler with the socket channel
278d6f200cSVernon Mauery auto msgHandler = std::make_shared<message::Handler>(channelPtr, io);
287fd26ddbSTom Joseph
298d6f200cSVernon Mauery msgHandler->processIncoming();
307fd26ddbSTom Joseph }
317a0142c5SVernon Mauery catch (const std::exception& e)
327fd26ddbSTom Joseph {
337b7f25f7SGeorge Liu lg2::error("Executing the IPMI message failed: {ERROR}", "ERROR", e);
347a0142c5SVernon Mauery }
357fd26ddbSTom Joseph }
367fd26ddbSTom Joseph
startRmcpReceive()377a0142c5SVernon Mauery void EventLoop::startRmcpReceive()
387a0142c5SVernon Mauery {
398425624aSPatrick Williams udpSocket->async_wait(
408425624aSPatrick Williams boost::asio::socket_base::wait_read,
417a0142c5SVernon Mauery [this](const boost::system::error_code& ec) {
427a0142c5SVernon Mauery if (!ec)
437a0142c5SVernon Mauery {
44099fb097SPatrick Williams boost::asio::post(*io, [this]() { startRmcpReceive(); });
457a0142c5SVernon Mauery handleRmcpPacket();
467a0142c5SVernon Mauery }
477a0142c5SVernon Mauery });
487fd26ddbSTom Joseph }
497fd26ddbSTom Joseph
getVLANID(const std::string channel)508c0bf983SAlvin Wang int EventLoop::getVLANID(const std::string channel)
518c0bf983SAlvin Wang {
528c0bf983SAlvin Wang int vlanid = 0;
538c0bf983SAlvin Wang if (channel.empty())
548c0bf983SAlvin Wang {
558c0bf983SAlvin Wang return 0;
568c0bf983SAlvin Wang }
578c0bf983SAlvin Wang
580a59062cSPatrick Williams sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()};
598c0bf983SAlvin Wang // Enumerate all VLAN + ETHERNET interfaces
608c0bf983SAlvin Wang auto req = bus.new_method_call(MAPPER_BUS_NAME, MAPPER_OBJ, MAPPER_INTF,
618c0bf983SAlvin Wang "GetSubTree");
628c0bf983SAlvin Wang req.append(PATH_ROOT, 0,
638c0bf983SAlvin Wang std::vector<std::string>{INTF_VLAN, INTF_ETHERNET});
648c0bf983SAlvin Wang ObjectTree objs;
658c0bf983SAlvin Wang try
668c0bf983SAlvin Wang {
678c0bf983SAlvin Wang auto reply = bus.call(req);
688c0bf983SAlvin Wang reply.read(objs);
698c0bf983SAlvin Wang }
7012d199b2SPatrick Williams catch (const std::exception& e)
718c0bf983SAlvin Wang {
727b7f25f7SGeorge Liu lg2::error("getVLANID: failed to execute/read GetSubTree: {ERROR}",
737b7f25f7SGeorge Liu "ERROR", e);
748c0bf983SAlvin Wang return 0;
758c0bf983SAlvin Wang }
768c0bf983SAlvin Wang
778c0bf983SAlvin Wang std::string ifService, logicalPath;
788c0bf983SAlvin Wang for (const auto& [path, impls] : objs)
798c0bf983SAlvin Wang {
808c0bf983SAlvin Wang if (path.find(channel) == path.npos)
818c0bf983SAlvin Wang {
828c0bf983SAlvin Wang continue;
838c0bf983SAlvin Wang }
848c0bf983SAlvin Wang for (const auto& [service, intfs] : impls)
858c0bf983SAlvin Wang {
868c0bf983SAlvin Wang bool vlan = false;
878c0bf983SAlvin Wang bool ethernet = false;
888c0bf983SAlvin Wang for (const auto& intf : intfs)
898c0bf983SAlvin Wang {
908c0bf983SAlvin Wang if (intf == INTF_VLAN)
918c0bf983SAlvin Wang {
928c0bf983SAlvin Wang vlan = true;
938c0bf983SAlvin Wang }
948c0bf983SAlvin Wang else if (intf == INTF_ETHERNET)
958c0bf983SAlvin Wang {
968c0bf983SAlvin Wang ethernet = true;
978c0bf983SAlvin Wang }
988c0bf983SAlvin Wang }
998c0bf983SAlvin Wang if (ifService.empty() && (vlan || ethernet))
1008c0bf983SAlvin Wang {
1018c0bf983SAlvin Wang ifService = service;
1028c0bf983SAlvin Wang }
1038c0bf983SAlvin Wang if (logicalPath.empty() && vlan)
1048c0bf983SAlvin Wang {
1058c0bf983SAlvin Wang logicalPath = path;
1068c0bf983SAlvin Wang }
1078c0bf983SAlvin Wang }
1088c0bf983SAlvin Wang }
1098c0bf983SAlvin Wang
1108c0bf983SAlvin Wang // VLAN devices will always have a separate logical object
1118c0bf983SAlvin Wang if (logicalPath.empty())
1128c0bf983SAlvin Wang {
1138c0bf983SAlvin Wang return 0;
1148c0bf983SAlvin Wang }
1158c0bf983SAlvin Wang
1168c0bf983SAlvin Wang Value value;
1178c0bf983SAlvin Wang auto method = bus.new_method_call(ifService.c_str(), logicalPath.c_str(),
1188c0bf983SAlvin Wang PROP_INTF, METHOD_GET);
1198c0bf983SAlvin Wang method.append(INTF_VLAN, "Id");
1208c0bf983SAlvin Wang try
1218c0bf983SAlvin Wang {
1228c0bf983SAlvin Wang auto method_reply = bus.call(method);
1238c0bf983SAlvin Wang method_reply.read(value);
1248c0bf983SAlvin Wang }
12512d199b2SPatrick Williams catch (const std::exception& e)
1268c0bf983SAlvin Wang {
1277b7f25f7SGeorge Liu lg2::error("getVLANID: failed to execute/read VLAN Id: {ERROR}",
1287b7f25f7SGeorge Liu "ERROR", e);
1298c0bf983SAlvin Wang return 0;
1308c0bf983SAlvin Wang }
1318c0bf983SAlvin Wang
1328c0bf983SAlvin Wang vlanid = std::get<uint32_t>(value);
1338c0bf983SAlvin Wang if ((vlanid & VLAN_VALUE_MASK) != vlanid)
1348c0bf983SAlvin Wang {
1357b7f25f7SGeorge Liu lg2::error("networkd returned an invalid vlan: {VLAN}", "VLAN", vlanid);
1368c0bf983SAlvin Wang return 0;
1378c0bf983SAlvin Wang }
1388c0bf983SAlvin Wang
1398c0bf983SAlvin Wang return vlanid;
1408c0bf983SAlvin Wang }
1418c0bf983SAlvin Wang
setupSocket(std::shared_ptr<sdbusplus::asio::connection> & bus,std::string channel,uint16_t reqPort)142d92bc324SVernon Mauery int EventLoop::setupSocket(std::shared_ptr<sdbusplus::asio::connection>& bus,
1438c0bf983SAlvin Wang std::string channel, uint16_t reqPort)
144d92bc324SVernon Mauery {
1458c0bf983SAlvin Wang std::string iface = channel;
146d92bc324SVernon Mauery static constexpr const char* unboundIface = "rmcpp";
1478c0bf983SAlvin Wang if (channel == "")
148d92bc324SVernon Mauery {
1498c0bf983SAlvin Wang iface = channel = unboundIface;
1508c0bf983SAlvin Wang }
1518c0bf983SAlvin Wang else
1528c0bf983SAlvin Wang {
1538c0bf983SAlvin Wang // If VLANID of this channel is set, bind the socket to this
1548c0bf983SAlvin Wang // VLAN logic device
1558c0bf983SAlvin Wang auto vlanid = getVLANID(channel);
1568c0bf983SAlvin Wang if (vlanid)
1578c0bf983SAlvin Wang {
1588c0bf983SAlvin Wang iface = iface + "." + std::to_string(vlanid);
1597b7f25f7SGeorge Liu lg2::debug("This channel has VLAN id: {VLAN}", "VLAN", vlanid);
1608c0bf983SAlvin Wang }
161d92bc324SVernon Mauery }
162d92bc324SVernon Mauery // Create our own socket if SysD did not supply one.
163d92bc324SVernon Mauery int listensFdCount = sd_listen_fds(0);
164d92bc324SVernon Mauery if (listensFdCount > 1)
165d92bc324SVernon Mauery {
1667b7f25f7SGeorge Liu lg2::error("Too many file descriptors received, listensFdCount: {FD}",
1677b7f25f7SGeorge Liu "FD", listensFdCount);
168d92bc324SVernon Mauery return EXIT_FAILURE;
169d92bc324SVernon Mauery }
170d92bc324SVernon Mauery if (listensFdCount == 1)
171d92bc324SVernon Mauery {
172d92bc324SVernon Mauery int openFd = SD_LISTEN_FDS_START;
173d92bc324SVernon Mauery if (!sd_is_socket(openFd, AF_UNSPEC, SOCK_DGRAM, -1))
174d92bc324SVernon Mauery {
1757b7f25f7SGeorge Liu lg2::error("Failed to set up systemd-passed socket: {ERROR}",
1767b7f25f7SGeorge Liu "ERROR", strerror(errno));
177d92bc324SVernon Mauery return EXIT_FAILURE;
178d92bc324SVernon Mauery }
179d92bc324SVernon Mauery udpSocket = std::make_shared<boost::asio::ip::udp::socket>(
180d92bc324SVernon Mauery *io, boost::asio::ip::udp::v6(), openFd);
181d92bc324SVernon Mauery }
182d92bc324SVernon Mauery else
183d92bc324SVernon Mauery {
184d92bc324SVernon Mauery // asio does not natively offer a way to bind to an interface
185d92bc324SVernon Mauery // so it must be done in steps
186d92bc324SVernon Mauery boost::asio::ip::udp::endpoint ep(boost::asio::ip::udp::v6(), reqPort);
187d92bc324SVernon Mauery udpSocket = std::make_shared<boost::asio::ip::udp::socket>(*io);
188d92bc324SVernon Mauery udpSocket->open(ep.protocol());
189d92bc324SVernon Mauery // bind
190d92bc324SVernon Mauery udpSocket->set_option(
191d92bc324SVernon Mauery boost::asio::ip::udp::socket::reuse_address(true));
192d92bc324SVernon Mauery udpSocket->bind(ep);
193d92bc324SVernon Mauery }
194d92bc324SVernon Mauery // SO_BINDTODEVICE
195d92bc324SVernon Mauery char nameout[IFNAMSIZ];
196d92bc324SVernon Mauery unsigned int lenout = sizeof(nameout);
197d92bc324SVernon Mauery if ((::getsockopt(udpSocket->native_handle(), SOL_SOCKET, SO_BINDTODEVICE,
198d92bc324SVernon Mauery nameout, &lenout) == -1))
199d92bc324SVernon Mauery {
2007b7f25f7SGeorge Liu lg2::error("Failed to read bound device: {ERROR}", "ERROR",
2017b7f25f7SGeorge Liu strerror(errno));
202d92bc324SVernon Mauery }
203d92bc324SVernon Mauery if (iface != nameout && iface != unboundIface)
204d92bc324SVernon Mauery {
205d92bc324SVernon Mauery // SO_BINDTODEVICE
206d92bc324SVernon Mauery if ((::setsockopt(udpSocket->native_handle(), SOL_SOCKET,
2078425624aSPatrick Williams SO_BINDTODEVICE, iface.c_str(), iface.size() + 1) ==
2088425624aSPatrick Williams -1))
209d92bc324SVernon Mauery {
2107b7f25f7SGeorge Liu lg2::error("Failed to bind to requested interface: {ERROR}",
2117b7f25f7SGeorge Liu "ERROR", strerror(errno));
212d92bc324SVernon Mauery return EXIT_FAILURE;
213d92bc324SVernon Mauery }
2147b7f25f7SGeorge Liu lg2::info("Bind to interface: {INTERFACE}", "INTERFACE", iface);
215d92bc324SVernon Mauery }
216d92bc324SVernon Mauery // cannot be constexpr because it gets passed by address
217d92bc324SVernon Mauery const int option_enabled = 1;
218d92bc324SVernon Mauery // common socket stuff; set options to get packet info (DST addr)
219d92bc324SVernon Mauery ::setsockopt(udpSocket->native_handle(), IPPROTO_IP, IP_PKTINFO,
220d92bc324SVernon Mauery &option_enabled, sizeof(option_enabled));
221d92bc324SVernon Mauery ::setsockopt(udpSocket->native_handle(), IPPROTO_IPV6, IPV6_RECVPKTINFO,
222d92bc324SVernon Mauery &option_enabled, sizeof(option_enabled));
223d92bc324SVernon Mauery
224d92bc324SVernon Mauery // set the dbus name
2258c0bf983SAlvin Wang std::string busName = "xyz.openbmc_project.Ipmi.Channel." + channel;
226d92bc324SVernon Mauery try
227d92bc324SVernon Mauery {
228d92bc324SVernon Mauery bus->request_name(busName.c_str());
229d92bc324SVernon Mauery }
230d92bc324SVernon Mauery catch (const std::exception& e)
231d92bc324SVernon Mauery {
2327b7f25f7SGeorge Liu lg2::error("Failed to acquire D-Bus name: {NAME}: {ERROR}", "NAME",
2337b7f25f7SGeorge Liu busName, "ERROR", e);
234d92bc324SVernon Mauery return EXIT_FAILURE;
235d92bc324SVernon Mauery }
236d92bc324SVernon Mauery return 0;
237d92bc324SVernon Mauery }
238d92bc324SVernon Mauery
startEventLoop()239cbccb05aSVernon Mauery int EventLoop::startEventLoop()
2407fd26ddbSTom Joseph {
241afac94d1SGeorge Liu startRmcpReceive();
242afac94d1SGeorge Liu
243afac94d1SGeorge Liu io->run();
244afac94d1SGeorge Liu
245afac94d1SGeorge Liu return EXIT_SUCCESS;
246afac94d1SGeorge Liu }
247afac94d1SGeorge Liu
setupSignal()248afac94d1SGeorge Liu void EventLoop::setupSignal()
249afac94d1SGeorge Liu {
250*b1cbd6fbSGeorge Liu static boost::asio::signal_set signals(*io, SIGINT, SIGTERM);
251be1470ccSGeorge Liu signals.async_wait([this](const boost::system::error_code& /* error */,
252be1470ccSGeorge Liu int /* signalNumber */) {
25301373c2bSGeorge Liu if (udpSocket)
25401373c2bSGeorge Liu {
2557a0142c5SVernon Mauery udpSocket->cancel();
2567a0142c5SVernon Mauery udpSocket->close();
25701373c2bSGeorge Liu }
2587a0142c5SVernon Mauery io->stop();
2597a0142c5SVernon Mauery });
2607fd26ddbSTom Joseph }
2617fd26ddbSTom Joseph
2627fd26ddbSTom Joseph } // namespace eventloop
263