blob: b993f0ead7ad01705d957034002f6821a19dee41 [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{
Rajashekar Gade Reddy9979e992020-02-07 19:18:34 +053017static constexpr uint8_t v4v6Index = 12;
Tom Josephaaeb29e2016-08-10 06:36:33 -050018
Tom Josephaaeb29e2016-08-10 06:36:33 -050019/** @class Channel
20 *
21 * @brief Provides encapsulation for UDP socket operations like Read, Peek,
22 * Write, Remote peer's IP Address and Port.
23 */
24class Channel
25{
Vernon Mauery9e801a22018-10-12 13:20:49 -070026 public:
Vernon Mauery7a0142c2018-11-09 08:38:16 -080027 Channel() = delete;
28 ~Channel() = default;
29 Channel(const Channel& right) = delete;
30 Channel& operator=(const Channel& right) = delete;
31 Channel(Channel&&) = delete;
32 Channel& operator=(Channel&&) = delete;
Tom Josephaaeb29e2016-08-10 06:36:33 -050033
Vernon Mauery9e801a22018-10-12 13:20:49 -070034 /**
35 * @brief Constructor
36 *
37 * Initialize the IPMI socket object with the socket descriptor
38 *
Vernon Mauery7a0142c2018-11-09 08:38:16 -080039 * @param [in] pointer to a boost::asio udp socket object
Vernon Mauery9e801a22018-10-12 13:20:49 -070040 *
41 * @return None
42 */
Vernon Mauery7a0142c2018-11-09 08:38:16 -080043 explicit Channel(std::shared_ptr<boost::asio::ip::udp::socket> socket) :
44 socket(socket)
Vernon Mauery9e801a22018-10-12 13:20:49 -070045 {
Vernon Mauery9e801a22018-10-12 13:20:49 -070046 }
Rajashekar Gade Reddy9979e992020-02-07 19:18:34 +053047 /**
48 * @brief Check if ip address is ipv4 mapped ipv6
49 *
50 * @param v6Addr : in6_addr obj
51 *
52 * @return true if ipv4 mapped ipv6 else return false
53 */
54 bool isIpv4InIpv6(const struct in6_addr& v6Addr) const
55 {
56 constexpr uint8_t prefix[v4v6Index] = {0, 0, 0, 0, 0, 0,
57 0, 0, 0, 0, 0xff, 0xff};
58 return 0 == std::memcmp(&v6Addr.s6_addr[0], &prefix[0], sizeof(prefix));
59 }
Vernon Mauery9e801a22018-10-12 13:20:49 -070060 /**
61 * @brief Fetch the IP address of the remote peer
62 *
Rajashekar Gade Reddy9979e992020-02-07 19:18:34 +053063 * @param remoteIpv4Addr : ipv4 address is assigned to it.
64 *
Vernon Mauery9e801a22018-10-12 13:20:49 -070065 * Returns the IP address of the remote peer which is connected to this
66 * socket
67 *
68 * @return IP address of the remote peer
69 */
Rajashekar Gade Reddy9979e992020-02-07 19:18:34 +053070 std::string getRemoteAddress(uint32_t& remoteIpv4Addr) const
Vernon Mauery7a0142c2018-11-09 08:38:16 -080071 {
Vernon Maueryd92bc322019-03-15 15:24:30 -070072 const char* retval = nullptr;
73 if (sockAddrSize == sizeof(sockaddr_in))
74 {
75 char ipv4addr[INET_ADDRSTRLEN];
Rajashekar Gade Reddy9979e992020-02-07 19:18:34 +053076 const sockaddr_in* sa =
77 reinterpret_cast<const sockaddr_in*>(&remoteSockAddr);
78 remoteIpv4Addr = sa->sin_addr.s_addr;
79 retval =
80 inet_ntop(AF_INET, &(sa->sin_addr), ipv4addr, sizeof(ipv4addr));
Vernon Maueryd92bc322019-03-15 15:24:30 -070081 }
82 else if (sockAddrSize == sizeof(sockaddr_in6))
83 {
84 char ipv6addr[INET6_ADDRSTRLEN];
Rajashekar Gade Reddy9979e992020-02-07 19:18:34 +053085 const sockaddr_in6* sa =
86 reinterpret_cast<const sockaddr_in6*>(&remoteSockAddr);
87
88 if (isIpv4InIpv6(sa->sin6_addr))
89 {
90 std::copy_n(&sa->sin6_addr.s6_addr[v4v6Index],
91 sizeof(remoteIpv4Addr),
92 reinterpret_cast<uint8_t*>(&remoteIpv4Addr));
93 }
94 retval = inet_ntop(AF_INET6, &(sa->sin6_addr), ipv6addr,
95 sizeof(ipv6addr));
Vernon Maueryd92bc322019-03-15 15:24:30 -070096 }
Rajashekar Gade Reddy9979e992020-02-07 19:18:34 +053097
Vernon Maueryd92bc322019-03-15 15:24:30 -070098 if (retval)
99 {
100 return retval;
101 }
102 phosphor::logging::log<phosphor::logging::level::ERR>(
103 "Error in inet_ntop",
104 phosphor::logging::entry("ERROR=%s", strerror(errno)));
105 return std::string();
Vernon Mauery7a0142c2018-11-09 08:38:16 -0800106 }
Tom Josephaaeb29e2016-08-10 06:36:33 -0500107
Vernon Mauery9e801a22018-10-12 13:20:49 -0700108 /**
109 * @brief Fetch the port number of the remote peer
110 *
111 * Returns the port number of the remote peer
112 *
113 * @return Port number
114 *
115 */
Vernon Maueryd92bc322019-03-15 15:24:30 -0700116 uint16_t getPort() const
Vernon Mauery9e801a22018-10-12 13:20:49 -0700117 {
Vernon Maueryd92bc322019-03-15 15:24:30 -0700118 if (sockAddrSize == sizeof(sockaddr_in))
119 {
120 return ntohs(reinterpret_cast<const sockaddr_in*>(&remoteSockAddr)
121 ->sin_port);
122 }
123 if (sockAddrSize == sizeof(sockaddr_in6))
124 {
125 return ntohs(reinterpret_cast<const sockaddr_in6*>(&remoteSockAddr)
126 ->sin6_port);
127 }
128 return 0;
Vernon Mauery9e801a22018-10-12 13:20:49 -0700129 }
Tom Josephaaeb29e2016-08-10 06:36:33 -0500130
Vernon Mauery9e801a22018-10-12 13:20:49 -0700131 /**
132 * @brief Read the incoming packet
133 *
134 * Reads the data available on the socket
135 *
136 * @return A tuple with return code and vector with the buffer
137 * In case of success, the vector is populated with the data
138 * available on the socket and return code is 0.
139 * In case of error, the return code is < 0 and vector is set
140 * to size 0.
141 */
Vernon Mauery7a0142c2018-11-09 08:38:16 -0800142 std::tuple<int, std::vector<uint8_t>> read()
143 {
Vernon Maueryd92bc322019-03-15 15:24:30 -0700144 // cannot use the standard asio reading mechanism because it does not
145 // provide a mechanism to reach down into the depths and use a msghdr
Vernon Mauery7a0142c2018-11-09 08:38:16 -0800146 std::vector<uint8_t> packet(socket->available());
Vernon Maueryd92bc322019-03-15 15:24:30 -0700147 iovec iov = {packet.data(), packet.size()};
148 char msgCtrl[1024];
149 msghdr msg = {&remoteSockAddr, sizeof(remoteSockAddr), &iov, 1,
150 msgCtrl, sizeof(msgCtrl), 0};
151
152 ssize_t bytesReceived = recvmsg(socket->native_handle(), &msg, 0);
153 // Read of the packet failed
154 if (bytesReceived < 0)
Vernon Mauery7a0142c2018-11-09 08:38:16 -0800155 {
Vernon Maueryd92bc322019-03-15 15:24:30 -0700156 // something bad happened; bail
157 phosphor::logging::log<phosphor::logging::level::ERR>(
158 "Error in recvmsg",
159 phosphor::logging::entry("ERROR=%s", strerror(errno)));
160 return std::make_tuple(-errno, std::vector<uint8_t>());
Vernon Mauery7a0142c2018-11-09 08:38:16 -0800161 }
Vernon Maueryd92bc322019-03-15 15:24:30 -0700162 // save the size of either ipv4 or i4v6 sockaddr
163 sockAddrSize = msg.msg_namelen;
164
165 // extract the destination address from the message
166 cmsghdr* cmsg;
167 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != 0;
168 cmsg = CMSG_NXTHDR(&msg, cmsg))
Vernon Mauery7a0142c2018-11-09 08:38:16 -0800169 {
Vernon Maueryd92bc322019-03-15 15:24:30 -0700170 if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO)
171 {
172 // save local address from the pktinfo4
173 pktinfo4 = *reinterpret_cast<in_pktinfo*>(CMSG_DATA(cmsg));
174 }
175 if (cmsg->cmsg_level == IPPROTO_IPV6 &&
176 cmsg->cmsg_type == IPV6_PKTINFO)
177 {
178 // save local address from the pktinfo6
179 pktinfo6 = *reinterpret_cast<in6_pktinfo*>(CMSG_DATA(cmsg));
180 }
Vernon Mauery7a0142c2018-11-09 08:38:16 -0800181 }
182 return std::make_tuple(0, packet);
183 }
Tom Josephaaeb29e2016-08-10 06:36:33 -0500184
Vernon Mauery9e801a22018-10-12 13:20:49 -0700185 /**
186 * @brief Write the outgoing packet
187 *
188 * Writes the data in the vector to the socket
189 *
190 * @param [in] inBuffer
191 * The vector would be the buffer of data to write to the socket.
192 *
Vernon Maueryd92bc322019-03-15 15:24:30 -0700193 * @return In case of success the return code is the number of bytes
194 * written and return code is < 0 in case of failure.
Vernon Mauery9e801a22018-10-12 13:20:49 -0700195 */
Vernon Mauery7a0142c2018-11-09 08:38:16 -0800196 int write(const std::vector<uint8_t>& inBuffer)
197 {
Vernon Maueryd92bc322019-03-15 15:24:30 -0700198 // in order to make sure packets go back out from the same
199 // IP address they came in on, sendmsg must be used instead
200 // of the boost::asio::ip::send or sendto
201 iovec iov = {const_cast<uint8_t*>(inBuffer.data()), inBuffer.size()};
202 char msgCtrl[1024];
203 msghdr msg = {&remoteSockAddr, sockAddrSize, &iov, 1,
204 msgCtrl, sizeof(msgCtrl), 0};
205 int cmsg_space = 0;
206 cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
207 if (pktinfo6)
Vernon Mauery7a0142c2018-11-09 08:38:16 -0800208 {
Vernon Maueryd92bc322019-03-15 15:24:30 -0700209 cmsg->cmsg_level = IPPROTO_IPV6;
210 cmsg->cmsg_type = IPV6_PKTINFO;
211 cmsg->cmsg_len = CMSG_LEN(sizeof(in6_pktinfo));
212 *reinterpret_cast<in6_pktinfo*>(CMSG_DATA(cmsg)) = *pktinfo6;
213 cmsg_space += CMSG_SPACE(sizeof(in6_pktinfo));
Vernon Mauery7a0142c2018-11-09 08:38:16 -0800214 }
Vernon Maueryd92bc322019-03-15 15:24:30 -0700215 else if (pktinfo4)
Vernon Mauery7a0142c2018-11-09 08:38:16 -0800216 {
Vernon Maueryd92bc322019-03-15 15:24:30 -0700217 cmsg->cmsg_level = IPPROTO_IP;
218 cmsg->cmsg_type = IP_PKTINFO;
219 cmsg->cmsg_len = CMSG_LEN(sizeof(in_pktinfo));
220 *reinterpret_cast<in_pktinfo*>(CMSG_DATA(cmsg)) = *pktinfo4;
221 cmsg_space += CMSG_SPACE(sizeof(in_pktinfo));
Vernon Mauery7a0142c2018-11-09 08:38:16 -0800222 }
Vernon Maueryd92bc322019-03-15 15:24:30 -0700223 msg.msg_controllen = cmsg_space;
224 int ret = sendmsg(socket->native_handle(), &msg, 0);
225 if (ret < 0)
226 {
227 phosphor::logging::log<phosphor::logging::level::ERR>(
228 "Error in sendmsg",
229 phosphor::logging::entry("ERROR=%s", strerror(errno)));
230 }
231 return ret;
Vernon Mauery7a0142c2018-11-09 08:38:16 -0800232 }
Tom Josephaaeb29e2016-08-10 06:36:33 -0500233
Vernon Mauery9e801a22018-10-12 13:20:49 -0700234 /**
235 * @brief Returns file descriptor for the socket
236 */
237 auto getHandle(void) const
238 {
Vernon Mauery7a0142c2018-11-09 08:38:16 -0800239 return socket->native_handle();
Vernon Mauery9e801a22018-10-12 13:20:49 -0700240 }
Tom Josephaaeb29e2016-08-10 06:36:33 -0500241
Vernon Mauery9e801a22018-10-12 13:20:49 -0700242 private:
Vernon Mauery7a0142c2018-11-09 08:38:16 -0800243 std::shared_ptr<boost::asio::ip::udp::socket> socket;
Vernon Maueryd92bc322019-03-15 15:24:30 -0700244 sockaddr_storage remoteSockAddr;
245 socklen_t sockAddrSize;
246 std::optional<in_pktinfo> pktinfo4;
247 std::optional<in6_pktinfo> pktinfo6;
Tom Josephaaeb29e2016-08-10 06:36:33 -0500248};
249
250} // namespace udpsocket