diff options
Diffstat (limited to 'chromium/net/third_party/quiche/src/quiche/quic/core/quic_linux_socket_utils_test.cc')
-rw-r--r-- | chromium/net/third_party/quiche/src/quiche/quic/core/quic_linux_socket_utils_test.cc | 324 |
1 files changed, 324 insertions, 0 deletions
diff --git a/chromium/net/third_party/quiche/src/quiche/quic/core/quic_linux_socket_utils_test.cc b/chromium/net/third_party/quiche/src/quiche/quic/core/quic_linux_socket_utils_test.cc new file mode 100644 index 00000000000..e9d1b147504 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quiche/quic/core/quic_linux_socket_utils_test.cc @@ -0,0 +1,324 @@ +// Copyright (c) 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 "quiche/quic/core/quic_linux_socket_utils.h" + +#include <netinet/in.h> +#include <stdint.h> + +#include <cstddef> +#include <sstream> +#include <string> +#include <vector> + +#include "quiche/quic/platform/api/quic_test.h" +#include "quiche/quic/test_tools/quic_mock_syscall_wrapper.h" +#include "quiche/common/quiche_circular_deque.h" + +using testing::_; +using testing::InSequence; +using testing::Invoke; + +namespace quic { +namespace test { +namespace { + +class QuicLinuxSocketUtilsTest : public QuicTest { + protected: + WriteResult TestWriteMultiplePackets( + int fd, + const quiche::QuicheCircularDeque<BufferedWrite>::const_iterator& first, + const quiche::QuicheCircularDeque<BufferedWrite>::const_iterator& last, + int* num_packets_sent) { + QuicMMsgHdr mhdr( + first, last, kCmsgSpaceForIp, + [](QuicMMsgHdr* mhdr, int i, const BufferedWrite& buffered_write) { + mhdr->SetIpInNextCmsg(i, buffered_write.self_address); + }); + + WriteResult res = + QuicLinuxSocketUtils::WriteMultiplePackets(fd, &mhdr, num_packets_sent); + return res; + } + + MockQuicSyscallWrapper mock_syscalls_; + ScopedGlobalSyscallWrapperOverride syscall_override_{&mock_syscalls_}; +}; + +void CheckIpAndTtlInCbuf(msghdr* hdr, const void* cbuf, + const QuicIpAddress& self_addr, int ttl) { + const bool is_ipv4 = self_addr.IsIPv4(); + const size_t ip_cmsg_space = is_ipv4 ? kCmsgSpaceForIpv4 : kCmsgSpaceForIpv6; + + EXPECT_EQ(cbuf, hdr->msg_control); + EXPECT_EQ(ip_cmsg_space + CMSG_SPACE(sizeof(uint16_t)), hdr->msg_controllen); + + cmsghdr* cmsg = CMSG_FIRSTHDR(hdr); + EXPECT_EQ(cmsg->cmsg_len, is_ipv4 ? CMSG_LEN(sizeof(in_pktinfo)) + : CMSG_LEN(sizeof(in6_pktinfo))); + EXPECT_EQ(cmsg->cmsg_level, is_ipv4 ? IPPROTO_IP : IPPROTO_IPV6); + EXPECT_EQ(cmsg->cmsg_type, is_ipv4 ? IP_PKTINFO : IPV6_PKTINFO); + + const std::string& self_addr_str = self_addr.ToPackedString(); + if (is_ipv4) { + in_pktinfo* pktinfo = reinterpret_cast<in_pktinfo*>(CMSG_DATA(cmsg)); + EXPECT_EQ(0, memcmp(&pktinfo->ipi_spec_dst, self_addr_str.c_str(), + self_addr_str.length())); + } else { + in6_pktinfo* pktinfo = reinterpret_cast<in6_pktinfo*>(CMSG_DATA(cmsg)); + EXPECT_EQ(0, memcmp(&pktinfo->ipi6_addr, self_addr_str.c_str(), + self_addr_str.length())); + } + + cmsg = CMSG_NXTHDR(hdr, cmsg); + EXPECT_EQ(cmsg->cmsg_len, CMSG_LEN(sizeof(int))); + EXPECT_EQ(cmsg->cmsg_level, is_ipv4 ? IPPROTO_IP : IPPROTO_IPV6); + EXPECT_EQ(cmsg->cmsg_type, is_ipv4 ? IP_TTL : IPV6_HOPLIMIT); + EXPECT_EQ(ttl, *reinterpret_cast<int*>(CMSG_DATA(cmsg))); + + EXPECT_EQ(nullptr, CMSG_NXTHDR(hdr, cmsg)); +} + +void CheckMsghdrWithoutCbuf(const msghdr* hdr, const void* buffer, + size_t buf_len, + const QuicSocketAddress& peer_addr) { + EXPECT_EQ( + peer_addr.host().IsIPv4() ? sizeof(sockaddr_in) : sizeof(sockaddr_in6), + hdr->msg_namelen); + sockaddr_storage peer_generic_addr = peer_addr.generic_address(); + EXPECT_EQ(0, memcmp(hdr->msg_name, &peer_generic_addr, hdr->msg_namelen)); + EXPECT_EQ(1u, hdr->msg_iovlen); + EXPECT_EQ(buffer, hdr->msg_iov->iov_base); + EXPECT_EQ(buf_len, hdr->msg_iov->iov_len); + EXPECT_EQ(0, hdr->msg_flags); + EXPECT_EQ(nullptr, hdr->msg_control); + EXPECT_EQ(0u, hdr->msg_controllen); +} + +void CheckIpAndGsoSizeInCbuf(msghdr* hdr, const void* cbuf, + const QuicIpAddress& self_addr, + uint16_t gso_size) { + const bool is_ipv4 = self_addr.IsIPv4(); + const size_t ip_cmsg_space = is_ipv4 ? kCmsgSpaceForIpv4 : kCmsgSpaceForIpv6; + + EXPECT_EQ(cbuf, hdr->msg_control); + EXPECT_EQ(ip_cmsg_space + CMSG_SPACE(sizeof(uint16_t)), hdr->msg_controllen); + + cmsghdr* cmsg = CMSG_FIRSTHDR(hdr); + EXPECT_EQ(cmsg->cmsg_len, is_ipv4 ? CMSG_LEN(sizeof(in_pktinfo)) + : CMSG_LEN(sizeof(in6_pktinfo))); + EXPECT_EQ(cmsg->cmsg_level, is_ipv4 ? IPPROTO_IP : IPPROTO_IPV6); + EXPECT_EQ(cmsg->cmsg_type, is_ipv4 ? IP_PKTINFO : IPV6_PKTINFO); + + const std::string& self_addr_str = self_addr.ToPackedString(); + if (is_ipv4) { + in_pktinfo* pktinfo = reinterpret_cast<in_pktinfo*>(CMSG_DATA(cmsg)); + EXPECT_EQ(0, memcmp(&pktinfo->ipi_spec_dst, self_addr_str.c_str(), + self_addr_str.length())); + } else { + in6_pktinfo* pktinfo = reinterpret_cast<in6_pktinfo*>(CMSG_DATA(cmsg)); + EXPECT_EQ(0, memcmp(&pktinfo->ipi6_addr, self_addr_str.c_str(), + self_addr_str.length())); + } + + cmsg = CMSG_NXTHDR(hdr, cmsg); + EXPECT_EQ(cmsg->cmsg_len, CMSG_LEN(sizeof(uint16_t))); + EXPECT_EQ(cmsg->cmsg_level, SOL_UDP); + EXPECT_EQ(cmsg->cmsg_type, UDP_SEGMENT); + EXPECT_EQ(gso_size, *reinterpret_cast<uint16_t*>(CMSG_DATA(cmsg))); + + EXPECT_EQ(nullptr, CMSG_NXTHDR(hdr, cmsg)); +} + +TEST_F(QuicLinuxSocketUtilsTest, QuicMsgHdr) { + QuicSocketAddress peer_addr(QuicIpAddress::Loopback4(), 1234); + char packet_buf[1024]; + + QuicMsgHdr quic_hdr(packet_buf, sizeof(packet_buf), peer_addr, nullptr, 0); + CheckMsghdrWithoutCbuf(quic_hdr.hdr(), packet_buf, sizeof(packet_buf), + peer_addr); + + for (bool is_ipv4 : {true, false}) { + QuicIpAddress self_addr = + is_ipv4 ? QuicIpAddress::Loopback4() : QuicIpAddress::Loopback6(); + char cbuf[kCmsgSpaceForIp + kCmsgSpaceForTTL]; + QuicMsgHdr quic_hdr(packet_buf, sizeof(packet_buf), peer_addr, cbuf, + sizeof(cbuf)); + msghdr* hdr = const_cast<msghdr*>(quic_hdr.hdr()); + + EXPECT_EQ(nullptr, hdr->msg_control); + EXPECT_EQ(0u, hdr->msg_controllen); + + quic_hdr.SetIpInNextCmsg(self_addr); + EXPECT_EQ(cbuf, hdr->msg_control); + const size_t ip_cmsg_space = + is_ipv4 ? kCmsgSpaceForIpv4 : kCmsgSpaceForIpv6; + EXPECT_EQ(ip_cmsg_space, hdr->msg_controllen); + + if (is_ipv4) { + *quic_hdr.GetNextCmsgData<int>(IPPROTO_IP, IP_TTL) = 32; + } else { + *quic_hdr.GetNextCmsgData<int>(IPPROTO_IPV6, IPV6_HOPLIMIT) = 32; + } + + CheckIpAndTtlInCbuf(hdr, cbuf, self_addr, 32); + } +} + +TEST_F(QuicLinuxSocketUtilsTest, QuicMMsgHdr) { + quiche::QuicheCircularDeque<BufferedWrite> buffered_writes; + char packet_buf1[1024]; + char packet_buf2[512]; + buffered_writes.emplace_back( + packet_buf1, sizeof(packet_buf1), QuicIpAddress::Loopback4(), + QuicSocketAddress(QuicIpAddress::Loopback4(), 4)); + buffered_writes.emplace_back( + packet_buf2, sizeof(packet_buf2), QuicIpAddress::Loopback6(), + QuicSocketAddress(QuicIpAddress::Loopback6(), 6)); + + QuicMMsgHdr quic_mhdr_without_cbuf(buffered_writes.begin(), + buffered_writes.end(), 0, nullptr); + for (size_t i = 0; i < buffered_writes.size(); ++i) { + const BufferedWrite& bw = buffered_writes[i]; + CheckMsghdrWithoutCbuf(&quic_mhdr_without_cbuf.mhdr()[i].msg_hdr, bw.buffer, + bw.buf_len, bw.peer_address); + } + + QuicMMsgHdr quic_mhdr_with_cbuf( + buffered_writes.begin(), buffered_writes.end(), + kCmsgSpaceForIp + kCmsgSpaceForSegmentSize, + [](QuicMMsgHdr* mhdr, int i, const BufferedWrite& buffered_write) { + mhdr->SetIpInNextCmsg(i, buffered_write.self_address); + *mhdr->GetNextCmsgData<uint16_t>(i, SOL_UDP, UDP_SEGMENT) = 1300; + }); + for (size_t i = 0; i < buffered_writes.size(); ++i) { + const BufferedWrite& bw = buffered_writes[i]; + msghdr* hdr = &quic_mhdr_with_cbuf.mhdr()[i].msg_hdr; + CheckIpAndGsoSizeInCbuf(hdr, hdr->msg_control, bw.self_address, 1300); + } +} + +TEST_F(QuicLinuxSocketUtilsTest, WriteMultiplePackets_NoPacketsToSend) { + int num_packets_sent; + quiche::QuicheCircularDeque<BufferedWrite> buffered_writes; + + EXPECT_CALL(mock_syscalls_, Sendmmsg(_, _, _, _)).Times(0); + + EXPECT_EQ(WriteResult(WRITE_STATUS_ERROR, EINVAL), + TestWriteMultiplePackets(1, buffered_writes.begin(), + buffered_writes.end(), &num_packets_sent)); +} + +TEST_F(QuicLinuxSocketUtilsTest, WriteMultiplePackets_WriteBlocked) { + int num_packets_sent; + quiche::QuicheCircularDeque<BufferedWrite> buffered_writes; + buffered_writes.emplace_back(nullptr, 0, QuicIpAddress(), + QuicSocketAddress(QuicIpAddress::Any4(), 0)); + + EXPECT_CALL(mock_syscalls_, Sendmmsg(_, _, _, _)) + .WillOnce(Invoke([](int /*fd*/, mmsghdr* /*msgvec*/, + unsigned int /*vlen*/, int /*flags*/) { + errno = EWOULDBLOCK; + return -1; + })); + + EXPECT_EQ(WriteResult(WRITE_STATUS_BLOCKED, EWOULDBLOCK), + TestWriteMultiplePackets(1, buffered_writes.begin(), + buffered_writes.end(), &num_packets_sent)); + EXPECT_EQ(0, num_packets_sent); +} + +TEST_F(QuicLinuxSocketUtilsTest, WriteMultiplePackets_WriteError) { + int num_packets_sent; + quiche::QuicheCircularDeque<BufferedWrite> buffered_writes; + buffered_writes.emplace_back(nullptr, 0, QuicIpAddress(), + QuicSocketAddress(QuicIpAddress::Any4(), 0)); + + EXPECT_CALL(mock_syscalls_, Sendmmsg(_, _, _, _)) + .WillOnce(Invoke([](int /*fd*/, mmsghdr* /*msgvec*/, + unsigned int /*vlen*/, int /*flags*/) { + errno = EPERM; + return -1; + })); + + EXPECT_EQ(WriteResult(WRITE_STATUS_ERROR, EPERM), + TestWriteMultiplePackets(1, buffered_writes.begin(), + buffered_writes.end(), &num_packets_sent)); + EXPECT_EQ(0, num_packets_sent); +} + +TEST_F(QuicLinuxSocketUtilsTest, WriteMultiplePackets_WriteSuccess) { + int num_packets_sent; + quiche::QuicheCircularDeque<BufferedWrite> buffered_writes; + const int kNumBufferedWrites = 10; + static_assert(kNumBufferedWrites < 256, "Must be less than 256"); + std::vector<std::string> buffer_holder; + for (int i = 0; i < kNumBufferedWrites; ++i) { + size_t buf_len = (i + 1) * 2; + std::ostringstream buffer_ostream; + while (buffer_ostream.str().length() < buf_len) { + buffer_ostream << i; + } + buffer_holder.push_back(buffer_ostream.str().substr(0, buf_len - 1) + '$'); + + buffered_writes.emplace_back(buffer_holder.back().data(), buf_len, + QuicIpAddress(), + QuicSocketAddress(QuicIpAddress::Any4(), 0)); + + // Leave the first self_address uninitialized. + if (i != 0) { + ASSERT_TRUE(buffered_writes.back().self_address.FromString("127.0.0.1")); + } + + std::ostringstream peer_ip_ostream; + QuicIpAddress peer_ip_address; + peer_ip_ostream << "127.0.1." << i + 1; + ASSERT_TRUE(peer_ip_address.FromString(peer_ip_ostream.str())); + buffered_writes.back().peer_address = + QuicSocketAddress(peer_ip_address, i + 1); + } + + InSequence s; + + for (int expected_num_packets_sent : {1, 2, 3, 10}) { + SCOPED_TRACE(testing::Message() + << "expected_num_packets_sent=" << expected_num_packets_sent); + EXPECT_CALL(mock_syscalls_, Sendmmsg(_, _, _, _)) + .WillOnce(Invoke([&](int /*fd*/, mmsghdr* msgvec, unsigned int vlen, + int /*flags*/) { + EXPECT_LE(static_cast<unsigned int>(expected_num_packets_sent), vlen); + for (unsigned int i = 0; i < vlen; ++i) { + const BufferedWrite& buffered_write = buffered_writes[i]; + const msghdr& hdr = msgvec[i].msg_hdr; + EXPECT_EQ(1u, hdr.msg_iovlen); + EXPECT_EQ(buffered_write.buffer, hdr.msg_iov->iov_base); + EXPECT_EQ(buffered_write.buf_len, hdr.msg_iov->iov_len); + sockaddr_storage expected_peer_address = + buffered_write.peer_address.generic_address(); + EXPECT_EQ(0, memcmp(&expected_peer_address, hdr.msg_name, + sizeof(sockaddr_storage))); + EXPECT_EQ(buffered_write.self_address.IsInitialized(), + hdr.msg_control != nullptr); + } + return expected_num_packets_sent; + })) + .RetiresOnSaturation(); + + int expected_bytes_written = 0; + for (auto it = buffered_writes.cbegin(); + it != buffered_writes.cbegin() + expected_num_packets_sent; ++it) { + expected_bytes_written += it->buf_len; + } + + EXPECT_EQ( + WriteResult(WRITE_STATUS_OK, expected_bytes_written), + TestWriteMultiplePackets(1, buffered_writes.cbegin(), + buffered_writes.cend(), &num_packets_sent)); + EXPECT_EQ(expected_num_packets_sent, num_packets_sent); + } +} + +} // namespace +} // namespace test +} // namespace quic |