blob: df7e3ae3acb5fe5a4b54886584838c7fb3e6277e [file] [log] [blame]
Tom Josephaaeb29e2016-08-10 06:36:33 -05001#pragma once
Vernon Maueryd92bc322019-03-15 15:24:30 -07002#include <netinet/in.h>
3#include <sys/socket.h>
4#include <sys/types.h>
Tom Josephaaeb29e2016-08-10 06:36:33 -05005
Vernon Mauery7a0142c2018-11-09 08:38:16 -08006#include <boost/asio/ip/udp.hpp>
7#include <memory>
Vernon Maueryd92bc322019-03-15 15:24:30 -07008#include <optional>
9#include <phosphor-logging/log.hpp>
Tom Josephaaeb29e2016-08-10 06:36:33 -050010#include <string>
11#include <tuple>
Vernon Maueryd92bc322019-03-15 15:24:30 -070012#include <variant>
Tom Josephaaeb29e2016-08-10 06:36:33 -050013#include <vector>
14
15namespace udpsocket
16{
17
Tom Josephaaeb29e2016-08-10 06:36:33 -050018/** @class Channel
19 *
20 * @brief Provides encapsulation for UDP socket operations like Read, Peek,
21 * Write, Remote peer's IP Address and Port.
22 */
23class Channel
24{
Vernon Mauery9e801a22018-10-12 13:20:49 -070025 public:
Vernon Mauery7a0142c2018-11-09 08:38:16 -080026 Channel() = delete;
27 ~Channel() = default;
28 Channel(const Channel& right) = delete;
29 Channel& operator=(const Channel& right) = delete;
30 Channel(Channel&&) = delete;
31 Channel& operator=(Channel&&) = delete;
Tom Josephaaeb29e2016-08-10 06:36:33 -050032
Vernon Mauery9e801a22018-10-12 13:20:49 -070033 /**
34 * @brief Constructor
35 *
36 * Initialize the IPMI socket object with the socket descriptor
37 *
Vernon Mauery7a0142c2018-11-09 08:38:16 -080038 * @param [in] pointer to a boost::asio udp socket object
Vernon Mauery9e801a22018-10-12 13:20:49 -070039 *
40 * @return None
41 */
Vernon Mauery7a0142c2018-11-09 08:38:16 -080042 explicit Channel(std::shared_ptr<boost::asio::ip::udp::socket> socket) :
43 socket(socket)
Vernon Mauery9e801a22018-10-12 13:20:49 -070044 {
Vernon Mauery9e801a22018-10-12 13:20:49 -070045 }
Tom Josephaaeb29e2016-08-10 06:36:33 -050046
Vernon Mauery9e801a22018-10-12 13:20:49 -070047 /**
48 * @brief Fetch the IP address of the remote peer
49 *
50 * Returns the IP address of the remote peer which is connected to this
51 * socket
52 *
53 * @return IP address of the remote peer
54 */
Vernon Mauery7a0142c2018-11-09 08:38:16 -080055 std::string getRemoteAddress() const
56 {
Vernon Maueryd92bc322019-03-15 15:24:30 -070057 const char* retval = nullptr;
58 if (sockAddrSize == sizeof(sockaddr_in))
59 {
60 char ipv4addr[INET_ADDRSTRLEN];
Ivan Mikhaylov86985d52019-11-29 12:22:28 +030061 retval = inet_ntop(
62 AF_INET,
63 &(reinterpret_cast<const sockaddr_in*>(&remoteSockAddr)
64 ->sin_addr),
65 ipv4addr, sizeof(ipv4addr));
Vernon Maueryd92bc322019-03-15 15:24:30 -070066 }
67 else if (sockAddrSize == sizeof(sockaddr_in6))
68 {
69 char ipv6addr[INET6_ADDRSTRLEN];
Ivan Mikhaylov86985d52019-11-29 12:22:28 +030070 retval = inet_ntop(
71 AF_INET6,
72 &(reinterpret_cast<const sockaddr_in6*>(&remoteSockAddr)
73 ->sin6_addr),
74 ipv6addr, sizeof(ipv6addr));
Vernon Maueryd92bc322019-03-15 15:24:30 -070075 }
76 if (retval)
77 {
78 return retval;
79 }
80 phosphor::logging::log<phosphor::logging::level::ERR>(
81 "Error in inet_ntop",
82 phosphor::logging::entry("ERROR=%s", strerror(errno)));
83 return std::string();
Vernon Mauery7a0142c2018-11-09 08:38:16 -080084 }
Tom Josephaaeb29e2016-08-10 06:36:33 -050085
Vernon Mauery9e801a22018-10-12 13:20:49 -070086 /**
87 * @brief Fetch the port number of the remote peer
88 *
89 * Returns the port number of the remote peer
90 *
91 * @return Port number
92 *
93 */
Vernon Maueryd92bc322019-03-15 15:24:30 -070094 uint16_t getPort() const
Vernon Mauery9e801a22018-10-12 13:20:49 -070095 {
Vernon Maueryd92bc322019-03-15 15:24:30 -070096 if (sockAddrSize == sizeof(sockaddr_in))
97 {
98 return ntohs(reinterpret_cast<const sockaddr_in*>(&remoteSockAddr)
99 ->sin_port);
100 }
101 if (sockAddrSize == sizeof(sockaddr_in6))
102 {
103 return ntohs(reinterpret_cast<const sockaddr_in6*>(&remoteSockAddr)
104 ->sin6_port);
105 }
106 return 0;
Vernon Mauery9e801a22018-10-12 13:20:49 -0700107 }
Tom Josephaaeb29e2016-08-10 06:36:33 -0500108
Vernon Mauery9e801a22018-10-12 13:20:49 -0700109 /**
110 * @brief Read the incoming packet
111 *
112 * Reads the data available on the socket
113 *
114 * @return A tuple with return code and vector with the buffer
115 * In case of success, the vector is populated with the data
116 * available on the socket and return code is 0.
117 * In case of error, the return code is < 0 and vector is set
118 * to size 0.
119 */
Vernon Mauery7a0142c2018-11-09 08:38:16 -0800120 std::tuple<int, std::vector<uint8_t>> read()
121 {
Vernon Maueryd92bc322019-03-15 15:24:30 -0700122 // cannot use the standard asio reading mechanism because it does not
123 // provide a mechanism to reach down into the depths and use a msghdr
Vernon Mauery7a0142c2018-11-09 08:38:16 -0800124 std::vector<uint8_t> packet(socket->available());
Vernon Maueryd92bc322019-03-15 15:24:30 -0700125 iovec iov = {packet.data(), packet.size()};
126 char msgCtrl[1024];
127 msghdr msg = {&remoteSockAddr, sizeof(remoteSockAddr), &iov, 1,
128 msgCtrl, sizeof(msgCtrl), 0};
129
130 ssize_t bytesReceived = recvmsg(socket->native_handle(), &msg, 0);
131 // Read of the packet failed
132 if (bytesReceived < 0)
Vernon Mauery7a0142c2018-11-09 08:38:16 -0800133 {
Vernon Maueryd92bc322019-03-15 15:24:30 -0700134 // something bad happened; bail
135 phosphor::logging::log<phosphor::logging::level::ERR>(
136 "Error in recvmsg",
137 phosphor::logging::entry("ERROR=%s", strerror(errno)));
138 return std::make_tuple(-errno, std::vector<uint8_t>());
Vernon Mauery7a0142c2018-11-09 08:38:16 -0800139 }
Vernon Maueryd92bc322019-03-15 15:24:30 -0700140 // save the size of either ipv4 or i4v6 sockaddr
141 sockAddrSize = msg.msg_namelen;
142
143 // extract the destination address from the message
144 cmsghdr* cmsg;
145 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != 0;
146 cmsg = CMSG_NXTHDR(&msg, cmsg))
Vernon Mauery7a0142c2018-11-09 08:38:16 -0800147 {
Vernon Maueryd92bc322019-03-15 15:24:30 -0700148 if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO)
149 {
150 // save local address from the pktinfo4
151 pktinfo4 = *reinterpret_cast<in_pktinfo*>(CMSG_DATA(cmsg));
152 }
153 if (cmsg->cmsg_level == IPPROTO_IPV6 &&
154 cmsg->cmsg_type == IPV6_PKTINFO)
155 {
156 // save local address from the pktinfo6
157 pktinfo6 = *reinterpret_cast<in6_pktinfo*>(CMSG_DATA(cmsg));
158 }
Vernon Mauery7a0142c2018-11-09 08:38:16 -0800159 }
160 return std::make_tuple(0, packet);
161 }
Tom Josephaaeb29e2016-08-10 06:36:33 -0500162
Vernon Mauery9e801a22018-10-12 13:20:49 -0700163 /**
164 * @brief Write the outgoing packet
165 *
166 * Writes the data in the vector to the socket
167 *
168 * @param [in] inBuffer
169 * The vector would be the buffer of data to write to the socket.
170 *
Vernon Maueryd92bc322019-03-15 15:24:30 -0700171 * @return In case of success the return code is the number of bytes
172 * written and return code is < 0 in case of failure.
Vernon Mauery9e801a22018-10-12 13:20:49 -0700173 */
Vernon Mauery7a0142c2018-11-09 08:38:16 -0800174 int write(const std::vector<uint8_t>& inBuffer)
175 {
Vernon Maueryd92bc322019-03-15 15:24:30 -0700176 // in order to make sure packets go back out from the same
177 // IP address they came in on, sendmsg must be used instead
178 // of the boost::asio::ip::send or sendto
179 iovec iov = {const_cast<uint8_t*>(inBuffer.data()), inBuffer.size()};
180 char msgCtrl[1024];
181 msghdr msg = {&remoteSockAddr, sockAddrSize, &iov, 1,
182 msgCtrl, sizeof(msgCtrl), 0};
183 int cmsg_space = 0;
184 cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
185 if (pktinfo6)
Vernon Mauery7a0142c2018-11-09 08:38:16 -0800186 {
Vernon Maueryd92bc322019-03-15 15:24:30 -0700187 cmsg->cmsg_level = IPPROTO_IPV6;
188 cmsg->cmsg_type = IPV6_PKTINFO;
189 cmsg->cmsg_len = CMSG_LEN(sizeof(in6_pktinfo));
190 *reinterpret_cast<in6_pktinfo*>(CMSG_DATA(cmsg)) = *pktinfo6;
191 cmsg_space += CMSG_SPACE(sizeof(in6_pktinfo));
Vernon Mauery7a0142c2018-11-09 08:38:16 -0800192 }
Vernon Maueryd92bc322019-03-15 15:24:30 -0700193 else if (pktinfo4)
Vernon Mauery7a0142c2018-11-09 08:38:16 -0800194 {
Vernon Maueryd92bc322019-03-15 15:24:30 -0700195 cmsg->cmsg_level = IPPROTO_IP;
196 cmsg->cmsg_type = IP_PKTINFO;
197 cmsg->cmsg_len = CMSG_LEN(sizeof(in_pktinfo));
198 *reinterpret_cast<in_pktinfo*>(CMSG_DATA(cmsg)) = *pktinfo4;
199 cmsg_space += CMSG_SPACE(sizeof(in_pktinfo));
Vernon Mauery7a0142c2018-11-09 08:38:16 -0800200 }
Vernon Maueryd92bc322019-03-15 15:24:30 -0700201 msg.msg_controllen = cmsg_space;
202 int ret = sendmsg(socket->native_handle(), &msg, 0);
203 if (ret < 0)
204 {
205 phosphor::logging::log<phosphor::logging::level::ERR>(
206 "Error in sendmsg",
207 phosphor::logging::entry("ERROR=%s", strerror(errno)));
208 }
209 return ret;
Vernon Mauery7a0142c2018-11-09 08:38:16 -0800210 }
Tom Josephaaeb29e2016-08-10 06:36:33 -0500211
Vernon Mauery9e801a22018-10-12 13:20:49 -0700212 /**
213 * @brief Returns file descriptor for the socket
214 */
215 auto getHandle(void) const
216 {
Vernon Mauery7a0142c2018-11-09 08:38:16 -0800217 return socket->native_handle();
Vernon Mauery9e801a22018-10-12 13:20:49 -0700218 }
Tom Josephaaeb29e2016-08-10 06:36:33 -0500219
Vernon Mauery9e801a22018-10-12 13:20:49 -0700220 private:
Vernon Mauery7a0142c2018-11-09 08:38:16 -0800221 std::shared_ptr<boost::asio::ip::udp::socket> socket;
Vernon Maueryd92bc322019-03-15 15:24:30 -0700222 sockaddr_storage remoteSockAddr;
223 socklen_t sockAddrSize;
224 std::optional<in_pktinfo> pktinfo4;
225 std::optional<in6_pktinfo> pktinfo6;
Tom Josephaaeb29e2016-08-10 06:36:33 -0500226};
227
228} // namespace udpsocket