xref: /aosp_15_r20/external/cronet/net/third_party/quiche/src/quiche/quic/core/quic_linux_socket_utils.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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