Socket Class to encapsulate the socket operations.
The operations provided are reading the UDP packet, writing the packet.
Change-Id: I087dcbdf715213edf787c3053be76813ce8d9802
Signed-off-by: Ratan Gupta <ratagupt@in.ibm.com>
diff --git a/endian.hpp b/endian.hpp
new file mode 100644
index 0000000..284fd03
--- /dev/null
+++ b/endian.hpp
@@ -0,0 +1,49 @@
+#pragma once
+
+#include <endian.h>
+#include <stdint.h>
+
+namespace endian
+{
+namespace details
+{
+template <typename T>
+struct convert
+{
+ static T to_network(T) = delete;
+ static T from_network(T) = delete;
+};
+
+template<> struct convert<uint16_t>
+{
+ static uint16_t to_network(uint16_t i)
+ {
+ return htobe16(i);
+ };
+ static uint16_t from_network(uint16_t i)
+ {
+ return be16toh(i);
+ };
+};
+
+template<> struct convert<uint32_t>
+{
+ static uint32_t to_network(uint32_t i)
+ {
+ return htobe32(i);
+ };
+ static uint32_t from_network(uint32_t i)
+ {
+ return be32toh(i);
+ };
+};
+}
+template<typename T> T to_network(T i)
+{
+ return details::convert<T>::to_network(i);
+}
+template<typename T> T from_network(T i)
+{
+ return details::convert<T>::from_network(i);
+}
+}
diff --git a/sock_channel.cpp b/sock_channel.cpp
new file mode 100644
index 0000000..ef86029
--- /dev/null
+++ b/sock_channel.cpp
@@ -0,0 +1,156 @@
+#include "sock_channel.hpp"
+
+#include <errno.h>
+#include <netinet/in.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include <iostream>
+#include <string>
+
+namespace udpsocket
+{
+
+std::string Channel::getRemoteAddress() const
+{
+ char tmp[INET_ADDRSTRLEN] = { 0 };
+ inet_ntop(AF_INET, &address.inAddr.sin_addr, tmp, sizeof(tmp));
+ return std::string(tmp);
+}
+
+std::tuple<int, buffer> Channel::read()
+{
+ int rc = 0;
+ int readSize = 0;
+ ssize_t readDataLen = 0;
+ buffer outBuffer(0);
+
+ if (ioctl(sockfd, FIONREAD, &readSize) < 0)
+ {
+ std::cerr << "E> Channel::Read : ioctl failed with errno = " << errno;
+ rc = -errno;
+ return std::make_tuple(rc, std::move(outBuffer));
+ }
+
+ outBuffer.resize(readSize);
+ auto bufferSize = outBuffer.size();
+ auto outputPtr = outBuffer.data();
+
+ address.addrSize = 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) // Data read from the socket
+ {
+ std::cout << "I> Channel::Read : DataIn Fd[" << sockfd << "] Req["
+ << bufferSize << "] Recv[" << readDataLen << "]\n";
+ }
+ else if (readDataLen == 0) // Peer has performed an orderly shutdown
+ {
+ std::cerr << "E> Channel::Read : Connection Closed Fd[" << sockfd
+ << "]\n";
+ outBuffer.resize(0);
+ rc = -1;
+ }
+ else if (readDataLen < 0) // Error
+ {
+ rc = -errno;
+ std::cerr << "E> Channel::Read : Receive Error Fd[" << sockfd << "]"
+ << "errno = " << rc << "\n";
+ 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(buffer& 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 = 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;
+ std::cerr << "Channel::Write: Write failed with errno:"
+ << rc << "\n";
+ }
+ else if (static_cast<size_t>(writeDataLen) < bufferSize)
+ {
+ rc = -1;
+ std::cerr << "Channel::Write: Complete data not written"
+ " to the socket\n";
+ }
+ }
+ while ((writeDataLen < 0) && (-(rc) == EINTR));
+ }
+ else
+ {
+ // Spurious wake up
+ std::cerr << "E> Spurious wake up on select (writeset)\n";
+ spuriousWakeup = true;
+ }
+ }
+ else
+ {
+ if (rc == 0)
+ {
+ // Timed out
+ rc = -1;
+ std::cerr << "E> We timed out on select call (writeset)\n";
+ }
+ else
+ {
+ // Error
+ rc = -errno;
+ std::cerr << "E> select call (writeset) had an error : "
+ << rc << "\n";
+ }
+
+ }
+ }
+ while (spuriousWakeup);
+
+ return rc;
+}
+
+} // namespace udpsocket
diff --git a/sock_channel.hpp b/sock_channel.hpp
new file mode 100644
index 0000000..e992564
--- /dev/null
+++ b/sock_channel.hpp
@@ -0,0 +1,116 @@
+#pragma once
+
+#include <arpa/inet.h>
+#include <unistd.h>
+
+#include <string>
+#include <tuple>
+#include <vector>
+
+namespace udpsocket
+{
+
+using buffer = std::vector<uint8_t>;
+/** @class Channel
+ *
+ * @brief Provides encapsulation for UDP socket operations like Read, Peek,
+ * Write, Remote peer's IP Address and Port.
+ */
+class Channel
+{
+ public:
+ struct SockAddr_t
+ {
+ union
+ {
+ sockaddr sockAddr;
+ sockaddr_in inAddr;
+ };
+ size_t addrSize;
+ };
+
+ /**
+ * @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
+ *
+ * @return None
+ */
+ Channel(int insockfd, timeval& inTimeout)
+ {
+ sockfd = insockfd;
+ timeout = inTimeout;
+ }
+
+ /**
+ * @brief Fetch the IP address of the remote peer
+ *
+ * Returns the IP address of the remote peer which is connected to this
+ * socket
+ *
+ * @return IP address of the remote peer
+ */
+ std::string getRemoteAddress() const;
+
+ /**
+ * @brief Fetch the port number of the remote peer
+ *
+ * Returns the port number of the remote peer
+ *
+ * @return Port number
+ *
+ */
+ auto getPort() const
+ {
+ return address.inAddr.sin_port;
+ }
+
+ /**
+ * @brief Read the incoming packet
+ *
+ * Reads the data available on the socket
+ *
+ * @return A tuple with return code and vector with the buffer
+ * In case of success, the vector is populated with the data
+ * available on the socket and return code is 0.
+ * In case of error, the return code is < 0 and vector is set
+ * to size 0.
+ */
+ std::tuple<int, buffer> read();
+
+ /**
+ * @brief Write the outgoing packet
+ *
+ * Writes the data in the vector to the socket
+ *
+ * @param [in] inBuffer
+ * The vector would be the buffer of data to write to the socket.
+ *
+ * @return In case of success the return code is 0 and return code is
+ * < 0 in case of failure.
+ */
+ int write(buffer& inBuffer);
+
+ ~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;
+};
+
+} // namespace udpsocket