gfe-relnote: Add QuicUdpSocketApi, a de-platformized version of QuicSocketUtils, and use it in some non-production code. No behavior change, not protected. This is the first step to kill quic_socket_utils.(h|cc). Chromium merge notes: 1) The platform impl function, GetGooglePacketHeadersFromControlMessageImpl, can simply return false. 2) Please add quic/core/quic_udp_socket* to "epoll_quic_tools" in BUILD.gn: http://shortn/_zpf4caOcsa PiperOrigin-RevId: 289439832 Change-Id: I3bf2d1f21314bcfb2c3a72f74fab25a22d49d49d
diff --git a/quic/core/quic_default_packet_writer.cc b/quic/core/quic_default_packet_writer.cc index d092958..4222005 100644 --- a/quic/core/quic_default_packet_writer.cc +++ b/quic/core/quic_default_packet_writer.cc
@@ -4,7 +4,7 @@ #include "net/third_party/quiche/src/quic/core/quic_default_packet_writer.h" -#include "net/quic/platform/impl/quic_socket_utils.h" +#include "net/third_party/quiche/src/quic/core/quic_udp_socket.h" namespace quic { @@ -22,8 +22,11 @@ DCHECK(!write_blocked_); DCHECK(nullptr == options) << "QuicDefaultPacketWriter does not accept any options."; - WriteResult result = QuicSocketUtils::WritePacket(fd_, buffer, buf_len, - self_address, peer_address); + QuicUdpPacketInfo packet_info; + packet_info.SetPeerAddress(peer_address); + packet_info.SetSelfIp(self_address); + WriteResult result = + QuicUdpSocketApi().WritePacket(fd_, buffer, buf_len, packet_info); if (IsWriteBlockedStatus(result.status)) { write_blocked_ = true; }
diff --git a/quic/core/quic_udp_socket.h b/quic/core/quic_udp_socket.h new file mode 100644 index 0000000..86972d1 --- /dev/null +++ b/quic/core/quic_udp_socket.h
@@ -0,0 +1,225 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef QUICHE_QUIC_CORE_QUIC_UDP_SOCKET_H_ +#define QUICHE_QUIC_CORE_QUIC_UDP_SOCKET_H_ + +#include <cstddef> +#include <cstdint> + +#include <type_traits> + +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_ip_address.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h" + +namespace quic { + +#if defined(_WIN32) +using QuicUdpSocketFd = SOCKET; +const QuicUdpSocketFd kQuicInvalidSocketFd = INVALID_SOCKET; +#else +using QuicUdpSocketFd = int; +const QuicUdpSocketFd kQuicInvalidSocketFd = -1; +#endif + +enum class QuicUdpPacketInfoBit : uint8_t { + DROPPED_PACKETS = 0, // Read + V4_SELF_IP, // Read + V6_SELF_IP, // Read + PEER_ADDRESS, // Read & Write + RECV_TIMESTAMP, // Read + TTL, // Read & Write + GOOGLE_PACKET_HEADER, // Read + NUM_BITS, +}; +static_assert(static_cast<size_t>(QuicUdpPacketInfoBit::NUM_BITS) <= + BitMask64::NumBits(), + "BitMask64 not wide enough to hold all bits."); + +// BufferSpan points to an unowned buffer, copying this structure only copies +// the pointer and length, not the buffer itself. +struct QUIC_EXPORT_PRIVATE BufferSpan { + BufferSpan(char* buffer, size_t buffer_len) + : buffer(buffer), buffer_len(buffer_len) {} + + BufferSpan() = default; + BufferSpan(const BufferSpan& other) = default; + BufferSpan& operator=(const BufferSpan& other) = default; + + char* buffer = nullptr; + size_t buffer_len = 0; +}; + +// QuicUdpPacketInfo contains per-packet information used for sending and +// receiving. +class QUIC_EXPORT_PRIVATE QuicUdpPacketInfo { + public: + BitMask64 bitmask() const { return bitmask_; } + + bool HasValue(QuicUdpPacketInfoBit bit) const { return bitmask_.IsSet(bit); } + + QuicPacketCount dropped_packets() const { + DCHECK(HasValue(QuicUdpPacketInfoBit::DROPPED_PACKETS)); + return dropped_packets_; + } + + void SetDroppedPackets(QuicPacketCount dropped_packets) { + dropped_packets_ = dropped_packets; + bitmask_.Set(QuicUdpPacketInfoBit::DROPPED_PACKETS); + } + + const QuicIpAddress& self_v4_ip() const { + DCHECK(HasValue(QuicUdpPacketInfoBit::V4_SELF_IP)); + return self_v4_ip_; + } + + void SetSelfV4Ip(QuicIpAddress self_v4_ip) { + self_v4_ip_ = self_v4_ip; + bitmask_.Set(QuicUdpPacketInfoBit::V4_SELF_IP); + } + + const QuicIpAddress& self_v6_ip() const { + DCHECK(HasValue(QuicUdpPacketInfoBit::V6_SELF_IP)); + return self_v6_ip_; + } + + void SetSelfV6Ip(QuicIpAddress self_v6_ip) { + self_v6_ip_ = self_v6_ip; + bitmask_.Set(QuicUdpPacketInfoBit::V6_SELF_IP); + } + + void SetSelfIp(QuicIpAddress self_ip) { + if (self_ip.IsIPv4()) { + SetSelfV4Ip(self_ip); + } else { + SetSelfV6Ip(self_ip); + } + } + + const QuicSocketAddress& peer_address() const { + DCHECK(HasValue(QuicUdpPacketInfoBit::PEER_ADDRESS)); + return peer_address_; + } + + void SetPeerAddress(QuicSocketAddress peer_address) { + peer_address_ = peer_address; + bitmask_.Set(QuicUdpPacketInfoBit::PEER_ADDRESS); + } + + QuicWallTime receive_timestamp() const { + DCHECK(HasValue(QuicUdpPacketInfoBit::RECV_TIMESTAMP)); + return receive_timestamp_; + } + + void SetReceiveTimestamp(QuicWallTime receive_timestamp) { + receive_timestamp_ = receive_timestamp; + bitmask_.Set(QuicUdpPacketInfoBit::RECV_TIMESTAMP); + } + + int ttl() const { + DCHECK(HasValue(QuicUdpPacketInfoBit::TTL)); + return ttl_; + } + + void SetTtl(int ttl) { + ttl_ = ttl; + bitmask_.Set(QuicUdpPacketInfoBit::TTL); + } + + void SetGooglePacketHeaders(BufferSpan google_packet_headers) { + google_packet_headers_ = google_packet_headers; + bitmask_.Set(QuicUdpPacketInfoBit::GOOGLE_PACKET_HEADER); + } + + private: + BitMask64 bitmask_; + QuicPacketCount dropped_packets_; + QuicIpAddress self_v4_ip_; + QuicIpAddress self_v6_ip_; + QuicSocketAddress peer_address_; + QuicWallTime receive_timestamp_ = QuicWallTime::Zero(); + int ttl_; + BufferSpan google_packet_headers_; +}; + +// QuicUdpSocketApi provides a minimal set of apis for sending and receiving +// udp packets. The low level udp socket apis differ between kernels and kernel +// versions, the goal of QuicUdpSocketApi is to hide such differences. +// We use non-static functions because it is easier to be mocked in tests when +// needed. +class QUIC_EXPORT_PRIVATE QuicUdpSocketApi { + public: + // Creates a non-blocking udp socket, sets the receive/send buffer and enable + // receiving of self ip addresses on read. + // Return kQuicInvalidSocketFd if failed. + QuicUdpSocketFd Create(int address_family, + int receive_buffer_size, + int send_buffer_size); + + // Closes |fd|. No-op if |fd| equals to kQuicInvalidSocketFd. + void Destroy(QuicUdpSocketFd fd); + + // Bind |fd| to |address|. If |address|'s port number is 0, kernel will choose + // a random port to bind to. Caller can use QuicSocketAddress::FromSocket(fd) + // to get the bound random port. + bool Bind(QuicUdpSocketFd fd, QuicSocketAddress address); + + // Enable receiving of various per-packet information. Return true if the + // corresponding information can be received on read. + bool EnableDroppedPacketCount(QuicUdpSocketFd fd); + bool EnableReceiveTimestamp(QuicUdpSocketFd fd); + bool EnableReceiveTtlForV4(QuicUdpSocketFd fd); + bool EnableReceiveTtlForV6(QuicUdpSocketFd fd); + + // Wait for |fd| to become readable, up to |timeout|. + // Return true if |fd| is readable upon return. + bool WaitUntilReadable(QuicUdpSocketFd fd, QuicTime::Delta timeout); + + struct QUIC_EXPORT_PRIVATE ReadPacketResult { + bool ok = false; + QuicUdpPacketInfo packet_info; + BufferSpan packet_buffer; + BufferSpan control_buffer; + }; + // Read a packet from |fd|: + // packet_info_interested: Bitmask indicating what information caller wants to + // receive into |result->packet_info|. + // result->packet_info: Received per packet information. + // result->packet_buffer: The packet buffer, to be filled with packet data. + // result->control_buffer: The control buffer, used by ReadPacket internally. + // It is recommended to be at least 512 bytes. + // result->ok: True iff a packet is successfully received. + void ReadPacket(QuicUdpSocketFd fd, + BitMask64 packet_info_interested, + ReadPacketResult* result); + + using ReadPacketResults = std::vector<ReadPacketResult>; + // Read up to |results->size()| packets from |fd|. The meaning of each element + // in |*results| has been documented on top of |ReadPacket|. + size_t ReadMultiplePackets(QuicUdpSocketFd fd, + BitMask64 packet_info_interested, + ReadPacketResults* results); + + // Write a packet to |fd|. + // packet_buffer, packet_buffer_len: The packet buffer to write. + // packet_info: The per packet information to set. + WriteResult WritePacket(QuicUdpSocketFd fd, + const char* packet_buffer, + size_t packet_buffer_len, + const QuicUdpPacketInfo& packet_info); + + protected: + bool SetupSocket(QuicUdpSocketFd fd, + int address_family, + int receive_buffer_size, + int send_buffer_size); + bool EnableReceiveSelfIpAddressForV4(QuicUdpSocketFd fd); + bool EnableReceiveSelfIpAddressForV6(QuicUdpSocketFd fd); +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QUIC_UDP_SOCKET_H_
diff --git a/quic/core/quic_udp_socket_posix.cc b/quic/core/quic_udp_socket_posix.cc new file mode 100644 index 0000000..f62dbe8 --- /dev/null +++ b/quic/core/quic_udp_socket_posix.cc
@@ -0,0 +1,608 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/third_party/quiche/src/quic/core/quic_udp_socket.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_udp_socket_platform_api.h" + +#include <arpa/inet.h> +#include <fcntl.h> +#include <netinet/in.h> +#include <sys/select.h> +#include <sys/socket.h> +#include <sys/types.h> + +#if defined(__APPLE__) && !defined(__APPLE_USE_RFC_3542) +#error "__APPLE_USE_RFC_3542 needs to be defined." +#endif + +#if defined(__linux__) +#include <alloca.h> +// For SO_TIMESTAMPING. +#include <linux/net_tstamp.h> +#endif + +#if defined(__linux__) && !defined(__ANDROID__) +#define QUIC_UDP_SOCKET_SUPPORT_TTL 1 +#endif + +namespace quic { +namespace { +QuicUdpSocketFd CreateNonblockingSocket(int address_family) { +#if defined(__linux__) && defined(SOCK_NONBLOCK) + + // Create a nonblocking socket directly. + int fd = socket(address_family, SOCK_DGRAM | SOCK_NONBLOCK, IPPROTO_UDP); + if (fd < 0) { + QUIC_LOG_FIRST_N(ERROR, 100) + << "socket() failed with address_family=" << address_family << ": " + << strerror(errno); + return kQuicInvalidSocketFd; + } + + return fd; + +#else + + // Create a socket and use fcntl to set it to nonblocking. + // This implementation is used when building for iOS, OSX and old versions of + // Linux (< 2.6.27) and old versions of Android (< API 21). + int fd = socket(address_family, SOCK_DGRAM, IPPROTO_UDP); + if (fd < 0) { + QUIC_LOG_FIRST_N(ERROR, 100) + << "socket() failed with address_family=" << address_family << ": " + << strerror(errno); + return kQuicInvalidSocketFd; + } + int current_flags = fcntl(fd, F_GETFL, 0); + if (current_flags == -1) { + QUIC_LOG_FIRST_N(ERROR, 100) + << "failed to get current socket flags: " << strerror(errno); + close(fd); + return kQuicInvalidSocketFd; + } + + int rc = fcntl(fd, F_SETFL, current_flags | O_NONBLOCK); + if (rc == -1) { + QUIC_LOG_FIRST_N(ERROR, 100) + << "failed to set socket to non-blocking: " << strerror(errno); + close(fd); + return kQuicInvalidSocketFd; + } + + return fd; + +#endif +} // End CreateNonblockingSocket + +void SetV4SelfIpInControlMessage(const QuicIpAddress& self_address, + cmsghdr* cmsg) { + DCHECK(self_address.IsIPv4()); + in_pktinfo* pktinfo = reinterpret_cast<in_pktinfo*>(CMSG_DATA(cmsg)); + memset(pktinfo, 0, sizeof(in_pktinfo)); + pktinfo->ipi_ifindex = 0; + std::string address_string = self_address.ToPackedString(); + memcpy(&pktinfo->ipi_spec_dst, address_string.c_str(), + address_string.length()); +} + +void SetV6SelfIpInControlMessage(const QuicIpAddress& self_address, + cmsghdr* cmsg) { + DCHECK(self_address.IsIPv6()); + in6_pktinfo* pktinfo = reinterpret_cast<in6_pktinfo*>(CMSG_DATA(cmsg)); + memset(pktinfo, 0, sizeof(in6_pktinfo)); + std::string address_string = self_address.ToPackedString(); + memcpy(&pktinfo->ipi6_addr, address_string.c_str(), address_string.length()); +} + +void PopulatePacketInfoFromControlMessage(struct cmsghdr* cmsg, + QuicUdpPacketInfo* packet_info, + BitMask64 packet_info_interested) { +#if defined(__linux__) && defined(SO_RXQ_OVFL) + if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SO_RXQ_OVFL) { + if (packet_info_interested.IsSet(QuicUdpPacketInfoBit::DROPPED_PACKETS)) { + packet_info->SetDroppedPackets( + *(reinterpret_cast<uint32_t*> CMSG_DATA(cmsg))); + } + return; + } +#endif + +#if defined(__linux__) && (!defined(__ANDROID_API__) || __ANDROID_API__ >= 21) + if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SO_TIMESTAMPING) { + // This is the structure that SO_TIMESTAMPING fills into the cmsg header. + // It is well-defined, but does not have a definition in a public header. + // See https://www.kernel.org/doc/Documentation/networking/timestamping.txt + // for more information. + struct LinuxTimestamping { + // The converted system time of the timestamp. + struct timespec systime; + // Deprecated; serves only as padding. + struct timespec hwtimetrans; + // The raw hardware timestamp. + struct timespec hwtimeraw; + }; + + if (packet_info_interested.IsSet(QuicUdpPacketInfoBit::RECV_TIMESTAMP)) { + LinuxTimestamping* linux_ts = + reinterpret_cast<LinuxTimestamping*>(CMSG_DATA(cmsg)); + timespec* ts = &linux_ts->systime; + int64_t usec = (static_cast<int64_t>(ts->tv_sec) * 1000 * 1000) + + (static_cast<int64_t>(ts->tv_nsec) / 1000); + packet_info->SetReceiveTimestamp( + QuicWallTime::FromUNIXMicroseconds(usec)); + } + return; + } +#endif + + if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) { + if (packet_info_interested.IsSet(QuicUdpPacketInfoBit::V6_SELF_IP)) { + const in6_pktinfo* info = reinterpret_cast<in6_pktinfo*>(CMSG_DATA(cmsg)); + const char* addr_data = reinterpret_cast<const char*>(&info->ipi6_addr); + int addr_len = sizeof(in6_addr); + QuicIpAddress self_v6_ip; + if (self_v6_ip.FromPackedString(addr_data, addr_len)) { + packet_info->SetSelfV6Ip(self_v6_ip); + } else { + QUIC_BUG << "QuicIpAddress::FromPackedString failed"; + } + } + return; + } + + if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO) { + if (packet_info_interested.IsSet(QuicUdpPacketInfoBit::V4_SELF_IP)) { + const in_pktinfo* info = reinterpret_cast<in_pktinfo*>(CMSG_DATA(cmsg)); + const char* addr_data = reinterpret_cast<const char*>(&info->ipi_addr); + int addr_len = sizeof(in_addr); + QuicIpAddress self_v4_ip; + if (self_v4_ip.FromPackedString(addr_data, addr_len)) { + packet_info->SetSelfV4Ip(self_v4_ip); + } else { + QUIC_BUG << "QuicIpAddress::FromPackedString failed"; + } + } + return; + } + + if ((cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_TTL) || + (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_HOPLIMIT)) { + if (packet_info_interested.IsSet(QuicUdpPacketInfoBit::TTL)) { + packet_info->SetTtl(*(reinterpret_cast<int*>(CMSG_DATA(cmsg)))); + } + return; + } + + if (packet_info_interested.IsSet( + QuicUdpPacketInfoBit::GOOGLE_PACKET_HEADER)) { + BufferSpan google_packet_headers; + if (GetGooglePacketHeadersFromControlMessage( + cmsg, &google_packet_headers.buffer, + &google_packet_headers.buffer_len)) { + packet_info->SetGooglePacketHeaders(google_packet_headers); + } + } +} + +bool NextCmsg(msghdr* hdr, + char* control_buffer, + size_t control_buffer_len, + int cmsg_level, + int cmsg_type, + size_t data_size, + cmsghdr** cmsg /*in, out*/) { + // msg_controllen needs to be increased first, otherwise CMSG_NXTHDR will + // return nullptr. + hdr->msg_controllen += CMSG_SPACE(data_size); + if (hdr->msg_controllen > control_buffer_len) { + return false; + } + + if ((*cmsg) == nullptr) { + DCHECK_EQ(nullptr, hdr->msg_control); + memset(control_buffer, 0, control_buffer_len); + hdr->msg_control = control_buffer; + (*cmsg) = CMSG_FIRSTHDR(hdr); + } else { + DCHECK_NE(nullptr, hdr->msg_control); + (*cmsg) = CMSG_NXTHDR(hdr, (*cmsg)); + } + + if (nullptr == (*cmsg)) { + return false; + } + + (*cmsg)->cmsg_len = CMSG_LEN(data_size); + (*cmsg)->cmsg_level = cmsg_level; + (*cmsg)->cmsg_type = cmsg_type; + + return true; +} +} // namespace + +QuicUdpSocketFd QuicUdpSocketApi::Create(int address_family, + int receive_buffer_size, + int send_buffer_size) { + QuicUdpSocketFd fd = CreateNonblockingSocket(address_family); + + if (fd == kQuicInvalidSocketFd) { + return kQuicInvalidSocketFd; + } + + if (!SetupSocket(fd, address_family, receive_buffer_size, send_buffer_size)) { + Destroy(fd); + return kQuicInvalidSocketFd; + } + + return fd; +} + +bool QuicUdpSocketApi::SetupSocket(QuicUdpSocketFd fd, + int address_family, + int receive_buffer_size, + int send_buffer_size) { + // Receive buffer size. + if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &receive_buffer_size, + sizeof(receive_buffer_size)) != 0) { + QUIC_LOG_FIRST_N(ERROR, 100) << "Failed to set socket recv size"; + return false; + } + + // Send buffer size. + if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &send_buffer_size, + sizeof(send_buffer_size)) != 0) { + QUIC_LOG_FIRST_N(ERROR, 100) << "Failed to set socket send size"; + return false; + } + + if (!EnableReceiveSelfIpAddressForV4(fd)) { + QUIC_LOG_FIRST_N(ERROR, 100) << "Failed to enable receiving of self v4 ip"; + return false; + } + + if (address_family == AF_INET6 && !EnableReceiveSelfIpAddressForV6(fd)) { + QUIC_LOG_FIRST_N(ERROR, 100) << "Failed to enable receiving of self v6 ip"; + return false; + } + + return true; +} + +void QuicUdpSocketApi::Destroy(QuicUdpSocketFd fd) { + if (fd != kQuicInvalidSocketFd) { + close(fd); + } +} + +bool QuicUdpSocketApi::Bind(QuicUdpSocketFd fd, QuicSocketAddress address) { + sockaddr_storage addr = address.generic_address(); + int addr_len = + address.host().IsIPv4() ? sizeof(sockaddr_in) : sizeof(sockaddr_in6); + return 0 == bind(fd, reinterpret_cast<sockaddr*>(&addr), addr_len); +} + +bool QuicUdpSocketApi::EnableDroppedPacketCount(QuicUdpSocketFd fd) { +#if defined(__linux__) && defined(SO_RXQ_OVFL) + int get_overflow = 1; + return 0 == setsockopt(fd, SOL_SOCKET, SO_RXQ_OVFL, &get_overflow, + sizeof(get_overflow)); +#else + (void)fd; + return false; +#endif +} + +bool QuicUdpSocketApi::EnableReceiveSelfIpAddressForV4(QuicUdpSocketFd fd) { + int get_self_ip = 1; + return 0 == setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &get_self_ip, + sizeof(get_self_ip)); +} + +bool QuicUdpSocketApi::EnableReceiveSelfIpAddressForV6(QuicUdpSocketFd fd) { + int get_self_ip = 1; + return 0 == setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &get_self_ip, + sizeof(get_self_ip)); +} + +bool QuicUdpSocketApi::EnableReceiveTimestamp(QuicUdpSocketFd fd) { +#if defined(__linux__) && (!defined(__ANDROID_API__) || __ANDROID_API__ >= 21) + int timestamping = SOF_TIMESTAMPING_RX_SOFTWARE | SOF_TIMESTAMPING_SOFTWARE; + return 0 == setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING, ×tamping, + sizeof(timestamping)); +#else + (void)fd; + return false; +#endif +} + +bool QuicUdpSocketApi::EnableReceiveTtlForV4(QuicUdpSocketFd fd) { +#if defined(QUIC_UDP_SOCKET_SUPPORT_TTL) + int get_ttl = 1; + return 0 == setsockopt(fd, IPPROTO_IP, IP_RECVTTL, &get_ttl, sizeof(get_ttl)); +#else + (void)fd; + return false; +#endif +} + +bool QuicUdpSocketApi::EnableReceiveTtlForV6(QuicUdpSocketFd fd) { +#if defined(QUIC_UDP_SOCKET_SUPPORT_TTL) + int get_ttl = 1; + return 0 == setsockopt(fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &get_ttl, + sizeof(get_ttl)); +#else + (void)fd; + return false; +#endif +} + +bool QuicUdpSocketApi::WaitUntilReadable(QuicUdpSocketFd fd, + QuicTime::Delta timeout) { + fd_set read_fds; + FD_ZERO(&read_fds); + FD_SET(fd, &read_fds); + + timeval select_timeout; + select_timeout.tv_sec = timeout.ToSeconds(); + select_timeout.tv_usec = timeout.ToMicroseconds() % 1000000; + + return 1 == select(1 + fd, &read_fds, nullptr, nullptr, &select_timeout); +} + +void QuicUdpSocketApi::ReadPacket(QuicUdpSocketFd fd, + BitMask64 packet_info_interested, + ReadPacketResult* result) { + result->ok = false; + BufferSpan& packet_buffer = result->packet_buffer; + BufferSpan& control_buffer = result->control_buffer; + QuicUdpPacketInfo* packet_info = &result->packet_info; + + struct iovec iov = {packet_buffer.buffer, packet_buffer.buffer_len}; + struct sockaddr_storage raw_peer_address; + + if (control_buffer.buffer_len > 0) { + reinterpret_cast<struct cmsghdr*>(control_buffer.buffer)->cmsg_len = + control_buffer.buffer_len; + } + + msghdr hdr; + hdr.msg_name = &raw_peer_address; + hdr.msg_namelen = sizeof(raw_peer_address); + hdr.msg_iov = &iov; + hdr.msg_iovlen = 1; + hdr.msg_flags = 0; + hdr.msg_control = control_buffer.buffer; + hdr.msg_controllen = control_buffer.buffer_len; + +#if defined(__linux__) && !defined(__ANDROID__) + // If MSG_TRUNC is set on Linux, recvmsg will return the real packet size even + // if |packet_buffer| is too small to receive it. + int flags = MSG_TRUNC; +#else + int flags = 0; +#endif + + int bytes_read = recvmsg(fd, &hdr, flags); + if (bytes_read < 0) { + const int error_num = errno; + if (error_num != EAGAIN) { + QUIC_LOG_FIRST_N(ERROR, 100) + << "Error reading packet: " << strerror(error_num); + } + return; + } + + if (QUIC_PREDICT_FALSE(hdr.msg_flags & MSG_CTRUNC)) { + QUIC_BUG << "Control buffer too small. size:" << control_buffer.buffer_len; + return; + } + + if (QUIC_PREDICT_FALSE(hdr.msg_flags & MSG_TRUNC)) { + QUIC_LOG_FIRST_N(WARNING, 100) + << "Received truncated QUIC packet: buffer size:" + << packet_buffer.buffer_len << " packet size:" << bytes_read; + return; + } + + packet_buffer.buffer_len = bytes_read; + if (packet_info_interested.IsSet(QuicUdpPacketInfoBit::PEER_ADDRESS)) { + packet_info->SetPeerAddress(QuicSocketAddress(raw_peer_address)); + } + + if (hdr.msg_controllen > 0) { + for (struct cmsghdr* cmsg = CMSG_FIRSTHDR(&hdr); cmsg != nullptr; + cmsg = CMSG_NXTHDR(&hdr, cmsg)) { + BitMask64 prior_bitmask = packet_info->bitmask(); + PopulatePacketInfoFromControlMessage(cmsg, packet_info, + packet_info_interested); + if (packet_info->bitmask() == prior_bitmask) { + QUIC_DLOG(INFO) << "Ignored cmsg_level:" << cmsg->cmsg_level + << ", cmsg_type:" << cmsg->cmsg_type; + } + } + } + + result->ok = true; +} + +size_t QuicUdpSocketApi::ReadMultiplePackets(QuicUdpSocketFd fd, + BitMask64 packet_info_interested, + ReadPacketResults* results) { +#if defined(__linux__) && !defined(__ANDROID__) + // Use recvmmsg. + size_t hdrs_size = sizeof(mmsghdr) * results->size(); + mmsghdr* hdrs = static_cast<mmsghdr*>(alloca(hdrs_size)); + memset(hdrs, 0, hdrs_size); + + struct TempPerPacketData { + iovec iov; + sockaddr_storage raw_peer_address; + }; + TempPerPacketData* packet_data_array = static_cast<TempPerPacketData*>( + alloca(sizeof(TempPerPacketData) * results->size())); + + for (size_t i = 0; i < results->size(); ++i) { + (*results)[i].ok = false; + + msghdr* hdr = &hdrs[i].msg_hdr; + TempPerPacketData* packet_data = &packet_data_array[i]; + packet_data->iov.iov_base = (*results)[i].packet_buffer.buffer; + packet_data->iov.iov_len = (*results)[i].packet_buffer.buffer_len; + + hdr->msg_name = &packet_data->raw_peer_address; + hdr->msg_namelen = sizeof(sockaddr_storage); + hdr->msg_iov = &packet_data->iov; + hdr->msg_iovlen = 1; + hdr->msg_flags = 0; + hdr->msg_control = (*results)[i].control_buffer.buffer; + hdr->msg_controllen = (*results)[i].control_buffer.buffer_len; + } + // If MSG_TRUNC is set on Linux, recvmmsg will return the real packet size in + // |hdrs[i].msg_len| even if packet buffer is too small to receive it. + int packets_read = recvmmsg(fd, hdrs, results->size(), MSG_TRUNC, nullptr); + if (packets_read <= 0) { + const int error_num = errno; + if (error_num != EAGAIN) { + QUIC_LOG_FIRST_N(ERROR, 100) + << "Error reading packets: " << strerror(error_num); + } + return 0; + } + + size_t num_good_packets = 0; + for (int i = 0; i < packets_read; ++i) { + if (hdrs[i].msg_len == 0) { + continue; + } + + msghdr& hdr = hdrs[i].msg_hdr; + if (QUIC_PREDICT_FALSE(hdr.msg_flags & MSG_CTRUNC)) { + QUIC_BUG << "Control buffer too small. size:" + << (*results)[i].control_buffer.buffer_len + << ", need:" << hdr.msg_controllen; + continue; + } + + if (QUIC_PREDICT_FALSE(hdr.msg_flags & MSG_TRUNC)) { + QUIC_LOG_FIRST_N(WARNING, 100) + << "Received truncated QUIC packet: buffer size:" + << (*results)[i].packet_buffer.buffer_len + << " packet size:" << hdrs[i].msg_len; + continue; + } + + ++num_good_packets; + (*results)[i].ok = true; + (*results)[i].packet_buffer.buffer_len = hdrs[i].msg_len; + + QuicUdpPacketInfo* packet_info = &(*results)[i].packet_info; + if (packet_info_interested.IsSet(QuicUdpPacketInfoBit::PEER_ADDRESS)) { + packet_info->SetPeerAddress( + QuicSocketAddress(packet_data_array[i].raw_peer_address)); + } + + if (hdr.msg_controllen > 0) { + for (struct cmsghdr* cmsg = CMSG_FIRSTHDR(&hdr); cmsg != nullptr; + cmsg = CMSG_NXTHDR(&hdr, cmsg)) { + PopulatePacketInfoFromControlMessage(cmsg, packet_info, + packet_info_interested); + } + } + } + return num_good_packets; +#else + size_t num_packets = 0; + for (ReadPacketResult& result : *results) { + result.ok = false; + } + for (ReadPacketResult& result : *results) { + ReadPacket(fd, packet_info_interested, &result); + if (!result.ok) { + break; + } + ++num_packets; + } + return num_packets; +#endif +} + +WriteResult QuicUdpSocketApi::WritePacket( + QuicUdpSocketFd fd, + const char* packet_buffer, + size_t packet_buffer_len, + const QuicUdpPacketInfo& packet_info) { + if (!packet_info.HasValue(QuicUdpPacketInfoBit::PEER_ADDRESS)) { + return WriteResult(WRITE_STATUS_ERROR, EINVAL); + } + + char control_buffer[512]; + sockaddr_storage raw_peer_address = + packet_info.peer_address().generic_address(); + iovec iov = {const_cast<char*>(packet_buffer), packet_buffer_len}; + + msghdr hdr; + hdr.msg_name = &raw_peer_address; + hdr.msg_namelen = packet_info.peer_address().host().IsIPv4() + ? sizeof(sockaddr_in) + : sizeof(sockaddr_in6); + hdr.msg_iov = &iov; + hdr.msg_iovlen = 1; + hdr.msg_flags = 0; + hdr.msg_control = nullptr; + hdr.msg_controllen = 0; + + cmsghdr* cmsg = nullptr; + + // Set self IP. + if (packet_info.HasValue(QuicUdpPacketInfoBit::V4_SELF_IP) && + packet_info.self_v4_ip().IsInitialized()) { + if (!NextCmsg(&hdr, control_buffer, sizeof(control_buffer), IPPROTO_IP, + IP_PKTINFO, sizeof(in_pktinfo), &cmsg)) { + QUIC_LOG_FIRST_N(ERROR, 100) + << "Not enough buffer to set self v4 ip address."; + return WriteResult(WRITE_STATUS_ERROR, EINVAL); + } + SetV4SelfIpInControlMessage(packet_info.self_v4_ip(), cmsg); + } else if (packet_info.HasValue(QuicUdpPacketInfoBit::V6_SELF_IP) && + packet_info.self_v6_ip().IsInitialized()) { + if (!NextCmsg(&hdr, control_buffer, sizeof(control_buffer), IPPROTO_IPV6, + IPV6_PKTINFO, sizeof(in6_pktinfo), &cmsg)) { + QUIC_LOG_FIRST_N(ERROR, 100) + << "Not enough buffer to set self v6 ip address."; + return WriteResult(WRITE_STATUS_ERROR, EINVAL); + } + SetV6SelfIpInControlMessage(packet_info.self_v6_ip(), cmsg); + } + +#if defined(QUIC_UDP_SOCKET_SUPPORT_TTL) + // Set ttl. + if (packet_info.HasValue(QuicUdpPacketInfoBit::TTL)) { + int cmsg_level = + packet_info.peer_address().host().IsIPv4() ? IPPROTO_IP : IPPROTO_IPV6; + int cmsg_type = + packet_info.peer_address().host().IsIPv4() ? IP_TTL : IPV6_HOPLIMIT; + if (!NextCmsg(&hdr, control_buffer, sizeof(control_buffer), cmsg_level, + cmsg_type, sizeof(int), &cmsg)) { + QUIC_LOG_FIRST_N(ERROR, 100) << "Not enough buffer to set ttl."; + return WriteResult(WRITE_STATUS_ERROR, EINVAL); + } + *reinterpret_cast<int*>(CMSG_DATA(cmsg)) = packet_info.ttl(); + } +#endif + + int rc; + do { + rc = sendmsg(fd, &hdr, 0); + } while (rc < 0 && errno == EINTR); + if (rc >= 0) { + return WriteResult(WRITE_STATUS_OK, rc); + } + return WriteResult((errno == EAGAIN || errno == EWOULDBLOCK) + ? WRITE_STATUS_BLOCKED + : WRITE_STATUS_ERROR, + errno); +} + +} // namespace quic
diff --git a/quic/core/quic_udp_socket_test.cc b/quic/core/quic_udp_socket_test.cc new file mode 100644 index 0000000..697e5e9 --- /dev/null +++ b/quic/core/quic_udp_socket_test.cc
@@ -0,0 +1,254 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/third_party/quiche/src/quic/core/quic_udp_socket.h" + +#include "net/third_party/quiche/src/quic/core/quic_constants.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" + +namespace quic { +namespace test { + +class QuicUdpSocketTest : public QuicTest { + protected: + void SetUp() override { + // Try creating AF_INET socket, if it fails because of unsupported address + // family then tests are being run under IPv6-only environment, initialize + // address family to use for running the test under as AF_INET6 otherwise + // initialize it as AF_INET. + address_family_ = AF_INET; + fd_client_ = + api_.Create(address_family_, + /*receive_buffer_size =*/kDefaultSocketReceiveBuffer, + /*send_buffer_size =*/kDefaultSocketReceiveBuffer); + if (fd_client_ == kQuicInvalidSocketFd) { + address_family_ = AF_INET6; + fd_client_ = + api_.Create(address_family_, + /*receive_buffer_size =*/kDefaultSocketReceiveBuffer, + /*send_buffer_size =*/kDefaultSocketReceiveBuffer); + } + ASSERT_NE(fd_client_, kQuicInvalidSocketFd); + + fd_server_ = + api_.Create(address_family_, + /*receive_buffer_size =*/kDefaultSocketReceiveBuffer, + /*send_buffer_size =*/kDefaultSocketReceiveBuffer); + ASSERT_NE(fd_server_, kQuicInvalidSocketFd); + + ASSERT_TRUE( + api_.Bind(fd_server_, QuicSocketAddress(Loopback(), /*port=*/0))); + + ASSERT_EQ(0, server_address_.FromSocket(fd_server_)); + + QUIC_LOG(INFO) << "Testing under IP" + << std::string((address_family_ == AF_INET) ? "v4" : "v6"); + } + + ~QuicUdpSocketTest() { + api_.Destroy(fd_client_); + api_.Destroy(fd_server_); + } + + QuicIpAddress Loopback() const { + return (address_family_ == AF_INET) ? QuicIpAddress::Loopback4() + : QuicIpAddress::Loopback6(); + } + + // Client sends the first |packet_size| bytes in |client_packet_buffer_| to + // server. + WriteResult SendPacketFromClient(size_t packet_size) { + EXPECT_LE(packet_size, sizeof(client_packet_buffer_)); + QuicUdpPacketInfo packet_info; + packet_info.SetPeerAddress(server_address_); + return api_.WritePacket(fd_client_, client_packet_buffer_, packet_size, + packet_info); + } + + WriteResult SendPacketFromClientWithTtl(size_t packet_size, int ttl) { + EXPECT_LE(packet_size, sizeof(client_packet_buffer_)); + QuicUdpPacketInfo packet_info; + packet_info.SetPeerAddress(server_address_); + packet_info.SetTtl(ttl); + return api_.WritePacket(fd_client_, client_packet_buffer_, packet_size, + packet_info); + } + + // Server waits for an incoming packet and reads it into + // |server_packet_buffer_|. + QuicUdpSocketApi::ReadPacketResult ReadPacketFromServer( + BitMask64 packet_info_interested) { + EXPECT_TRUE( + api_.WaitUntilReadable(fd_server_, QuicTime::Delta::FromSeconds(5))); + memset(server_packet_buffer_, 0, sizeof(server_packet_buffer_)); + QuicUdpSocketApi::ReadPacketResult result; + result.packet_buffer = {server_packet_buffer_, + sizeof(server_packet_buffer_)}; + result.control_buffer = {server_control_buffer_, + sizeof(server_control_buffer_)}; + api_.ReadPacket(fd_server_, packet_info_interested, &result); + return result; + } + + int ComparePacketBuffers(size_t packet_size) { + return memcmp(client_packet_buffer_, server_packet_buffer_, packet_size); + } + + QuicUdpSocketApi api_; + QuicUdpSocketFd fd_client_; + QuicUdpSocketFd fd_server_; + QuicSocketAddress server_address_; + int address_family_; + char client_packet_buffer_[kEthernetMTU] = {0}; + char server_packet_buffer_[kDefaultMaxPacketSize] = {0}; + char server_control_buffer_[512] = {0}; +}; + +TEST_F(QuicUdpSocketTest, ReadPacketOnly) { + const size_t kPacketSize = 512; + memset(client_packet_buffer_, '-', kPacketSize); + ASSERT_EQ(WriteResult(WRITE_STATUS_OK, kPacketSize), + SendPacketFromClient(kPacketSize)); + + QuicUdpSocketApi::ReadPacketResult read_result = + ReadPacketFromServer(/*packet_info_interested=*/BitMask64()); + ASSERT_TRUE(read_result.ok); + ASSERT_EQ(kPacketSize, read_result.packet_buffer.buffer_len); + ASSERT_EQ(0, ComparePacketBuffers(kPacketSize)); +} + +TEST_F(QuicUdpSocketTest, ReadTruncated) { + const size_t kPacketSize = kDefaultMaxPacketSize + 1; + memset(client_packet_buffer_, '*', kPacketSize); + ASSERT_EQ(WriteResult(WRITE_STATUS_OK, kPacketSize), + SendPacketFromClient(kPacketSize)); + + QuicUdpSocketApi::ReadPacketResult read_result = + ReadPacketFromServer(/*packet_info_interested=*/BitMask64()); + ASSERT_FALSE(read_result.ok); +} + +TEST_F(QuicUdpSocketTest, ReadDroppedPackets) { + const size_t kPacketSize = kDefaultMaxPacketSize; + memset(client_packet_buffer_, '-', kPacketSize); + ASSERT_EQ(WriteResult(WRITE_STATUS_OK, kPacketSize), + SendPacketFromClient(kPacketSize)); + ASSERT_EQ(WriteResult(WRITE_STATUS_OK, kPacketSize), + SendPacketFromClient(kPacketSize)); + + // Read the first packet without enabling DROPPED_PACKETS. + QuicUdpSocketApi::ReadPacketResult read_result = + ReadPacketFromServer(BitMask64(QuicUdpPacketInfoBit::DROPPED_PACKETS)); + ASSERT_TRUE(read_result.ok); + ASSERT_EQ(kPacketSize, read_result.packet_buffer.buffer_len); + ASSERT_EQ(0, ComparePacketBuffers(kPacketSize)); + ASSERT_FALSE( + read_result.packet_info.HasValue(QuicUdpPacketInfoBit::DROPPED_PACKETS)); + + // Enable DROPPED_PACKETS and read the second packet. + if (!api_.EnableDroppedPacketCount(fd_server_)) { + QUIC_LOG(INFO) << "DROPPED_PACKETS is not supported"; + return; + } + read_result = + ReadPacketFromServer(BitMask64(QuicUdpPacketInfoBit::DROPPED_PACKETS)); + ASSERT_TRUE(read_result.ok); + ASSERT_EQ(kPacketSize, read_result.packet_buffer.buffer_len); + ASSERT_EQ(0, ComparePacketBuffers(kPacketSize)); + if (read_result.packet_info.HasValue(QuicUdpPacketInfoBit::DROPPED_PACKETS)) { + EXPECT_EQ(0u, read_result.packet_info.dropped_packets()); + } +} + +TEST_F(QuicUdpSocketTest, ReadSelfIp) { + const QuicUdpPacketInfoBit self_ip_bit = + (address_family_ == AF_INET) ? QuicUdpPacketInfoBit::V4_SELF_IP + : QuicUdpPacketInfoBit::V6_SELF_IP; + + const size_t kPacketSize = 512; + memset(client_packet_buffer_, '&', kPacketSize); + ASSERT_EQ(WriteResult(WRITE_STATUS_OK, kPacketSize), + SendPacketFromClient(kPacketSize)); + + QuicUdpSocketApi::ReadPacketResult read_result = + ReadPacketFromServer(BitMask64(self_ip_bit)); + ASSERT_TRUE(read_result.ok); + ASSERT_EQ(kPacketSize, read_result.packet_buffer.buffer_len); + ASSERT_EQ(0, ComparePacketBuffers(kPacketSize)); + ASSERT_TRUE(read_result.packet_info.HasValue(self_ip_bit)); + EXPECT_EQ(Loopback(), (address_family_ == AF_INET) + ? read_result.packet_info.self_v4_ip() + : read_result.packet_info.self_v6_ip()); +} + +TEST_F(QuicUdpSocketTest, ReadReceiveTimestamp) { + const size_t kPacketSize = kDefaultMaxPacketSize; + memset(client_packet_buffer_, '-', kPacketSize); + ASSERT_EQ(WriteResult(WRITE_STATUS_OK, kPacketSize), + SendPacketFromClient(kPacketSize)); + ASSERT_EQ(WriteResult(WRITE_STATUS_OK, kPacketSize), + SendPacketFromClient(kPacketSize)); + + // Read the first packet without enabling RECV_TIMESTAMP. + QuicUdpSocketApi::ReadPacketResult read_result = + ReadPacketFromServer(BitMask64(QuicUdpPacketInfoBit::RECV_TIMESTAMP)); + ASSERT_TRUE(read_result.ok); + ASSERT_EQ(kPacketSize, read_result.packet_buffer.buffer_len); + ASSERT_EQ(0, ComparePacketBuffers(kPacketSize)); + ASSERT_FALSE( + read_result.packet_info.HasValue(QuicUdpPacketInfoBit::RECV_TIMESTAMP)); + + // Enable RECV_TIMESTAMP and read the second packet. + if (!api_.EnableReceiveTimestamp(fd_server_)) { + QUIC_LOG(INFO) << "RECV_TIMESTAMP is not supported"; + return; + } + read_result = + ReadPacketFromServer(BitMask64(QuicUdpPacketInfoBit::RECV_TIMESTAMP)); + ASSERT_TRUE(read_result.ok); + ASSERT_EQ(kPacketSize, read_result.packet_buffer.buffer_len); + ASSERT_EQ(0, ComparePacketBuffers(kPacketSize)); + ASSERT_TRUE( + read_result.packet_info.HasValue(QuicUdpPacketInfoBit::RECV_TIMESTAMP)); + QuicWallTime recv_timestamp = read_result.packet_info.receive_timestamp(); + // 1577836800 is the unix seconds for 2020-01-01 + EXPECT_TRUE( + QuicWallTime::FromUNIXSeconds(1577836800).IsBefore(recv_timestamp)); +} + +TEST_F(QuicUdpSocketTest, Ttl) { + const size_t kPacketSize = 512; + memset(client_packet_buffer_, '$', kPacketSize); + ASSERT_EQ(WriteResult(WRITE_STATUS_OK, kPacketSize), + SendPacketFromClientWithTtl(kPacketSize, 13)); + ASSERT_EQ(WriteResult(WRITE_STATUS_OK, kPacketSize), + SendPacketFromClientWithTtl(kPacketSize, 13)); + + // Read the first packet without enabling ttl reporting. + QuicUdpSocketApi::ReadPacketResult read_result = + ReadPacketFromServer(BitMask64(QuicUdpPacketInfoBit::TTL)); + ASSERT_TRUE(read_result.ok); + ASSERT_EQ(kPacketSize, read_result.packet_buffer.buffer_len); + ASSERT_EQ(0, ComparePacketBuffers(kPacketSize)); + ASSERT_FALSE(read_result.packet_info.HasValue(QuicUdpPacketInfoBit::TTL)); + + // Enable ttl reporting and read the second packet. + if (!((address_family_ == AF_INET) + ? api_.EnableReceiveTtlForV4(fd_server_) + : api_.EnableReceiveTtlForV6(fd_server_))) { + QUIC_LOG(INFO) << "TTL is not supported for address family " + << address_family_; + return; + } + + read_result = ReadPacketFromServer(BitMask64(QuicUdpPacketInfoBit::TTL)); + ASSERT_TRUE(read_result.ok); + ASSERT_EQ(kPacketSize, read_result.packet_buffer.buffer_len); + ASSERT_EQ(0, ComparePacketBuffers(kPacketSize)); + ASSERT_TRUE(read_result.packet_info.HasValue(QuicUdpPacketInfoBit::TTL)); + EXPECT_EQ(13, read_result.packet_info.ttl()); +} + +} // namespace test +} // namespace quic
diff --git a/quic/core/quic_utils.h b/quic/core/quic_utils.h index ab8d6af..590d377 100644 --- a/quic/core/quic_utils.h +++ b/quic/core/quic_utils.h
@@ -7,7 +7,9 @@ #include <cstddef> #include <cstdint> +#include <sstream> #include <string> +#include <type_traits> #include "net/third_party/quiche/src/quic/core/crypto/quic_random.h" #include "net/third_party/quiche/src/quic/core/frames/quic_frame.h" @@ -222,6 +224,63 @@ Perspective perspective); }; +template <typename Mask> +class QUIC_EXPORT_PRIVATE BitMask { + public: + // explicit to prevent (incorrect) usage like "BitMask bitmask = 0;". + template <typename... Bits> + explicit BitMask(Bits... bits) { + mask_ = MakeMask(bits...); + } + + BitMask() = default; + BitMask(const BitMask& other) = default; + BitMask& operator=(const BitMask& other) = default; + + template <typename... Bits> + void Set(Bits... bits) { + mask_ |= MakeMask(bits...); + } + + template <typename Bit> + bool IsSet(Bit bit) const { + return (MakeMask(bit) & mask_) != 0; + } + + static constexpr size_t NumBits() { return 8 * sizeof(Mask); } + + friend bool operator==(const BitMask& lhs, const BitMask& rhs) { + return lhs.mask_ == rhs.mask_; + } + + std::string DebugString() const { + std::ostringstream oss; + oss << "0x" << std::hex << mask_; + return oss.str(); + } + + private: + template <typename Bit> + static std::enable_if_t<std::is_enum<Bit>::value, Mask> MakeMask(Bit bit) { + using IntType = typename std::underlying_type<Bit>::type; + return Mask(1) << static_cast<IntType>(bit); + } + + template <typename Bit> + static std::enable_if_t<!std::is_enum<Bit>::value, Mask> MakeMask(Bit bit) { + return Mask(1) << bit; + } + + template <typename Bit, typename... Bits> + static Mask MakeMask(Bit first_bit, Bits... other_bits) { + return MakeMask(first_bit) | MakeMask(other_bits...); + } + + Mask mask_ = 0; +}; + +using BitMask64 = BitMask<uint64_t>; + } // namespace quic #endif // QUICHE_QUIC_CORE_QUIC_UTILS_H_
diff --git a/quic/core/quic_utils_test.cc b/quic/core/quic_utils_test.cc index baaa3ae..69be371 100644 --- a/quic/core/quic_utils_test.cc +++ b/quic/core/quic_utils_test.cc
@@ -272,6 +272,71 @@ EXPECT_NE(token1a, token2); } +enum class TestEnumClassBit : uint8_t { + BIT_ZERO = 0, + BIT_ONE, + BIT_TWO, +}; + +enum TestEnumBit { + TEST_BIT_0 = 0, + TEST_BIT_1, + TEST_BIT_2, +}; + +TEST(QuicBitMaskTest, EnumClass) { + BitMask64 mask(TestEnumClassBit::BIT_ZERO, TestEnumClassBit::BIT_TWO); + EXPECT_TRUE(mask.IsSet(TestEnumClassBit::BIT_ZERO)); + EXPECT_FALSE(mask.IsSet(TestEnumClassBit::BIT_ONE)); + EXPECT_TRUE(mask.IsSet(TestEnumClassBit::BIT_TWO)); +} + +TEST(QuicBitMaskTest, Enum) { + BitMask64 mask(TEST_BIT_1, TEST_BIT_2); + EXPECT_FALSE(mask.IsSet(TEST_BIT_0)); + EXPECT_TRUE(mask.IsSet(TEST_BIT_1)); + EXPECT_TRUE(mask.IsSet(TEST_BIT_2)); +} + +TEST(QuicBitMaskTest, Integer) { + BitMask64 mask(1, 3); + mask.Set(3); + mask.Set(5, 7, 9); + EXPECT_FALSE(mask.IsSet(0)); + EXPECT_TRUE(mask.IsSet(1)); + EXPECT_FALSE(mask.IsSet(2)); + EXPECT_TRUE(mask.IsSet(3)); + EXPECT_FALSE(mask.IsSet(4)); + EXPECT_TRUE(mask.IsSet(5)); + EXPECT_FALSE(mask.IsSet(6)); + EXPECT_TRUE(mask.IsSet(7)); + EXPECT_FALSE(mask.IsSet(8)); + EXPECT_TRUE(mask.IsSet(9)); +} + +TEST(QuicBitMaskTest, NumBits) { + EXPECT_EQ(64u, BitMask64::NumBits()); + EXPECT_EQ(32u, BitMask<uint32_t>::NumBits()); +} + +TEST(QuicBitMaskTest, Constructor) { + BitMask64 empty_mask; + for (size_t bit = 0; bit < empty_mask.NumBits(); ++bit) { + EXPECT_FALSE(empty_mask.IsSet(bit)); + } + + BitMask64 mask(1, 3); + BitMask64 mask2 = mask; + BitMask64 mask3(mask2); + + for (size_t bit = 0; bit < mask.NumBits(); ++bit) { + EXPECT_EQ(mask.IsSet(bit), mask2.IsSet(bit)); + EXPECT_EQ(mask.IsSet(bit), mask3.IsSet(bit)); + } + + EXPECT_TRUE(std::is_trivially_copyable<BitMask64>::value); +} + } // namespace } // namespace test } // namespace quic
diff --git a/quic/platform/api/quic_udp_socket_platform_api.h b/quic/platform/api/quic_udp_socket_platform_api.h new file mode 100644 index 0000000..78d35f8 --- /dev/null +++ b/quic/platform/api/quic_udp_socket_platform_api.h
@@ -0,0 +1,22 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef QUICHE_QUIC_PLATFORM_API_QUIC_UDP_SOCKET_PLATFORM_API_H_ +#define QUICHE_QUIC_PLATFORM_API_QUIC_UDP_SOCKET_PLATFORM_API_H_ + +#include "net/quic/platform/impl/quic_udp_socket_platform_impl.h" + +namespace quic { + +inline bool GetGooglePacketHeadersFromControlMessage( + struct ::cmsghdr* cmsg, + char** packet_headers, + size_t* packet_headers_len) { + return GetGooglePacketHeadersFromControlMessageImpl(cmsg, packet_headers, + packet_headers_len); +} + +} // namespace quic + +#endif // QUICHE_QUIC_PLATFORM_API_QUIC_UDP_SOCKET_PLATFORM_API_H_
diff --git a/quic/tools/quic_client.cc b/quic/tools/quic_client.cc index 379cfac..50ba040 100644 --- a/quic/tools/quic_client.cc +++ b/quic/tools/quic_client.cc
@@ -26,13 +26,8 @@ #include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" #include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" #include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h" -#include "net/quic/platform/impl/quic_socket_utils.h" #include "net/third_party/quiche/src/quic/tools/quic_simple_client_session.h" -#ifndef SO_RXQ_OVFL -#define SO_RXQ_OVFL 40 -#endif - namespace quic { namespace tools {
diff --git a/quic/tools/quic_client_epoll_network_helper.cc b/quic/tools/quic_client_epoll_network_helper.cc index 2f6d47d..60d04c7 100644 --- a/quic/tools/quic_client_epoll_network_helper.cc +++ b/quic/tools/quic_client_epoll_network_helper.cc
@@ -19,14 +19,10 @@ #include "net/third_party/quiche/src/quic/core/quic_epoll_connection_helper.h" #include "net/third_party/quiche/src/quic/core/quic_packets.h" #include "net/third_party/quiche/src/quic/core/quic_server_id.h" +#include "net/third_party/quiche/src/quic/core/quic_udp_socket.h" #include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" #include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" #include "net/third_party/quiche/src/quic/platform/api/quic_system_event_loop.h" -#include "net/quic/platform/impl/quic_socket_utils.h" - -#ifndef SO_RXQ_OVFL -#define SO_RXQ_OVFL 40 -#endif namespace quic { @@ -200,9 +196,16 @@ int QuicClientEpollNetworkHelper::CreateUDPSocket( QuicSocketAddress server_address, bool* overflow_supported) { - return QuicSocketUtils::CreateUDPSocket( - server_address, - /*receive_buffer_size =*/kDefaultSocketReceiveBuffer, - /*send_buffer_size =*/kDefaultSocketReceiveBuffer, overflow_supported); + QuicUdpSocketApi api; + int fd = api.Create(server_address.host().AddressFamilyToInt(), + /*receive_buffer_size =*/kDefaultSocketReceiveBuffer, + /*send_buffer_size =*/kDefaultSocketReceiveBuffer); + if (fd < 0) { + return fd; + } + + *overflow_supported = api.EnableDroppedPacketCount(fd); + api.EnableReceiveTimestamp(fd); + return fd; } } // namespace quic
diff --git a/quic/tools/quic_server.cc b/quic/tools/quic_server.cc index 04cc950..ef31d86 100644 --- a/quic/tools/quic_server.cc +++ b/quic/tools/quic_server.cc
@@ -28,15 +28,10 @@ #include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" #include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" #include "net/quic/platform/impl/quic_epoll_clock.h" -#include "net/quic/platform/impl/quic_socket_utils.h" #include "net/third_party/quiche/src/quic/tools/quic_simple_crypto_server_stream_helper.h" #include "net/third_party/quiche/src/quic/tools/quic_simple_dispatcher.h" #include "net/third_party/quiche/src/quic/tools/quic_simple_server_backend.h" -#ifndef SO_RXQ_OVFL -#define SO_RXQ_OVFL 40 -#endif - namespace quic { namespace { @@ -118,15 +113,18 @@ QuicServer::~QuicServer() = default; bool QuicServer::CreateUDPSocketAndListen(const QuicSocketAddress& address) { - fd_ = QuicSocketUtils::CreateUDPSocket( - address, - /*receive_buffer_size =*/kDefaultSocketReceiveBuffer, - /*send_buffer_size =*/kDefaultSocketReceiveBuffer, &overflow_supported_); - if (fd_ < 0) { + QuicUdpSocketApi socket_api; + fd_ = socket_api.Create(address.host().AddressFamilyToInt(), + /*receive_buffer_size =*/kDefaultSocketReceiveBuffer, + /*send_buffer_size =*/kDefaultSocketReceiveBuffer); + if (fd_ == kQuicInvalidSocketFd) { QUIC_LOG(ERROR) << "CreateSocket() failed: " << strerror(errno); return false; } + overflow_supported_ = socket_api.EnableDroppedPacketCount(fd_); + socket_api.EnableReceiveTimestamp(fd_); + sockaddr_storage addr = address.generic_address(); int rc = bind(fd_, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)); if (rc < 0) {
diff --git a/quic/tools/quic_server.h b/quic/tools/quic_server.h index d2d9782..1fb17f6 100644 --- a/quic/tools/quic_server.h +++ b/quic/tools/quic_server.h
@@ -18,6 +18,7 @@ #include "net/third_party/quiche/src/quic/core/quic_epoll_connection_helper.h" #include "net/third_party/quiche/src/quic/core/quic_framer.h" #include "net/third_party/quiche/src/quic/core/quic_packet_writer.h" +#include "net/third_party/quiche/src/quic/core/quic_udp_socket.h" #include "net/third_party/quiche/src/quic/core/quic_version_manager.h" #include "net/third_party/quiche/src/quic/platform/api/quic_epoll.h" #include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h" @@ -128,7 +129,7 @@ int port_; // Listening connection. Also used for outbound client communication. - int fd_; + QuicUdpSocketFd fd_; // If overflow_supported_ is true this will be the number of packets dropped // during the lifetime of the server. This may overflow if enough packets