blob: 0edfcd50f514b9969c41a18d25a8e5f4ffac36d8 [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>
George Liu7b7f25f2022-07-04 17:07:32 +08007#include <phosphor-logging/lg2.hpp>
George Liubc8958f2022-07-04 09:29:49 +08008
Vernon Mauery7a0142c2018-11-09 08:38:16 -08009#include <memory>
Vernon Maueryd92bc322019-03-15 15:24:30 -070010#include <optional>
Tom Josephaaeb29e2016-08-10 06:36:33 -050011#include <string>
12#include <tuple>
Vernon Maueryd92bc322019-03-15 15:24:30 -070013#include <variant>
Tom Josephaaeb29e2016-08-10 06:36:33 -050014#include <vector>
15
16namespace udpsocket
17{
Rajashekar Gade Reddy9979e992020-02-07 19:18:34 +053018static constexpr uint8_t v4v6Index = 12;
Tom Josephaaeb29e2016-08-10 06:36:33 -050019
Tom Josephaaeb29e2016-08-10 06:36:33 -050020/** @class Channel
21 *
22 * @brief Provides encapsulation for UDP socket operations like Read, Peek,
23 * Write, Remote peer's IP Address and Port.
24 */
25class Channel
26{
Vernon Mauery9e801a22018-10-12 13:20:49 -070027 public:
Vernon Mauery7a0142c2018-11-09 08:38:16 -080028 Channel() = delete;
29 ~Channel() = default;
30 Channel(const Channel& right) = delete;
31 Channel& operator=(const Channel& right) = delete;
32 Channel(Channel&&) = delete;
33 Channel& operator=(Channel&&) = delete;
Tom Josephaaeb29e2016-08-10 06:36:33 -050034
Vernon Mauery9e801a22018-10-12 13:20:49 -070035 /**
36 * @brief Constructor
37 *
38 * Initialize the IPMI socket object with the socket descriptor
39 *
Vernon Mauery7a0142c2018-11-09 08:38:16 -080040 * @param [in] pointer to a boost::asio udp socket object
Vernon Mauery9e801a22018-10-12 13:20:49 -070041 *
42 * @return None
43 */
Vernon Mauery7a0142c2018-11-09 08:38:16 -080044 explicit Channel(std::shared_ptr<boost::asio::ip::udp::socket> socket) :
45 socket(socket)
George Liubc8958f2022-07-04 09:29:49 +080046 {}
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 }
George Liu7b7f25f2022-07-04 17:07:32 +0800102 lg2::error("Error in inet_ntop: {ERROR}", "ERROR", strerror(errno));
Vernon Maueryd92bc322019-03-15 15:24:30 -0700103 return std::string();
Vernon Mauery7a0142c2018-11-09 08:38:16 -0800104 }
Tom Josephaaeb29e2016-08-10 06:36:33 -0500105
Vernon Mauery9e801a22018-10-12 13:20:49 -0700106 /**
107 * @brief Fetch the port number of the remote peer
108 *
109 * Returns the port number of the remote peer
110 *
111 * @return Port number
112 *
113 */
Vernon Maueryd92bc322019-03-15 15:24:30 -0700114 uint16_t getPort() const
Vernon Mauery9e801a22018-10-12 13:20:49 -0700115 {
Vernon Maueryd92bc322019-03-15 15:24:30 -0700116 if (sockAddrSize == sizeof(sockaddr_in))
117 {
118 return ntohs(reinterpret_cast<const sockaddr_in*>(&remoteSockAddr)
119 ->sin_port);
120 }
121 if (sockAddrSize == sizeof(sockaddr_in6))
122 {
123 return ntohs(reinterpret_cast<const sockaddr_in6*>(&remoteSockAddr)
124 ->sin6_port);
125 }
126 return 0;
Vernon Mauery9e801a22018-10-12 13:20:49 -0700127 }
Tom Josephaaeb29e2016-08-10 06:36:33 -0500128
Vernon Mauery9e801a22018-10-12 13:20:49 -0700129 /**
130 * @brief Read the incoming packet
131 *
132 * Reads the data available on the socket
133 *
134 * @return A tuple with return code and vector with the buffer
135 * In case of success, the vector is populated with the data
136 * available on the socket and return code is 0.
137 * In case of error, the return code is < 0 and vector is set
138 * to size 0.
139 */
Vernon Mauery7a0142c2018-11-09 08:38:16 -0800140 std::tuple<int, std::vector<uint8_t>> read()
141 {
Vernon Maueryd92bc322019-03-15 15:24:30 -0700142 // cannot use the standard asio reading mechanism because it does not
143 // provide a mechanism to reach down into the depths and use a msghdr
Vernon Mauery7a0142c2018-11-09 08:38:16 -0800144 std::vector<uint8_t> packet(socket->available());
Vernon Maueryd92bc322019-03-15 15:24:30 -0700145 iovec iov = {packet.data(), packet.size()};
146 char msgCtrl[1024];
147 msghdr msg = {&remoteSockAddr, sizeof(remoteSockAddr), &iov, 1,
148 msgCtrl, sizeof(msgCtrl), 0};
149
150 ssize_t bytesReceived = recvmsg(socket->native_handle(), &msg, 0);
151 // Read of the packet failed
152 if (bytesReceived < 0)
Vernon Mauery7a0142c2018-11-09 08:38:16 -0800153 {
Vernon Maueryd92bc322019-03-15 15:24:30 -0700154 // something bad happened; bail
George Liu7b7f25f2022-07-04 17:07:32 +0800155 lg2::error("Error in recvmsg: {ERROR}", "ERROR",
156 strerror(-bytesReceived));
Vernon Maueryd92bc322019-03-15 15:24:30 -0700157 return std::make_tuple(-errno, std::vector<uint8_t>());
Vernon Mauery7a0142c2018-11-09 08:38:16 -0800158 }
Vernon Maueryd92bc322019-03-15 15:24:30 -0700159 // save the size of either ipv4 or i4v6 sockaddr
160 sockAddrSize = msg.msg_namelen;
161
162 // extract the destination address from the message
163 cmsghdr* cmsg;
164 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != 0;
165 cmsg = CMSG_NXTHDR(&msg, cmsg))
Vernon Mauery7a0142c2018-11-09 08:38:16 -0800166 {
Vernon Maueryd92bc322019-03-15 15:24:30 -0700167 if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO)
168 {
169 // save local address from the pktinfo4
170 pktinfo4 = *reinterpret_cast<in_pktinfo*>(CMSG_DATA(cmsg));
171 }
172 if (cmsg->cmsg_level == IPPROTO_IPV6 &&
173 cmsg->cmsg_type == IPV6_PKTINFO)
174 {
175 // save local address from the pktinfo6
176 pktinfo6 = *reinterpret_cast<in6_pktinfo*>(CMSG_DATA(cmsg));
177 }
Vernon Mauery7a0142c2018-11-09 08:38:16 -0800178 }
179 return std::make_tuple(0, packet);
180 }
Tom Josephaaeb29e2016-08-10 06:36:33 -0500181
Vernon Mauery9e801a22018-10-12 13:20:49 -0700182 /**
183 * @brief Write the outgoing packet
184 *
185 * Writes the data in the vector to the socket
186 *
187 * @param [in] inBuffer
188 * The vector would be the buffer of data to write to the socket.
189 *
Vernon Maueryd92bc322019-03-15 15:24:30 -0700190 * @return In case of success the return code is the number of bytes
191 * written and return code is < 0 in case of failure.
Vernon Mauery9e801a22018-10-12 13:20:49 -0700192 */
Vernon Mauery7a0142c2018-11-09 08:38:16 -0800193 int write(const std::vector<uint8_t>& inBuffer)
194 {
Vernon Maueryd92bc322019-03-15 15:24:30 -0700195 // in order to make sure packets go back out from the same
196 // IP address they came in on, sendmsg must be used instead
197 // of the boost::asio::ip::send or sendto
198 iovec iov = {const_cast<uint8_t*>(inBuffer.data()), inBuffer.size()};
199 char msgCtrl[1024];
200 msghdr msg = {&remoteSockAddr, sockAddrSize, &iov, 1,
201 msgCtrl, sizeof(msgCtrl), 0};
202 int cmsg_space = 0;
203 cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
204 if (pktinfo6)
Vernon Mauery7a0142c2018-11-09 08:38:16 -0800205 {
Vernon Maueryd92bc322019-03-15 15:24:30 -0700206 cmsg->cmsg_level = IPPROTO_IPV6;
207 cmsg->cmsg_type = IPV6_PKTINFO;
208 cmsg->cmsg_len = CMSG_LEN(sizeof(in6_pktinfo));
209 *reinterpret_cast<in6_pktinfo*>(CMSG_DATA(cmsg)) = *pktinfo6;
210 cmsg_space += CMSG_SPACE(sizeof(in6_pktinfo));
Vernon Mauery7a0142c2018-11-09 08:38:16 -0800211 }
Vernon Maueryd92bc322019-03-15 15:24:30 -0700212 else if (pktinfo4)
Vernon Mauery7a0142c2018-11-09 08:38:16 -0800213 {
Vernon Maueryd92bc322019-03-15 15:24:30 -0700214 cmsg->cmsg_level = IPPROTO_IP;
215 cmsg->cmsg_type = IP_PKTINFO;
216 cmsg->cmsg_len = CMSG_LEN(sizeof(in_pktinfo));
217 *reinterpret_cast<in_pktinfo*>(CMSG_DATA(cmsg)) = *pktinfo4;
218 cmsg_space += CMSG_SPACE(sizeof(in_pktinfo));
Vernon Mauery7a0142c2018-11-09 08:38:16 -0800219 }
Vernon Maueryd92bc322019-03-15 15:24:30 -0700220 msg.msg_controllen = cmsg_space;
221 int ret = sendmsg(socket->native_handle(), &msg, 0);
222 if (ret < 0)
223 {
George Liu7b7f25f2022-07-04 17:07:32 +0800224 lg2::error("Error in sendmsg: {ERROR}", "ERROR", strerror(-ret));
Vernon Maueryd92bc322019-03-15 15:24:30 -0700225 }
226 return ret;
Vernon Mauery7a0142c2018-11-09 08:38:16 -0800227 }
Tom Josephaaeb29e2016-08-10 06:36:33 -0500228
Vernon Mauery9e801a22018-10-12 13:20:49 -0700229 /**
230 * @brief Returns file descriptor for the socket
231 */
232 auto getHandle(void) const
233 {
Vernon Mauery7a0142c2018-11-09 08:38:16 -0800234 return socket->native_handle();
Vernon Mauery9e801a22018-10-12 13:20:49 -0700235 }
Tom Josephaaeb29e2016-08-10 06:36:33 -0500236
Vernon Mauery9e801a22018-10-12 13:20:49 -0700237 private:
Vernon Mauery7a0142c2018-11-09 08:38:16 -0800238 std::shared_ptr<boost::asio::ip::udp::socket> socket;
Vernon Maueryd92bc322019-03-15 15:24:30 -0700239 sockaddr_storage remoteSockAddr;
240 socklen_t sockAddrSize;
241 std::optional<in_pktinfo> pktinfo4;
242 std::optional<in6_pktinfo> pktinfo6;
Tom Josephaaeb29e2016-08-10 06:36:33 -0500243};
244
245} // namespace udpsocket