1 #include "sd_event_loop.hpp"
2 
3 #include "main.hpp"
4 #include "message_handler.hpp"
5 
6 #include <netinet/in.h>
7 #include <sys/ioctl.h>
8 #include <sys/socket.h>
9 #include <systemd/sd-daemon.h>
10 
11 #include <boost/asio/io_context.hpp>
12 #include <phosphor-logging/log.hpp>
13 #include <sdbusplus/asio/sd_event.hpp>
14 
15 namespace eventloop
16 {
17 using namespace phosphor::logging;
18 
19 void EventLoop::handleRmcpPacket()
20 {
21     try
22     {
23         auto channelPtr = std::make_shared<udpsocket::Channel>(udpSocket);
24 
25         // Initialize the Message Handler with the socket channel
26         auto msgHandler = std::make_shared<message::Handler>(channelPtr, io);
27 
28         msgHandler->processIncoming();
29     }
30     catch (const std::exception& e)
31     {
32         log<level::ERR>("Executing the IPMI message failed",
33                         entry("EXCEPTION=%s", e.what()));
34     }
35 }
36 
37 void EventLoop::startRmcpReceive()
38 {
39     udpSocket->async_wait(boost::asio::socket_base::wait_read,
40                           [this](const boost::system::error_code& ec) {
41                               if (!ec)
42                               {
43                                   io->post([this]() { startRmcpReceive(); });
44                                   handleRmcpPacket();
45                               }
46                           });
47 }
48 
49 int EventLoop::setupSocket(std::shared_ptr<sdbusplus::asio::connection>& bus,
50                            std::string iface, uint16_t reqPort)
51 {
52     static constexpr const char* unboundIface = "rmcpp";
53     if (iface == "")
54     {
55         iface = unboundIface;
56     }
57     // Create our own socket if SysD did not supply one.
58     int listensFdCount = sd_listen_fds(0);
59     if (listensFdCount > 1)
60     {
61         log<level::ERR>("Too many file descriptors received");
62         return EXIT_FAILURE;
63     }
64     if (listensFdCount == 1)
65     {
66         int openFd = SD_LISTEN_FDS_START;
67         if (!sd_is_socket(openFd, AF_UNSPEC, SOCK_DGRAM, -1))
68         {
69             log<level::ERR>("Failed to set up systemd-passed socket");
70             return EXIT_FAILURE;
71         }
72         udpSocket = std::make_shared<boost::asio::ip::udp::socket>(
73             *io, boost::asio::ip::udp::v6(), openFd);
74     }
75     else
76     {
77         // asio does not natively offer a way to bind to an interface
78         // so it must be done in steps
79         boost::asio::ip::udp::endpoint ep(boost::asio::ip::udp::v6(), reqPort);
80         udpSocket = std::make_shared<boost::asio::ip::udp::socket>(*io);
81         udpSocket->open(ep.protocol());
82         // bind
83         udpSocket->set_option(
84             boost::asio::ip::udp::socket::reuse_address(true));
85         udpSocket->bind(ep);
86     }
87     // SO_BINDTODEVICE
88     char nameout[IFNAMSIZ];
89     unsigned int lenout = sizeof(nameout);
90     if ((::getsockopt(udpSocket->native_handle(), SOL_SOCKET, SO_BINDTODEVICE,
91                       nameout, &lenout) == -1))
92     {
93         log<level::ERR>("Failed to read bound device",
94                         entry("ERROR=%s", strerror(errno)));
95     }
96     if (iface != nameout && iface != unboundIface)
97     {
98         // SO_BINDTODEVICE
99         if ((::setsockopt(udpSocket->native_handle(), SOL_SOCKET,
100                           SO_BINDTODEVICE, iface.c_str(),
101                           iface.size() + 1) == -1))
102         {
103             log<level::ERR>("Failed to bind to requested interface",
104                             entry("ERROR=%s", strerror(errno)));
105             return EXIT_FAILURE;
106         }
107     }
108     // cannot be constexpr because it gets passed by address
109     const int option_enabled = 1;
110     // common socket stuff; set options to get packet info (DST addr)
111     ::setsockopt(udpSocket->native_handle(), IPPROTO_IP, IP_PKTINFO,
112                  &option_enabled, sizeof(option_enabled));
113     ::setsockopt(udpSocket->native_handle(), IPPROTO_IPV6, IPV6_RECVPKTINFO,
114                  &option_enabled, sizeof(option_enabled));
115 
116     // set the dbus name
117     std::string busName = "xyz.openbmc_project.Ipmi.Channel." + iface;
118     try
119     {
120         bus->request_name(busName.c_str());
121     }
122     catch (const std::exception& e)
123     {
124         log<level::ERR>("Failed to acquire D-Bus name",
125                         entry("NAME=%s", busName.c_str()),
126                         entry("ERROR=%s", e.what()));
127         return EXIT_FAILURE;
128     }
129     return 0;
130 }
131 
132 int EventLoop::startEventLoop()
133 {
134     // set up boost::asio signal handling
135     boost::asio::signal_set signals(*io, SIGINT, SIGTERM);
136     signals.async_wait(
137         [this](const boost::system::error_code& error, int signalNumber) {
138             udpSocket->cancel();
139             udpSocket->close();
140             io->stop();
141         });
142 
143     startRmcpReceive();
144 
145     io->run();
146 
147     return EXIT_SUCCESS;
148 }
149 
150 } // namespace eventloop
151