1 // Copyright (c) 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "quiche/quic/core/quic_linux_socket_utils.h"
6
7 #include <linux/net_tstamp.h>
8 #include <netinet/in.h>
9
10 #include <cstdint>
11
12 #include "quiche/quic/core/quic_syscall_wrapper.h"
13 #include "quiche/quic/platform/api/quic_ip_address.h"
14 #include "quiche/quic/platform/api/quic_logging.h"
15 #include "quiche/quic/platform/api/quic_socket_address.h"
16
17 namespace quic {
18
QuicMsgHdr(const char * buffer,size_t buf_len,const QuicSocketAddress & peer_address,char * cbuf,size_t cbuf_size)19 QuicMsgHdr::QuicMsgHdr(const char* buffer, size_t buf_len,
20 const QuicSocketAddress& peer_address, char* cbuf,
21 size_t cbuf_size)
22 : iov_{const_cast<char*>(buffer), buf_len},
23 cbuf_(cbuf),
24 cbuf_size_(cbuf_size),
25 cmsg_(nullptr) {
26 // Only support unconnected sockets.
27 QUICHE_DCHECK(peer_address.IsInitialized());
28
29 raw_peer_address_ = peer_address.generic_address();
30 hdr_.msg_name = &raw_peer_address_;
31 hdr_.msg_namelen = raw_peer_address_.ss_family == AF_INET
32 ? sizeof(sockaddr_in)
33 : sizeof(sockaddr_in6);
34
35 hdr_.msg_iov = &iov_;
36 hdr_.msg_iovlen = 1;
37 hdr_.msg_flags = 0;
38
39 hdr_.msg_control = nullptr;
40 hdr_.msg_controllen = 0;
41 }
42
SetIpInNextCmsg(const QuicIpAddress & self_address)43 void QuicMsgHdr::SetIpInNextCmsg(const QuicIpAddress& self_address) {
44 if (!self_address.IsInitialized()) {
45 return;
46 }
47
48 if (self_address.IsIPv4()) {
49 QuicLinuxSocketUtils::SetIpInfoInCmsgData(
50 self_address, GetNextCmsgData<in_pktinfo>(IPPROTO_IP, IP_PKTINFO));
51 } else {
52 QuicLinuxSocketUtils::SetIpInfoInCmsgData(
53 self_address, GetNextCmsgData<in6_pktinfo>(IPPROTO_IPV6, IPV6_PKTINFO));
54 }
55 }
56
GetNextCmsgDataInternal(int cmsg_level,int cmsg_type,size_t data_size)57 void* QuicMsgHdr::GetNextCmsgDataInternal(int cmsg_level, int cmsg_type,
58 size_t data_size) {
59 // msg_controllen needs to be increased first, otherwise CMSG_NXTHDR will
60 // return nullptr.
61 hdr_.msg_controllen += CMSG_SPACE(data_size);
62 QUICHE_DCHECK_LE(hdr_.msg_controllen, cbuf_size_);
63
64 if (cmsg_ == nullptr) {
65 QUICHE_DCHECK_EQ(nullptr, hdr_.msg_control);
66 memset(cbuf_, 0, cbuf_size_);
67 hdr_.msg_control = cbuf_;
68 cmsg_ = CMSG_FIRSTHDR(&hdr_);
69 } else {
70 QUICHE_DCHECK_NE(nullptr, hdr_.msg_control);
71 cmsg_ = CMSG_NXTHDR(&hdr_, cmsg_);
72 }
73
74 QUICHE_DCHECK_NE(nullptr, cmsg_) << "Insufficient control buffer space";
75
76 cmsg_->cmsg_len = CMSG_LEN(data_size);
77 cmsg_->cmsg_level = cmsg_level;
78 cmsg_->cmsg_type = cmsg_type;
79
80 return CMSG_DATA(cmsg_);
81 }
82
InitOneHeader(int i,const BufferedWrite & buffered_write)83 void QuicMMsgHdr::InitOneHeader(int i, const BufferedWrite& buffered_write) {
84 mmsghdr* mhdr = GetMMsgHdr(i);
85 msghdr* hdr = &mhdr->msg_hdr;
86 iovec* iov = GetIov(i);
87
88 iov->iov_base = const_cast<char*>(buffered_write.buffer);
89 iov->iov_len = buffered_write.buf_len;
90 hdr->msg_iov = iov;
91 hdr->msg_iovlen = 1;
92 hdr->msg_control = nullptr;
93 hdr->msg_controllen = 0;
94
95 // Only support unconnected sockets.
96 QUICHE_DCHECK(buffered_write.peer_address.IsInitialized());
97
98 sockaddr_storage* peer_address_storage = GetPeerAddressStorage(i);
99 *peer_address_storage = buffered_write.peer_address.generic_address();
100 hdr->msg_name = peer_address_storage;
101 hdr->msg_namelen = peer_address_storage->ss_family == AF_INET
102 ? sizeof(sockaddr_in)
103 : sizeof(sockaddr_in6);
104 }
105
SetIpInNextCmsg(int i,const QuicIpAddress & self_address)106 void QuicMMsgHdr::SetIpInNextCmsg(int i, const QuicIpAddress& self_address) {
107 if (!self_address.IsInitialized()) {
108 return;
109 }
110
111 if (self_address.IsIPv4()) {
112 QuicLinuxSocketUtils::SetIpInfoInCmsgData(
113 self_address, GetNextCmsgData<in_pktinfo>(i, IPPROTO_IP, IP_PKTINFO));
114 } else {
115 QuicLinuxSocketUtils::SetIpInfoInCmsgData(
116 self_address,
117 GetNextCmsgData<in6_pktinfo>(i, IPPROTO_IPV6, IPV6_PKTINFO));
118 }
119 }
120
GetNextCmsgDataInternal(int i,int cmsg_level,int cmsg_type,size_t data_size)121 void* QuicMMsgHdr::GetNextCmsgDataInternal(int i, int cmsg_level, int cmsg_type,
122 size_t data_size) {
123 mmsghdr* mhdr = GetMMsgHdr(i);
124 msghdr* hdr = &mhdr->msg_hdr;
125 cmsghdr*& cmsg = *GetCmsgHdr(i);
126
127 // msg_controllen needs to be increased first, otherwise CMSG_NXTHDR will
128 // return nullptr.
129 hdr->msg_controllen += CMSG_SPACE(data_size);
130 QUICHE_DCHECK_LE(hdr->msg_controllen, cbuf_size_);
131
132 if (cmsg == nullptr) {
133 QUICHE_DCHECK_EQ(nullptr, hdr->msg_control);
134 hdr->msg_control = GetCbuf(i);
135 cmsg = CMSG_FIRSTHDR(hdr);
136 } else {
137 QUICHE_DCHECK_NE(nullptr, hdr->msg_control);
138 cmsg = CMSG_NXTHDR(hdr, cmsg);
139 }
140
141 QUICHE_DCHECK_NE(nullptr, cmsg) << "Insufficient control buffer space";
142
143 cmsg->cmsg_len = CMSG_LEN(data_size);
144 cmsg->cmsg_level = cmsg_level;
145 cmsg->cmsg_type = cmsg_type;
146
147 return CMSG_DATA(cmsg);
148 }
149
num_bytes_sent(int num_packets_sent)150 int QuicMMsgHdr::num_bytes_sent(int num_packets_sent) {
151 QUICHE_DCHECK_LE(0, num_packets_sent);
152 QUICHE_DCHECK_LE(num_packets_sent, num_msgs_);
153
154 int bytes_sent = 0;
155 iovec* iov = GetIov(0);
156 for (int i = 0; i < num_packets_sent; ++i) {
157 bytes_sent += iov[i].iov_len;
158 }
159 return bytes_sent;
160 }
161
162 // static
GetUDPSegmentSize(int fd)163 int QuicLinuxSocketUtils::GetUDPSegmentSize(int fd) {
164 int optval;
165 socklen_t optlen = sizeof(optval);
166 int rc = getsockopt(fd, SOL_UDP, UDP_SEGMENT, &optval, &optlen);
167 if (rc < 0) {
168 QUIC_LOG_EVERY_N_SEC(INFO, 10)
169 << "getsockopt(UDP_SEGMENT) failed: " << strerror(errno);
170 return -1;
171 }
172 QUIC_LOG_EVERY_N_SEC(INFO, 10)
173 << "getsockopt(UDP_SEGMENT) returned segment size: " << optval;
174 return optval;
175 }
176
177 // static
EnableReleaseTime(int fd,clockid_t clockid)178 bool QuicLinuxSocketUtils::EnableReleaseTime(int fd, clockid_t clockid) {
179 // TODO(wub): Change to sock_txtime once it is available in linux/net_tstamp.h
180 struct LinuxSockTxTime {
181 clockid_t clockid; /* reference clockid */
182 uint32_t flags; /* flags defined by enum txtime_flags */
183 };
184
185 LinuxSockTxTime so_txtime_val{clockid, 0};
186
187 if (setsockopt(fd, SOL_SOCKET, SO_TXTIME, &so_txtime_val,
188 sizeof(so_txtime_val)) != 0) {
189 QUIC_LOG_EVERY_N_SEC(INFO, 10)
190 << "setsockopt(SOL_SOCKET,SO_TXTIME) failed: " << strerror(errno);
191 return false;
192 }
193
194 return true;
195 }
196
197 // static
GetTtlFromMsghdr(struct msghdr * hdr,int * ttl)198 bool QuicLinuxSocketUtils::GetTtlFromMsghdr(struct msghdr* hdr, int* ttl) {
199 if (hdr->msg_controllen > 0) {
200 struct cmsghdr* cmsg;
201 for (cmsg = CMSG_FIRSTHDR(hdr); cmsg != nullptr;
202 cmsg = CMSG_NXTHDR(hdr, cmsg)) {
203 if ((cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_TTL) ||
204 (cmsg->cmsg_level == IPPROTO_IPV6 &&
205 cmsg->cmsg_type == IPV6_HOPLIMIT)) {
206 *ttl = *(reinterpret_cast<int*>(CMSG_DATA(cmsg)));
207 return true;
208 }
209 }
210 }
211 return false;
212 }
213
214 // static
SetIpInfoInCmsgData(const QuicIpAddress & self_address,void * cmsg_data)215 void QuicLinuxSocketUtils::SetIpInfoInCmsgData(
216 const QuicIpAddress& self_address, void* cmsg_data) {
217 QUICHE_DCHECK(self_address.IsInitialized());
218 const std::string& address_str = self_address.ToPackedString();
219 if (self_address.IsIPv4()) {
220 in_pktinfo* pktinfo = static_cast<in_pktinfo*>(cmsg_data);
221 pktinfo->ipi_ifindex = 0;
222 memcpy(&pktinfo->ipi_spec_dst, address_str.c_str(), address_str.length());
223 } else if (self_address.IsIPv6()) {
224 in6_pktinfo* pktinfo = static_cast<in6_pktinfo*>(cmsg_data);
225 memcpy(&pktinfo->ipi6_addr, address_str.c_str(), address_str.length());
226 } else {
227 QUIC_BUG(quic_bug_10598_1) << "Unrecognized IPAddress";
228 }
229 }
230
231 // static
SetIpInfoInCmsg(const QuicIpAddress & self_address,cmsghdr * cmsg)232 size_t QuicLinuxSocketUtils::SetIpInfoInCmsg(const QuicIpAddress& self_address,
233 cmsghdr* cmsg) {
234 std::string address_string;
235 if (self_address.IsIPv4()) {
236 cmsg->cmsg_len = CMSG_LEN(sizeof(in_pktinfo));
237 cmsg->cmsg_level = IPPROTO_IP;
238 cmsg->cmsg_type = IP_PKTINFO;
239 in_pktinfo* pktinfo = reinterpret_cast<in_pktinfo*>(CMSG_DATA(cmsg));
240 memset(pktinfo, 0, sizeof(in_pktinfo));
241 pktinfo->ipi_ifindex = 0;
242 address_string = self_address.ToPackedString();
243 memcpy(&pktinfo->ipi_spec_dst, address_string.c_str(),
244 address_string.length());
245 return sizeof(in_pktinfo);
246 } else if (self_address.IsIPv6()) {
247 cmsg->cmsg_len = CMSG_LEN(sizeof(in6_pktinfo));
248 cmsg->cmsg_level = IPPROTO_IPV6;
249 cmsg->cmsg_type = IPV6_PKTINFO;
250 in6_pktinfo* pktinfo = reinterpret_cast<in6_pktinfo*>(CMSG_DATA(cmsg));
251 memset(pktinfo, 0, sizeof(in6_pktinfo));
252 address_string = self_address.ToPackedString();
253 memcpy(&pktinfo->ipi6_addr, address_string.c_str(),
254 address_string.length());
255 return sizeof(in6_pktinfo);
256 } else {
257 QUIC_BUG(quic_bug_10598_2) << "Unrecognized IPAddress";
258 return 0;
259 }
260 }
261
262 // static
WritePacket(int fd,const QuicMsgHdr & hdr)263 WriteResult QuicLinuxSocketUtils::WritePacket(int fd, const QuicMsgHdr& hdr) {
264 int rc;
265 do {
266 rc = GetGlobalSyscallWrapper()->Sendmsg(fd, hdr.hdr(), 0);
267 } while (rc < 0 && errno == EINTR);
268 if (rc >= 0) {
269 return WriteResult(WRITE_STATUS_OK, rc);
270 }
271 return WriteResult((errno == EAGAIN || errno == EWOULDBLOCK)
272 ? WRITE_STATUS_BLOCKED
273 : WRITE_STATUS_ERROR,
274 errno);
275 }
276
277 // static
WriteMultiplePackets(int fd,QuicMMsgHdr * mhdr,int * num_packets_sent)278 WriteResult QuicLinuxSocketUtils::WriteMultiplePackets(int fd,
279 QuicMMsgHdr* mhdr,
280 int* num_packets_sent) {
281 *num_packets_sent = 0;
282
283 if (mhdr->num_msgs() <= 0) {
284 return WriteResult(WRITE_STATUS_ERROR, EINVAL);
285 }
286
287 int rc;
288 do {
289 rc = GetGlobalSyscallWrapper()->Sendmmsg(fd, mhdr->mhdr(), mhdr->num_msgs(),
290 0);
291 } while (rc < 0 && errno == EINTR);
292
293 if (rc > 0) {
294 *num_packets_sent = rc;
295
296 return WriteResult(WRITE_STATUS_OK, mhdr->num_bytes_sent(rc));
297 } else if (rc == 0) {
298 QUIC_BUG(quic_bug_10598_3)
299 << "sendmmsg returned 0, returning WRITE_STATUS_ERROR. errno: "
300 << errno;
301 errno = EIO;
302 }
303
304 return WriteResult((errno == EAGAIN || errno == EWOULDBLOCK)
305 ? WRITE_STATUS_BLOCKED
306 : WRITE_STATUS_ERROR,
307 errno);
308 }
309
310 } // namespace quic
311