-
Notifications
You must be signed in to change notification settings - Fork 11
/
Copy pathsocket_channel.hpp
245 lines (230 loc) · 7.87 KB
/
socket_channel.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
#pragma once
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <boost/asio/ip/udp.hpp>
#include <phosphor-logging/lg2.hpp>
#include <memory>
#include <optional>
#include <string>
#include <tuple>
#include <variant>
#include <vector>
namespace udpsocket
{
static constexpr uint8_t v4v6Index = 12;
/** @class Channel
*
* @brief Provides encapsulation for UDP socket operations like Read, Peek,
* Write, Remote peer's IP Address and Port.
*/
class Channel
{
public:
Channel() = delete;
~Channel() = default;
Channel(const Channel& right) = delete;
Channel& operator=(const Channel& right) = delete;
Channel(Channel&&) = delete;
Channel& operator=(Channel&&) = delete;
/**
* @brief Constructor
*
* Initialize the IPMI socket object with the socket descriptor
*
* @param [in] pointer to a boost::asio udp socket object
*
* @return None
*/
explicit Channel(std::shared_ptr<boost::asio::ip::udp::socket> socket) :
socket(socket)
{}
/**
* @brief Check if ip address is ipv4 mapped ipv6
*
* @param v6Addr : in6_addr obj
*
* @return true if ipv4 mapped ipv6 else return false
*/
bool isIpv4InIpv6(const struct in6_addr& v6Addr) const
{
constexpr uint8_t prefix[v4v6Index] = {0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0xff, 0xff};
return 0 == std::memcmp(&v6Addr.s6_addr[0], &prefix[0], sizeof(prefix));
}
/**
* @brief Fetch the IP address of the remote peer
*
* @param remoteIpv4Addr : ipv4 address is assigned to it.
*
* Returns the IP address of the remote peer which is connected to this
* socket
*
* @return IP address of the remote peer
*/
std::string getRemoteAddress(uint32_t& remoteIpv4Addr) const
{
const char* retval = nullptr;
if (sockAddrSize == sizeof(sockaddr_in))
{
char ipv4addr[INET_ADDRSTRLEN];
const sockaddr_in* sa =
reinterpret_cast<const sockaddr_in*>(&remoteSockAddr);
remoteIpv4Addr = sa->sin_addr.s_addr;
retval =
inet_ntop(AF_INET, &(sa->sin_addr), ipv4addr, sizeof(ipv4addr));
}
else if (sockAddrSize == sizeof(sockaddr_in6))
{
char ipv6addr[INET6_ADDRSTRLEN];
const sockaddr_in6* sa =
reinterpret_cast<const sockaddr_in6*>(&remoteSockAddr);
if (isIpv4InIpv6(sa->sin6_addr))
{
std::copy_n(&sa->sin6_addr.s6_addr[v4v6Index],
sizeof(remoteIpv4Addr),
reinterpret_cast<uint8_t*>(&remoteIpv4Addr));
}
retval = inet_ntop(AF_INET6, &(sa->sin6_addr), ipv6addr,
sizeof(ipv6addr));
}
if (retval)
{
return retval;
}
lg2::error("Error in inet_ntop: {ERROR}", "ERROR", strerror(errno));
return std::string();
}
/**
* @brief Fetch the port number of the remote peer
*
* Returns the port number of the remote peer
*
* @return Port number
*
*/
uint16_t getPort() const
{
if (sockAddrSize == sizeof(sockaddr_in))
{
return ntohs(reinterpret_cast<const sockaddr_in*>(&remoteSockAddr)
->sin_port);
}
if (sockAddrSize == sizeof(sockaddr_in6))
{
return ntohs(reinterpret_cast<const sockaddr_in6*>(&remoteSockAddr)
->sin6_port);
}
return 0;
}
/**
* @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, std::vector<uint8_t>> read()
{
// cannot use the standard asio reading mechanism because it does not
// provide a mechanism to reach down into the depths and use a msghdr
std::vector<uint8_t> packet(socket->available());
iovec iov = {packet.data(), packet.size()};
char msgCtrl[1024];
msghdr msg = {&remoteSockAddr, sizeof(remoteSockAddr), &iov, 1,
msgCtrl, sizeof(msgCtrl), 0};
ssize_t bytesReceived = recvmsg(socket->native_handle(), &msg, 0);
// Read of the packet failed
if (bytesReceived < 0)
{
// something bad happened; bail
lg2::error("Error in recvmsg: {ERROR}", "ERROR",
strerror(-bytesReceived));
return std::make_tuple(-errno, std::vector<uint8_t>());
}
// save the size of either ipv4 or i4v6 sockaddr
sockAddrSize = msg.msg_namelen;
// extract the destination address from the message
cmsghdr* cmsg;
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != 0;
cmsg = CMSG_NXTHDR(&msg, cmsg))
{
if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO)
{
// save local address from the pktinfo4
pktinfo4 = *reinterpret_cast<in_pktinfo*>(CMSG_DATA(cmsg));
}
if (cmsg->cmsg_level == IPPROTO_IPV6 &&
cmsg->cmsg_type == IPV6_PKTINFO)
{
// save local address from the pktinfo6
pktinfo6 = *reinterpret_cast<in6_pktinfo*>(CMSG_DATA(cmsg));
}
}
return std::make_tuple(0, packet);
}
/**
* @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 the number of bytes
* written and return code is < 0 in case of failure.
*/
int write(const std::vector<uint8_t>& inBuffer)
{
// in order to make sure packets go back out from the same
// IP address they came in on, sendmsg must be used instead
// of the boost::asio::ip::send or sendto
iovec iov = {const_cast<uint8_t*>(inBuffer.data()), inBuffer.size()};
char msgCtrl[1024];
msghdr msg = {&remoteSockAddr, sockAddrSize, &iov, 1,
msgCtrl, sizeof(msgCtrl), 0};
int cmsg_space = 0;
cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
if (pktinfo6)
{
cmsg->cmsg_level = IPPROTO_IPV6;
cmsg->cmsg_type = IPV6_PKTINFO;
cmsg->cmsg_len = CMSG_LEN(sizeof(in6_pktinfo));
*reinterpret_cast<in6_pktinfo*>(CMSG_DATA(cmsg)) = *pktinfo6;
cmsg_space += CMSG_SPACE(sizeof(in6_pktinfo));
}
else if (pktinfo4)
{
cmsg->cmsg_level = IPPROTO_IP;
cmsg->cmsg_type = IP_PKTINFO;
cmsg->cmsg_len = CMSG_LEN(sizeof(in_pktinfo));
*reinterpret_cast<in_pktinfo*>(CMSG_DATA(cmsg)) = *pktinfo4;
cmsg_space += CMSG_SPACE(sizeof(in_pktinfo));
}
msg.msg_controllen = cmsg_space;
int ret = sendmsg(socket->native_handle(), &msg, 0);
if (ret < 0)
{
lg2::error("Error in sendmsg: {ERROR}", "ERROR", strerror(-ret));
}
return ret;
}
/**
* @brief Returns file descriptor for the socket
*/
auto getHandle(void) const
{
return socket->native_handle();
}
private:
std::shared_ptr<boost::asio::ip::udp::socket> socket;
sockaddr_storage remoteSockAddr;
socklen_t sockAddrSize;
std::optional<in_pktinfo> pktinfo4;
std::optional<in6_pktinfo> pktinfo6;
};
} // namespace udpsocket