| // 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. | |
| #if defined(__APPLE__) && !defined(__APPLE_USE_RFC_3542) | |
| // This must be defined before including any system headers. | |
| #define __APPLE_USE_RFC_3542 | |
| #endif // defined(__APPLE__) && !defined(__APPLE_USE_RFC_3542) | |
| #include "quiche/quic/core/quic_udp_socket.h" | |
| #include <string> | |
| #include "absl/base/optimization.h" | |
| #include "quiche/quic/core/io/socket.h" | |
| #include "quiche/quic/platform/api/quic_bug_tracker.h" | |
| #include "quiche/quic/platform/api/quic_flag_utils.h" | |
| // Common cmsg-related functions are defined below. | |
| // Windows and POSIX cmsg formats are actually fairly similar, except the | |
| // Windows ones have all of the macros prefixed with WSA_ and all the type names | |
| // are different. | |
| namespace quic { | |
| namespace { | |
| #if defined(_WIN32) | |
| using PlatformCmsghdr = ::WSACMSGHDR; | |
| #if !defined(CMSG_DATA) | |
| #define CMSG_DATA WSA_CMSG_DATA | |
| #endif // !defined(CMSG_DATA) | |
| #else | |
| using PlatformCmsghdr = ::cmsghdr; | |
| #endif // defined(_WIN32) | |
| void PopulatePacketInfoFromControlMessageBase( | |
| PlatformCmsghdr* cmsg, QuicUdpPacketInfo* packet_info, | |
| QuicUdpPacketInfoBitMask packet_info_interested) { | |
| 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(quic_bug_10751_1) << "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(quic_bug_10751_2) << "QuicIpAddress::FromPackedString failed"; | |
| } | |
| } | |
| return; | |
| } | |
| } | |
| } // namespace | |
| } // namespace quic | |
| #if defined(_WIN32) | |
| #include "quiche/quic/core/quic_udp_socket_win.inc" | |
| #else | |
| #include "quiche/quic/core/quic_udp_socket_posix.inc" | |
| #endif | |
| namespace quic { | |
| QuicUdpSocketFd QuicUdpSocketApi::Create(int address_family, | |
| int receive_buffer_size, | |
| int send_buffer_size, bool ipv6_only) { | |
| // QUICHE_DCHECK here so the program exits early(before reading packets) in | |
| // debug mode. This should have been a static_assert, however it can't be done | |
| // on ios/osx because CMSG_SPACE isn't a constant expression there. | |
| QUICHE_DCHECK_GE(kDefaultUdpPacketControlBufferSize, kMinCmsgSpaceForRead); | |
| absl::StatusOr<SocketFd> socket = socket_api::CreateSocket( | |
| quiche::FromPlatformAddressFamily(address_family), | |
| socket_api::SocketProtocol::kUdp, | |
| /*blocking=*/false); | |
| if (!socket.ok()) { | |
| QUIC_LOG_FIRST_N(ERROR, 100) | |
| << "UDP non-blocking socket creation for address_family=" | |
| << address_family << " failed: " << socket.status(); | |
| return kQuicInvalidSocketFd; | |
| } | |
| #if !defined(_WIN32) | |
| SetGoogleSocketOptions(*socket); | |
| #endif | |
| if (!SetupSocket(*socket, address_family, receive_buffer_size, | |
| send_buffer_size, ipv6_only)) { | |
| Destroy(*socket); | |
| return kQuicInvalidSocketFd; | |
| } | |
| return *socket; | |
| } | |
| void QuicUdpSocketApi::Destroy(QuicUdpSocketFd fd) { | |
| if (fd != kQuicInvalidSocketFd) { | |
| absl::Status result = socket_api::Close(fd); | |
| if (!result.ok()) { | |
| QUIC_LOG_FIRST_N(WARNING, 100) | |
| << "Failed to close UDP socket with error " << result; | |
| } | |
| } | |
| } | |
| 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::BindInterface(QuicUdpSocketFd fd, | |
| const std::string& interface_name) { | |
| #if defined(__linux__) && !defined(__ANDROID_API__) | |
| if (interface_name.empty() || interface_name.size() >= IFNAMSIZ) { | |
| QUIC_BUG(udp_bad_interface_name) | |
| << "interface_name must be nonempty and shorter than " << IFNAMSIZ; | |
| return false; | |
| } | |
| return 0 == setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, | |
| interface_name.c_str(), interface_name.length()); | |
| #else | |
| (void)fd; | |
| (void)interface_name; | |
| QUIC_BUG(interface_bind_not_implemented) | |
| << "Interface binding is not implemented on this platform"; | |
| return false; | |
| #endif | |
| } | |
| 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, | |
| reinterpret_cast<char*>(&get_self_ip), | |
| sizeof(get_self_ip)); | |
| } | |
| bool QuicUdpSocketApi::EnableReceiveSelfIpAddressForV6(QuicUdpSocketFd fd) { | |
| int get_self_ip = 1; | |
| return 0 == setsockopt(fd, IPPROTO_IPV6, kIpv6RecvPacketInfo, | |
| reinterpret_cast<char*>(&get_self_ip), | |
| sizeof(get_self_ip)); | |
| } | |
| bool QuicUdpSocketApi::EnableReceiveTimestamp(QuicUdpSocketFd fd) { | |
| #if defined(QUIC_UDP_SOCKET_SUPPORT_LINUX_TIMESTAMPING) | |
| 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 | |
| } | |
| } // namespace quic |