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