blob: 574294440c8c27831ecb392d149a3fad6e1c28fb [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];
61 retval =
62 inet_ntop(AF_INET, &remoteSockAddr, ipv4addr, sizeof(ipv4addr));
63 }
64 else if (sockAddrSize == sizeof(sockaddr_in6))
65 {
66 char ipv6addr[INET6_ADDRSTRLEN];
67 retval = inet_ntop(AF_INET6, &remoteSockAddr, ipv6addr,
68 sizeof(ipv6addr));
69 }
70 if (retval)
71 {
72 return retval;
73 }
74 phosphor::logging::log<phosphor::logging::level::ERR>(
75 "Error in inet_ntop",
76 phosphor::logging::entry("ERROR=%s", strerror(errno)));
77 return std::string();
Vernon Mauery7a0142c2018-11-09 08:38:16 -080078 }
Tom Josephaaeb29e2016-08-10 06:36:33 -050079
Vernon Mauery9e801a22018-10-12 13:20:49 -070080 /**
81 * @brief Fetch the port number of the remote peer
82 *
83 * Returns the port number of the remote peer
84 *
85 * @return Port number
86 *
87 */
Vernon Maueryd92bc322019-03-15 15:24:30 -070088 uint16_t getPort() const
Vernon Mauery9e801a22018-10-12 13:20:49 -070089 {
Vernon Maueryd92bc322019-03-15 15:24:30 -070090 if (sockAddrSize == sizeof(sockaddr_in))
91 {
92 return ntohs(reinterpret_cast<const sockaddr_in*>(&remoteSockAddr)
93 ->sin_port);
94 }
95 if (sockAddrSize == sizeof(sockaddr_in6))
96 {
97 return ntohs(reinterpret_cast<const sockaddr_in6*>(&remoteSockAddr)
98 ->sin6_port);
99 }
100 return 0;
Vernon Mauery9e801a22018-10-12 13:20:49 -0700101 }
Tom Josephaaeb29e2016-08-10 06:36:33 -0500102
Vernon Mauery9e801a22018-10-12 13:20:49 -0700103 /**
104 * @brief Read the incoming packet
105 *
106 * Reads the data available on the socket
107 *
108 * @return A tuple with return code and vector with the buffer
109 * In case of success, the vector is populated with the data
110 * available on the socket and return code is 0.
111 * In case of error, the return code is < 0 and vector is set
112 * to size 0.
113 */
Vernon Mauery7a0142c2018-11-09 08:38:16 -0800114 std::tuple<int, std::vector<uint8_t>> read()
115 {
Vernon Maueryd92bc322019-03-15 15:24:30 -0700116 // cannot use the standard asio reading mechanism because it does not
117 // provide a mechanism to reach down into the depths and use a msghdr
Vernon Mauery7a0142c2018-11-09 08:38:16 -0800118 std::vector<uint8_t> packet(socket->available());
Vernon Maueryd92bc322019-03-15 15:24:30 -0700119 iovec iov = {packet.data(), packet.size()};
120 char msgCtrl[1024];
121 msghdr msg = {&remoteSockAddr, sizeof(remoteSockAddr), &iov, 1,
122 msgCtrl, sizeof(msgCtrl), 0};
123
124 ssize_t bytesReceived = recvmsg(socket->native_handle(), &msg, 0);
125 // Read of the packet failed
126 if (bytesReceived < 0)
Vernon Mauery7a0142c2018-11-09 08:38:16 -0800127 {
Vernon Maueryd92bc322019-03-15 15:24:30 -0700128 // something bad happened; bail
129 phosphor::logging::log<phosphor::logging::level::ERR>(
130 "Error in recvmsg",
131 phosphor::logging::entry("ERROR=%s", strerror(errno)));
132 return std::make_tuple(-errno, std::vector<uint8_t>());
Vernon Mauery7a0142c2018-11-09 08:38:16 -0800133 }
Vernon Maueryd92bc322019-03-15 15:24:30 -0700134 // save the size of either ipv4 or i4v6 sockaddr
135 sockAddrSize = msg.msg_namelen;
136
137 // extract the destination address from the message
138 cmsghdr* cmsg;
139 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != 0;
140 cmsg = CMSG_NXTHDR(&msg, cmsg))
Vernon Mauery7a0142c2018-11-09 08:38:16 -0800141 {
Vernon Maueryd92bc322019-03-15 15:24:30 -0700142 if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO)
143 {
144 // save local address from the pktinfo4
145 pktinfo4 = *reinterpret_cast<in_pktinfo*>(CMSG_DATA(cmsg));
146 }
147 if (cmsg->cmsg_level == IPPROTO_IPV6 &&
148 cmsg->cmsg_type == IPV6_PKTINFO)
149 {
150 // save local address from the pktinfo6
151 pktinfo6 = *reinterpret_cast<in6_pktinfo*>(CMSG_DATA(cmsg));
152 }
Vernon Mauery7a0142c2018-11-09 08:38:16 -0800153 }
154 return std::make_tuple(0, packet);
155 }
Tom Josephaaeb29e2016-08-10 06:36:33 -0500156
Vernon Mauery9e801a22018-10-12 13:20:49 -0700157 /**
158 * @brief Write the outgoing packet
159 *
160 * Writes the data in the vector to the socket
161 *
162 * @param [in] inBuffer
163 * The vector would be the buffer of data to write to the socket.
164 *
Vernon Maueryd92bc322019-03-15 15:24:30 -0700165 * @return In case of success the return code is the number of bytes
166 * written and return code is < 0 in case of failure.
Vernon Mauery9e801a22018-10-12 13:20:49 -0700167 */
Vernon Mauery7a0142c2018-11-09 08:38:16 -0800168 int write(const std::vector<uint8_t>& inBuffer)
169 {
Vernon Maueryd92bc322019-03-15 15:24:30 -0700170 // in order to make sure packets go back out from the same
171 // IP address they came in on, sendmsg must be used instead
172 // of the boost::asio::ip::send or sendto
173 iovec iov = {const_cast<uint8_t*>(inBuffer.data()), inBuffer.size()};
174 char msgCtrl[1024];
175 msghdr msg = {&remoteSockAddr, sockAddrSize, &iov, 1,
176 msgCtrl, sizeof(msgCtrl), 0};
177 int cmsg_space = 0;
178 cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
179 if (pktinfo6)
Vernon Mauery7a0142c2018-11-09 08:38:16 -0800180 {
Vernon Maueryd92bc322019-03-15 15:24:30 -0700181 cmsg->cmsg_level = IPPROTO_IPV6;
182 cmsg->cmsg_type = IPV6_PKTINFO;
183 cmsg->cmsg_len = CMSG_LEN(sizeof(in6_pktinfo));
184 *reinterpret_cast<in6_pktinfo*>(CMSG_DATA(cmsg)) = *pktinfo6;
185 cmsg_space += CMSG_SPACE(sizeof(in6_pktinfo));
Vernon Mauery7a0142c2018-11-09 08:38:16 -0800186 }
Vernon Maueryd92bc322019-03-15 15:24:30 -0700187 else if (pktinfo4)
Vernon Mauery7a0142c2018-11-09 08:38:16 -0800188 {
Vernon Maueryd92bc322019-03-15 15:24:30 -0700189 cmsg->cmsg_level = IPPROTO_IP;
190 cmsg->cmsg_type = IP_PKTINFO;
191 cmsg->cmsg_len = CMSG_LEN(sizeof(in_pktinfo));
192 *reinterpret_cast<in_pktinfo*>(CMSG_DATA(cmsg)) = *pktinfo4;
193 cmsg_space += CMSG_SPACE(sizeof(in_pktinfo));
Vernon Mauery7a0142c2018-11-09 08:38:16 -0800194 }
Vernon Maueryd92bc322019-03-15 15:24:30 -0700195 msg.msg_controllen = cmsg_space;
196 int ret = sendmsg(socket->native_handle(), &msg, 0);
197 if (ret < 0)
198 {
199 phosphor::logging::log<phosphor::logging::level::ERR>(
200 "Error in sendmsg",
201 phosphor::logging::entry("ERROR=%s", strerror(errno)));
202 }
203 return ret;
Vernon Mauery7a0142c2018-11-09 08:38:16 -0800204 }
Tom Josephaaeb29e2016-08-10 06:36:33 -0500205
Vernon Mauery9e801a22018-10-12 13:20:49 -0700206 /**
207 * @brief Returns file descriptor for the socket
208 */
209 auto getHandle(void) const
210 {
Vernon Mauery7a0142c2018-11-09 08:38:16 -0800211 return socket->native_handle();
Vernon Mauery9e801a22018-10-12 13:20:49 -0700212 }
Tom Josephaaeb29e2016-08-10 06:36:33 -0500213
Vernon Mauery9e801a22018-10-12 13:20:49 -0700214 private:
Vernon Mauery7a0142c2018-11-09 08:38:16 -0800215 std::shared_ptr<boost::asio::ip::udp::socket> socket;
Vernon Maueryd92bc322019-03-15 15:24:30 -0700216 sockaddr_storage remoteSockAddr;
217 socklen_t sockAddrSize;
218 std::optional<in_pktinfo> pktinfo4;
219 std::optional<in6_pktinfo> pktinfo6;
Tom Josephaaeb29e2016-08-10 06:36:33 -0500220};
221
222} // namespace udpsocket