netipmid: move raw sockets to boost::asio sockets
Replacing the raw socket code with boost::asio sockets once again
provides a simple API with fewer lines of code.
Change-Id: Ibdd4b5ecbead947128200f17025c351d9b3ec859
Signed-off-by: Vernon Mauery <vernon.mauery@linux.intel.com>
diff --git a/Makefile.am b/Makefile.am
index 297aa9c..fd6a20e 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -11,7 +11,6 @@
netipmid_SOURCES = \
endian.hpp \
socket_channel.hpp \
- socket_channel.cpp \
message.hpp \
auth_algo.hpp \
auth_algo.cpp \
diff --git a/sd_event_loop.cpp b/sd_event_loop.cpp
index 3e42d6c..990426f 100644
--- a/sd_event_loop.cpp
+++ b/sd_event_loop.cpp
@@ -16,45 +16,49 @@
{
using namespace phosphor::logging;
-static int udp623Handler(sd_event_source* es, int fd, uint32_t revents,
- void* userdata)
+void EventLoop::handleRmcpPacket()
{
- std::shared_ptr<udpsocket::Channel> channelPtr;
- struct timeval timeout;
- timeout.tv_sec = SELECT_CALL_TIMEOUT;
- timeout.tv_usec = 0;
-
try
{
- channelPtr.reset(new udpsocket::Channel(fd, timeout));
+ auto channelPtr = std::make_shared<udpsocket::Channel>(udpSocket);
// Initialize the Message Handler with the socket channel
- message::Handler msgHandler(channelPtr);
+ auto msgHandler = std::make_shared<message::Handler>(channelPtr);
// Read the incoming IPMI packet
- std::shared_ptr<message::Message> inMessage(msgHandler.receive());
+ std::shared_ptr<message::Message> inMessage(msgHandler->receive());
if (inMessage == nullptr)
{
- return 0;
+ return;
}
// Execute the Command
- auto outMessage = msgHandler.executeCommand(inMessage);
+ std::shared_ptr<message::Message> outMessage =
+ msgHandler->executeCommand(inMessage);
if (outMessage == nullptr)
{
- return 0;
+ return;
}
-
// Send the response IPMI Message
- msgHandler.send(outMessage);
+ msgHandler->send(outMessage);
}
- catch (std::exception& e)
+ catch (const std::exception& e)
{
- log<level::ERR>("Executing the IPMI message failed");
- log<level::ERR>(e.what());
+ log<level::ERR>("Executing the IPMI message failed",
+ entry("EXCEPTION=%s", e.what()));
}
+}
- return 0;
+void EventLoop::startRmcpReceive()
+{
+ udpSocket->async_wait(boost::asio::socket_base::wait_read,
+ [this](const boost::system::error_code& ec) {
+ if (!ec)
+ {
+ io->post([this]() { startRmcpReceive(); });
+ handleRmcpPacket();
+ }
+ });
}
static int consoleInputHandler(sd_event_source* es, int fd, uint32_t revents,
@@ -174,62 +178,45 @@
int EventLoop::startEventLoop()
{
- int fd = -1;
- int r = 0;
- int listenFd;
- sd_event_source* source = nullptr;
-
sdbusplus::asio::sd_event_wrapper sdEvents(*io);
event = sdEvents.get();
// set up boost::asio signal handling
boost::asio::signal_set signals(*io, SIGINT, SIGTERM);
- signals.async_wait([this](const boost::system::error_code& error,
- int signalNumber) { io->stop(); });
+ signals.async_wait(
+ [this](const boost::system::error_code& error, int signalNumber) {
+ udpSocket->cancel();
+ udpSocket->close();
+ io->stop();
+ });
// Create our own socket if SysD did not supply one.
- listenFd = sd_listen_fds(0);
- if (listenFd == 1)
+ int listensFdCount = sd_listen_fds(0);
+ if (listensFdCount == 1)
{
- fd = SD_LISTEN_FDS_START;
+ if (sd_is_socket(SD_LISTEN_FDS_START, AF_UNSPEC, SOCK_DGRAM, -1))
+ {
+ udpSocket = std::make_shared<boost::asio::ip::udp::socket>(
+ *io, boost::asio::ip::udp::v6(), SD_LISTEN_FDS_START);
+ }
}
- else if (listenFd > 1)
+ else if (listensFdCount > 1)
{
log<level::ERR>("Too many file descriptors received");
- return 1;
- }
- else
- {
- struct sockaddr_in address;
- if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == 0)
- {
- r = -errno;
- log<level::ERR>("Unable to manually open socket");
- return EXIT_FAILURE;
- }
-
- address.sin_family = AF_INET;
- address.sin_addr.s_addr = INADDR_ANY;
- address.sin_port = htons(IPMI_STD_PORT);
-
- if (bind(fd, (struct sockaddr*)&address, sizeof(address)) < 0)
- {
- r = -errno;
- log<level::ERR>("Unable to bind socket");
- close(fd);
- return EXIT_FAILURE;
- }
- }
-
- r = sd_event_add_io(event, &source, fd, EPOLLIN, udp623Handler, nullptr);
- if (r < 0)
- {
- close(fd);
return EXIT_FAILURE;
}
-
- udpIPMI.reset(source);
- source = nullptr;
+ if (!udpSocket)
+ {
+ udpSocket = std::make_shared<boost::asio::ip::udp::socket>(
+ *io, boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v6(),
+ IPMI_STD_PORT));
+ if (!udpSocket)
+ {
+ log<level::ERR>("Failed to start listening on RMCP socket");
+ return EXIT_FAILURE;
+ }
+ }
+ startRmcpReceive();
io->run();
diff --git a/sd_event_loop.hpp b/sd_event_loop.hpp
index 5218b27..c81218a 100644
--- a/sd_event_loop.hpp
+++ b/sd_event_loop.hpp
@@ -139,6 +139,12 @@
sd_event* event = nullptr;
private:
+ /** @brief async handler for incoming udp packets */
+ void handleRmcpPacket();
+
+ /** @brief register the async handler for incoming udp packets */
+ void startRmcpReceive();
+
/** @brief Event source object for host console. */
EventSource hostConsole = nullptr;
@@ -146,10 +152,9 @@
*/
std::shared_ptr<boost::asio::io_context> io;
- /** @brief Event source for the UDP socket listening on IPMI standard
- * port.
+ /** @brief boost::asio udp socket
*/
- EventSource udpIPMI = nullptr;
+ std::shared_ptr<boost::asio::ip::udp::socket> udpSocket = nullptr;
/** @brief Map to keep information regarding IPMI payload instance and
* timers for character accumulate interval and retry interval.
diff --git a/socket_channel.cpp b/socket_channel.cpp
deleted file mode 100644
index a3e1301..0000000
--- a/socket_channel.cpp
+++ /dev/null
@@ -1,152 +0,0 @@
-#include "socket_channel.hpp"
-
-#include <errno.h>
-#include <netinet/in.h>
-#include <sys/ioctl.h>
-#include <sys/socket.h>
-#include <unistd.h>
-
-#include <phosphor-logging/log.hpp>
-#include <string>
-
-using namespace phosphor::logging;
-
-namespace udpsocket
-{
-
-std::string Channel::getRemoteAddress() const
-{
- char tmp[INET_ADDRSTRLEN] = {0};
- inet_ntop(AF_INET6, &address.inAddr.sin6_addr, tmp, sizeof(tmp));
- return std::string(tmp);
-}
-
-std::tuple<int, std::vector<uint8_t>> Channel::read()
-{
- int rc = 0;
- int readSize = 0;
- ssize_t readDataLen = 0;
- std::vector<uint8_t> outBuffer(0);
-
- if (ioctl(sockfd, FIONREAD, &readSize) < 0)
- {
- log<level::ERR>("Channel::Read: ioctl failed",
- entry("ERRNO=%d", errno));
- rc = -errno;
- return std::make_tuple(rc, std::move(outBuffer));
- }
-
- outBuffer.resize(readSize);
- auto bufferSize = outBuffer.size();
- auto outputPtr = outBuffer.data();
-
- address.addrSize = static_cast<socklen_t>(sizeof(address.inAddr));
-
- do
- {
- readDataLen = recvfrom(sockfd, // File Descriptor
- outputPtr, // Buffer
- bufferSize, // Bytes requested
- 0, // Flags
- &address.sockAddr, // Address
- &address.addrSize); // Address Length
-
- if (readDataLen == 0) // Peer has performed an orderly shutdown
- {
- log<level::ERR>("Channel::Read: Connection Closed");
- outBuffer.resize(0);
- rc = -1;
- }
- else if (readDataLen < 0) // Error
- {
- rc = -errno;
- log<level::ERR>("Channel::Read: Receive Error",
- entry("ERRNO=%d", rc));
- outBuffer.resize(0);
- }
- } while ((readDataLen < 0) && (-(rc) == EINTR));
-
- // Resize the vector to the actual data read from the socket
- outBuffer.resize(readDataLen);
- return std::make_tuple(rc, std::move(outBuffer));
-}
-
-int Channel::write(const std::vector<uint8_t>& inBuffer)
-{
- int rc = 0;
- auto outputPtr = inBuffer.data();
- auto bufferSize = inBuffer.size();
- auto spuriousWakeup = false;
- ssize_t writeDataLen = 0;
- timeval varTimeout = timeout;
-
- fd_set writeSet;
- FD_ZERO(&writeSet);
- FD_SET(sockfd, &writeSet);
-
- do
- {
- spuriousWakeup = false;
-
- rc = select((sockfd + 1), nullptr, &writeSet, NULL, &varTimeout);
-
- if (rc > 0)
- {
- if (FD_ISSET(sockfd, &writeSet))
- {
- address.addrSize =
- static_cast<socklen_t>(sizeof(address.inAddr));
- do
- {
- writeDataLen =
- sendto(sockfd, // File Descriptor
- outputPtr, // Message
- bufferSize, // Length
- MSG_NOSIGNAL, // Flags
- &address.sockAddr, // Destination Address
- address.addrSize); // Address Length
-
- if (writeDataLen < 0)
- {
- rc = -errno;
- log<level::ERR>("Channel::Write: Write failed",
- entry("ERRNO=%d", rc));
- }
- else if (static_cast<size_t>(writeDataLen) < bufferSize)
- {
- rc = -1;
- log<level::ERR>(
- "Channel::Write: Complete data not written"
- " to the socket");
- }
- } while ((writeDataLen < 0) && (-(rc) == EINTR));
- }
- else
- {
- // Spurious wake up
- log<level::ERR>("Spurious wake up on select (writeset)");
- spuriousWakeup = true;
- }
- }
- else
- {
- if (rc == 0)
- {
- // Timed out
- rc = -1;
- log<level::ERR>("We timed out on select call (writeset)");
- }
- else
- {
- // Error
- rc = -errno;
- log<level::ERR>("select call (writeset) error",
- entry("ERRNO=%d", rc));
- }
- }
- } while (spuriousWakeup);
-
- return rc;
-}
-
-} // namespace udpsocket
diff --git a/socket_channel.hpp b/socket_channel.hpp
index 4b10b9a..ebe0c8f 100644
--- a/socket_channel.hpp
+++ b/socket_channel.hpp
@@ -1,8 +1,7 @@
#pragma once
-#include <arpa/inet.h>
-#include <unistd.h>
-
+#include <boost/asio/ip/udp.hpp>
+#include <memory>
#include <string>
#include <tuple>
#include <vector>
@@ -18,28 +17,24 @@
class Channel
{
public:
- struct SockAddr_t
- {
- union
- {
- sockaddr sockAddr;
- sockaddr_in6 inAddr;
- };
- socklen_t addrSize;
- };
+ Channel() = delete;
+ ~Channel() = default;
+ Channel(const Channel& right) = delete;
+ Channel& operator=(const Channel& right) = delete;
+ Channel(Channel&&) = delete;
+ Channel& operator=(Channel&&) = delete;
/**
* @brief Constructor
*
* Initialize the IPMI socket object with the socket descriptor
*
- * @param [in] File Descriptor for the socket
- * @param [in] Timeout parameter for the select call
+ * @param [in] pointer to a boost::asio udp socket object
*
* @return None
*/
- Channel(int insockfd, timeval& inTimeout) :
- sockfd(insockfd), timeout(inTimeout)
+ explicit Channel(std::shared_ptr<boost::asio::ip::udp::socket> socket) :
+ socket(socket)
{
}
@@ -51,7 +46,10 @@
*
* @return IP address of the remote peer
*/
- std::string getRemoteAddress() const;
+ std::string getRemoteAddress() const
+ {
+ return endpoint.address().to_string();
+ }
/**
* @brief Fetch the port number of the remote peer
@@ -63,7 +61,7 @@
*/
auto getPort() const
{
- return address.inAddr.sin6_port;
+ return endpoint.port();
}
/**
@@ -77,7 +75,19 @@
* In case of error, the return code is < 0 and vector is set
* to size 0.
*/
- std::tuple<int, std::vector<uint8_t>> read();
+ std::tuple<int, std::vector<uint8_t>> read()
+ {
+ std::vector<uint8_t> packet(socket->available());
+ try
+ {
+ socket->receive_from(boost::asio::buffer(packet), endpoint);
+ }
+ catch (const boost::system::system_error& e)
+ {
+ return std::make_tuple(e.code().value(), std::vector<uint8_t>());
+ }
+ return std::make_tuple(0, packet);
+ }
/**
* @brief Write the outgoing packet
@@ -90,33 +100,30 @@
* @return In case of success the return code is 0 and return code is
* < 0 in case of failure.
*/
- int write(const std::vector<uint8_t>& inBuffer);
+ int write(const std::vector<uint8_t>& inBuffer)
+ {
+ try
+ {
+ socket->send_to(boost::asio::buffer(inBuffer), endpoint);
+ }
+ catch (const boost::system::system_error& e)
+ {
+ return e.code().value();
+ }
+ return 0;
+ }
/**
* @brief Returns file descriptor for the socket
*/
auto getHandle(void) const
{
- return sockfd;
+ return socket->native_handle();
}
- ~Channel() = default;
- Channel(const Channel& right) = delete;
- Channel& operator=(const Channel& right) = delete;
- Channel(Channel&&) = default;
- Channel& operator=(Channel&&) = default;
-
private:
- /*
- * The socket descriptor is the UDP server socket for the IPMI port.
- * The same socket descriptor is used for multiple ipmi clients and the
- * life of the descriptor is lifetime of the net-ipmid server. So we
- * do not need to close the socket descriptor in the cleanup of the
- * udpsocket class.
- */
- int sockfd;
- SockAddr_t address;
- timeval timeout;
+ std::shared_ptr<boost::asio::ip::udp::socket> socket;
+ boost::asio::ip::udp::endpoint endpoint{};
};
} // namespace udpsocket