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 #ifndef QUICHE_QUIC_CORE_QUIC_LINUX_SOCKET_UTILS_H_ 6 #define QUICHE_QUIC_CORE_QUIC_LINUX_SOCKET_UTILS_H_ 7 8 #include <errno.h> 9 #include <stddef.h> 10 #include <string.h> 11 #include <sys/socket.h> 12 #include <sys/uio.h> 13 14 #include <deque> 15 #include <functional> 16 #include <iterator> 17 #include <memory> 18 #include <type_traits> 19 #include <utility> 20 21 #include "quiche/quic/core/quic_packet_writer.h" 22 #include "quiche/quic/core/quic_types.h" 23 #include "quiche/quic/platform/api/quic_bug_tracker.h" 24 #include "quiche/quic/platform/api/quic_ip_address.h" 25 #include "quiche/quic/platform/api/quic_logging.h" 26 #include "quiche/quic/platform/api/quic_socket_address.h" 27 #include "quiche/common/quiche_callbacks.h" 28 29 #ifndef SOL_UDP 30 #define SOL_UDP 17 31 #endif 32 33 #ifndef UDP_SEGMENT 34 #define UDP_SEGMENT 103 35 #endif 36 37 #ifndef UDP_MAX_SEGMENTS 38 #define UDP_MAX_SEGMENTS (1 << 6UL) 39 #endif 40 41 #ifndef SO_TXTIME 42 #define SO_TXTIME 61 43 #endif 44 45 namespace quic { 46 47 inline constexpr int kCmsgSpaceForIpv4 = CMSG_SPACE(sizeof(in_pktinfo)); 48 inline constexpr int kCmsgSpaceForIpv6 = CMSG_SPACE(sizeof(in6_pktinfo)); 49 // kCmsgSpaceForIp should be big enough to hold both IPv4 and IPv6 packet info. 50 inline constexpr int kCmsgSpaceForIp = (kCmsgSpaceForIpv4 < kCmsgSpaceForIpv6) 51 ? kCmsgSpaceForIpv6 52 : kCmsgSpaceForIpv4; 53 54 inline constexpr int kCmsgSpaceForSegmentSize = CMSG_SPACE(sizeof(uint16_t)); 55 56 inline constexpr int kCmsgSpaceForTxTime = CMSG_SPACE(sizeof(uint64_t)); 57 58 inline constexpr int kCmsgSpaceForTTL = CMSG_SPACE(sizeof(int)); 59 60 inline constexpr int kCmsgSpaceForTOS = CMSG_SPACE(sizeof(int)); 61 62 // QuicMsgHdr is used to build msghdr objects that can be used send packets via 63 // ::sendmsg. 64 // 65 // Example: 66 // // cbuf holds control messages(cmsgs). The size is determined from what 67 // // cmsgs will be set for this msghdr. 68 // char cbuf[kCmsgSpaceForIp + kCmsgSpaceForSegmentSize]; 69 // QuicMsgHdr hdr(packet_buf, packet_buf_len, peer_addr, cbuf, sizeof(cbuf)); 70 // 71 // // Set IP in cmsgs. 72 // hdr.SetIpInNextCmsg(self_addr); 73 // 74 // // Set GSO size in cmsgs. 75 // *hdr.GetNextCmsgData<uint16_t>(SOL_UDP, UDP_SEGMENT) = 1200; 76 // 77 // QuicLinuxSocketUtils::WritePacket(fd, hdr); 78 class QUICHE_EXPORT QuicMsgHdr { 79 public: 80 QuicMsgHdr(const char* buffer, size_t buf_len, 81 const QuicSocketAddress& peer_address, char* cbuf, 82 size_t cbuf_size); 83 84 // Set IP info in the next cmsg. Both IPv4 and IPv6 are supported. 85 void SetIpInNextCmsg(const QuicIpAddress& self_address); 86 87 template <typename DataType> GetNextCmsgData(int cmsg_level,int cmsg_type)88 DataType* GetNextCmsgData(int cmsg_level, int cmsg_type) { 89 return reinterpret_cast<DataType*>( 90 GetNextCmsgDataInternal(cmsg_level, cmsg_type, sizeof(DataType))); 91 } 92 hdr()93 const msghdr* hdr() const { return &hdr_; } 94 95 protected: 96 void* GetNextCmsgDataInternal(int cmsg_level, int cmsg_type, 97 size_t data_size); 98 99 msghdr hdr_; 100 iovec iov_; 101 sockaddr_storage raw_peer_address_; 102 char* cbuf_; 103 const size_t cbuf_size_; 104 // The last cmsg populated so far. nullptr means nothing has been populated. 105 cmsghdr* cmsg_; 106 }; 107 108 // BufferedWrite holds all information needed to send a packet. 109 struct QUICHE_EXPORT BufferedWrite { BufferedWriteBufferedWrite110 BufferedWrite(const char* buffer, size_t buf_len, 111 const QuicIpAddress& self_address, 112 const QuicSocketAddress& peer_address) 113 : BufferedWrite(buffer, buf_len, self_address, peer_address, 114 std::unique_ptr<PerPacketOptions>(), 115 QuicPacketWriterParams(), /*release_time=*/0) {} 116 BufferedWriteBufferedWrite117 BufferedWrite(const char* buffer, size_t buf_len, 118 const QuicIpAddress& self_address, 119 const QuicSocketAddress& peer_address, 120 std::unique_ptr<PerPacketOptions> options, 121 const QuicPacketWriterParams& params, uint64_t release_time) 122 : buffer(buffer), 123 buf_len(buf_len), 124 self_address(self_address), 125 peer_address(peer_address), 126 options(std::move(options)), 127 params(params), 128 release_time(release_time) {} 129 130 const char* buffer; // Not owned. 131 size_t buf_len; 132 QuicIpAddress self_address; 133 QuicSocketAddress peer_address; 134 std::unique_ptr<PerPacketOptions> options; 135 QuicPacketWriterParams params; 136 137 // The release time according to the owning packet writer's clock, which is 138 // often not a QuicClock. Calculated from packet writer's Now() and the 139 // release time delay in |options|. 140 // 0 means it can be sent at the same time as the previous packet in a batch, 141 // or can be sent Now() if this is the first packet of a batch. 142 uint64_t release_time; 143 }; 144 145 // QuicMMsgHdr is used to build mmsghdr objects that can be used to send 146 // multiple packets at once via ::sendmmsg. 147 // 148 // Example: 149 // quiche::QuicheCircularDeque<BufferedWrite> buffered_writes; 150 // ... (Populate buffered_writes) ... 151 // 152 // QuicMMsgHdr mhdr( 153 // buffered_writes.begin(), buffered_writes.end(), kCmsgSpaceForIp, 154 // [](QuicMMsgHdr* mhdr, int i, const BufferedWrite& buffered_write) { 155 // mhdr->SetIpInNextCmsg(i, buffered_write.self_address); 156 // }); 157 // 158 // int num_packets_sent; 159 // QuicSocketUtils::WriteMultiplePackets(fd, &mhdr, &num_packets_sent); 160 class QUICHE_EXPORT QuicMMsgHdr { 161 public: 162 using ControlBufferInitializer = quiche::UnretainedCallback<void( 163 QuicMMsgHdr* mhdr, int i, const BufferedWrite& buffered_write)>; 164 template <typename IteratorT> 165 QuicMMsgHdr( 166 const IteratorT& first, const IteratorT& last, size_t cbuf_size, 167 ControlBufferInitializer cbuf_initializer = 168 +[](quic::QuicMMsgHdr*, int, const quic::BufferedWrite&) {}) num_msgs_(std::distance (first,last))169 : num_msgs_(std::distance(first, last)), cbuf_size_(cbuf_size) { 170 static_assert( 171 std::is_same<typename std::iterator_traits<IteratorT>::value_type, 172 BufferedWrite>::value, 173 "Must iterate over a collection of BufferedWrite."); 174 175 QUICHE_DCHECK_LE(0, num_msgs_); 176 if (num_msgs_ == 0) { 177 return; 178 } 179 180 storage_.reset(new char[StorageSize()]); 181 memset(&storage_[0], 0, StorageSize()); 182 183 int i = -1; 184 for (auto it = first; it != last; ++it) { 185 ++i; 186 187 InitOneHeader(i, *it); 188 cbuf_initializer(this, i, *it); 189 } 190 } 191 192 void SetIpInNextCmsg(int i, const QuicIpAddress& self_address); 193 194 template <typename DataType> GetNextCmsgData(int i,int cmsg_level,int cmsg_type)195 DataType* GetNextCmsgData(int i, int cmsg_level, int cmsg_type) { 196 return reinterpret_cast<DataType*>( 197 GetNextCmsgDataInternal(i, cmsg_level, cmsg_type, sizeof(DataType))); 198 } 199 mhdr()200 mmsghdr* mhdr() { return GetMMsgHdr(0); } 201 num_msgs()202 int num_msgs() const { return num_msgs_; } 203 204 // Get the total number of bytes in the first |num_packets_sent| packets. 205 int num_bytes_sent(int num_packets_sent); 206 207 protected: 208 void InitOneHeader(int i, const BufferedWrite& buffered_write); 209 210 void* GetNextCmsgDataInternal(int i, int cmsg_level, int cmsg_type, 211 size_t data_size); 212 StorageSize()213 size_t StorageSize() const { 214 return num_msgs_ * 215 (sizeof(mmsghdr) + sizeof(iovec) + sizeof(sockaddr_storage) + 216 sizeof(cmsghdr*) + cbuf_size_); 217 } 218 GetMMsgHdr(int i)219 mmsghdr* GetMMsgHdr(int i) { 220 auto* first = reinterpret_cast<mmsghdr*>(&storage_[0]); 221 return &first[i]; 222 } 223 GetIov(int i)224 iovec* GetIov(int i) { 225 auto* first = reinterpret_cast<iovec*>(GetMMsgHdr(num_msgs_)); 226 return &first[i]; 227 } 228 GetPeerAddressStorage(int i)229 sockaddr_storage* GetPeerAddressStorage(int i) { 230 auto* first = reinterpret_cast<sockaddr_storage*>(GetIov(num_msgs_)); 231 return &first[i]; 232 } 233 GetCmsgHdr(int i)234 cmsghdr** GetCmsgHdr(int i) { 235 auto* first = reinterpret_cast<cmsghdr**>(GetPeerAddressStorage(num_msgs_)); 236 return &first[i]; 237 } 238 GetCbuf(int i)239 char* GetCbuf(int i) { 240 auto* first = reinterpret_cast<char*>(GetCmsgHdr(num_msgs_)); 241 return &first[i * cbuf_size_]; 242 } 243 244 const int num_msgs_; 245 // Size of cmsg buffer for each message. 246 const size_t cbuf_size_; 247 // storage_ holds the memory of 248 // |num_msgs_| mmsghdr 249 // |num_msgs_| iovec 250 // |num_msgs_| sockaddr_storage, for peer addresses 251 // |num_msgs_| cmsghdr* 252 // |num_msgs_| cbuf, each of size cbuf_size 253 std::unique_ptr<char[]> storage_; 254 }; 255 256 class QUICHE_EXPORT QuicLinuxSocketUtils { 257 public: 258 // Return the UDP segment size of |fd|, 0 means segment size has not been set 259 // on this socket. If GSO is not supported, return -1. 260 static int GetUDPSegmentSize(int fd); 261 262 // Enable release time on |fd|. 263 static bool EnableReleaseTime(int fd, clockid_t clockid); 264 265 // If the msghdr contains an IP_TTL entry, this will set ttl to the correct 266 // value and return true. Otherwise it will return false. 267 static bool GetTtlFromMsghdr(struct msghdr* hdr, int* ttl); 268 269 // Set IP(self_address) in |cmsg_data|. Does not touch other fields in the 270 // containing cmsghdr. 271 static void SetIpInfoInCmsgData(const QuicIpAddress& self_address, 272 void* cmsg_data); 273 274 // A helper for WritePacket which fills in the cmsg with the supplied self 275 // address. 276 // Returns the length of the packet info structure used. 277 static size_t SetIpInfoInCmsg(const QuicIpAddress& self_address, 278 cmsghdr* cmsg); 279 280 // Writes the packet in |hdr| to the socket, using ::sendmsg. 281 static WriteResult WritePacket(int fd, const QuicMsgHdr& hdr); 282 283 // Writes the packets in |mhdr| to the socket, using ::sendmmsg if available. 284 static WriteResult WriteMultiplePackets(int fd, QuicMMsgHdr* mhdr, 285 int* num_packets_sent); 286 }; 287 288 } // namespace quic 289 290 #endif // QUICHE_QUIC_CORE_QUIC_LINUX_SOCKET_UTILS_H_ 291