danzh | 5887951 | 2020-06-08 12:25:12 -0700 | [diff] [blame] | 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 "net/third_party/quiche/src/quic/core/quic_linux_socket_utils.h" |
| 6 | |
| 7 | #include <netinet/in.h> |
| 8 | #include <stdint.h> |
| 9 | #include <cstddef> |
| 10 | #include <sstream> |
| 11 | #include <vector> |
| 12 | |
| 13 | #include <string> |
| 14 | |
| 15 | #include "net/third_party/quiche/src/quic/core/quic_circular_deque.h" |
| 16 | #include "net/third_party/quiche/src/quic/platform/api/quic_test.h" |
| 17 | #include "net/third_party/quiche/src/quic/test_tools/quic_mock_syscall_wrapper.h" |
| 18 | |
| 19 | using testing::_; |
| 20 | using testing::InSequence; |
| 21 | using testing::Invoke; |
| 22 | |
| 23 | namespace quic { |
| 24 | namespace test { |
| 25 | namespace { |
| 26 | |
| 27 | class QuicLinuxSocketUtilsTest : public QuicTest { |
| 28 | protected: |
| 29 | WriteResult TestWriteMultiplePackets( |
| 30 | int fd, |
| 31 | const QuicCircularDeque<BufferedWrite>::const_iterator& first, |
| 32 | const QuicCircularDeque<BufferedWrite>::const_iterator& last, |
| 33 | int* num_packets_sent) { |
| 34 | QuicMMsgHdr mhdr( |
| 35 | first, last, kCmsgSpaceForIp, |
| 36 | [](QuicMMsgHdr* mhdr, int i, const BufferedWrite& buffered_write) { |
| 37 | mhdr->SetIpInNextCmsg(i, buffered_write.self_address); |
| 38 | }); |
| 39 | |
| 40 | WriteResult res = |
| 41 | QuicLinuxSocketUtils::WriteMultiplePackets(fd, &mhdr, num_packets_sent); |
| 42 | return res; |
| 43 | } |
| 44 | |
| 45 | MockQuicSyscallWrapper mock_syscalls_; |
| 46 | ScopedGlobalSyscallWrapperOverride syscall_override_{&mock_syscalls_}; |
| 47 | }; |
| 48 | |
| 49 | void CheckIpAndTtlInCbuf(msghdr* hdr, |
| 50 | const void* cbuf, |
| 51 | const QuicIpAddress& self_addr, |
| 52 | int ttl) { |
| 53 | const bool is_ipv4 = self_addr.IsIPv4(); |
| 54 | const size_t ip_cmsg_space = is_ipv4 ? kCmsgSpaceForIpv4 : kCmsgSpaceForIpv6; |
| 55 | |
| 56 | EXPECT_EQ(cbuf, hdr->msg_control); |
| 57 | EXPECT_EQ(ip_cmsg_space + CMSG_SPACE(sizeof(uint16_t)), hdr->msg_controllen); |
| 58 | |
| 59 | cmsghdr* cmsg = CMSG_FIRSTHDR(hdr); |
| 60 | EXPECT_EQ(cmsg->cmsg_len, is_ipv4 ? CMSG_LEN(sizeof(in_pktinfo)) |
| 61 | : CMSG_LEN(sizeof(in6_pktinfo))); |
| 62 | EXPECT_EQ(cmsg->cmsg_level, is_ipv4 ? IPPROTO_IP : IPPROTO_IPV6); |
| 63 | EXPECT_EQ(cmsg->cmsg_type, is_ipv4 ? IP_PKTINFO : IPV6_PKTINFO); |
| 64 | |
| 65 | const std::string& self_addr_str = self_addr.ToPackedString(); |
| 66 | if (is_ipv4) { |
| 67 | in_pktinfo* pktinfo = reinterpret_cast<in_pktinfo*>(CMSG_DATA(cmsg)); |
| 68 | EXPECT_EQ(0, memcmp(&pktinfo->ipi_spec_dst, self_addr_str.c_str(), |
| 69 | self_addr_str.length())); |
| 70 | } else { |
| 71 | in6_pktinfo* pktinfo = reinterpret_cast<in6_pktinfo*>(CMSG_DATA(cmsg)); |
| 72 | EXPECT_EQ(0, memcmp(&pktinfo->ipi6_addr, self_addr_str.c_str(), |
| 73 | self_addr_str.length())); |
| 74 | } |
| 75 | |
| 76 | cmsg = CMSG_NXTHDR(hdr, cmsg); |
| 77 | EXPECT_EQ(cmsg->cmsg_len, CMSG_LEN(sizeof(int))); |
| 78 | EXPECT_EQ(cmsg->cmsg_level, is_ipv4 ? IPPROTO_IP : IPPROTO_IPV6); |
| 79 | EXPECT_EQ(cmsg->cmsg_type, is_ipv4 ? IP_TTL : IPV6_HOPLIMIT); |
| 80 | EXPECT_EQ(ttl, *reinterpret_cast<int*>(CMSG_DATA(cmsg))); |
| 81 | |
| 82 | EXPECT_EQ(nullptr, CMSG_NXTHDR(hdr, cmsg)); |
| 83 | } |
| 84 | |
| 85 | void CheckMsghdrWithoutCbuf(const msghdr* hdr, |
| 86 | const void* buffer, |
| 87 | size_t buf_len, |
| 88 | const QuicSocketAddress& peer_addr) { |
| 89 | EXPECT_EQ( |
| 90 | peer_addr.host().IsIPv4() ? sizeof(sockaddr_in) : sizeof(sockaddr_in6), |
| 91 | hdr->msg_namelen); |
| 92 | sockaddr_storage peer_generic_addr = peer_addr.generic_address(); |
| 93 | EXPECT_EQ(0, memcmp(hdr->msg_name, &peer_generic_addr, hdr->msg_namelen)); |
| 94 | EXPECT_EQ(1u, hdr->msg_iovlen); |
| 95 | EXPECT_EQ(buffer, hdr->msg_iov->iov_base); |
| 96 | EXPECT_EQ(buf_len, hdr->msg_iov->iov_len); |
| 97 | EXPECT_EQ(0, hdr->msg_flags); |
| 98 | EXPECT_EQ(nullptr, hdr->msg_control); |
| 99 | EXPECT_EQ(0u, hdr->msg_controllen); |
| 100 | } |
| 101 | |
| 102 | void CheckIpAndGsoSizeInCbuf(msghdr* hdr, |
| 103 | const void* cbuf, |
| 104 | const QuicIpAddress& self_addr, |
| 105 | uint16_t gso_size) { |
| 106 | const bool is_ipv4 = self_addr.IsIPv4(); |
| 107 | const size_t ip_cmsg_space = is_ipv4 ? kCmsgSpaceForIpv4 : kCmsgSpaceForIpv6; |
| 108 | |
| 109 | EXPECT_EQ(cbuf, hdr->msg_control); |
| 110 | EXPECT_EQ(ip_cmsg_space + CMSG_SPACE(sizeof(uint16_t)), hdr->msg_controllen); |
| 111 | |
| 112 | cmsghdr* cmsg = CMSG_FIRSTHDR(hdr); |
| 113 | EXPECT_EQ(cmsg->cmsg_len, is_ipv4 ? CMSG_LEN(sizeof(in_pktinfo)) |
| 114 | : CMSG_LEN(sizeof(in6_pktinfo))); |
| 115 | EXPECT_EQ(cmsg->cmsg_level, is_ipv4 ? IPPROTO_IP : IPPROTO_IPV6); |
| 116 | EXPECT_EQ(cmsg->cmsg_type, is_ipv4 ? IP_PKTINFO : IPV6_PKTINFO); |
| 117 | |
| 118 | const std::string& self_addr_str = self_addr.ToPackedString(); |
| 119 | if (is_ipv4) { |
| 120 | in_pktinfo* pktinfo = reinterpret_cast<in_pktinfo*>(CMSG_DATA(cmsg)); |
| 121 | EXPECT_EQ(0, memcmp(&pktinfo->ipi_spec_dst, self_addr_str.c_str(), |
| 122 | self_addr_str.length())); |
| 123 | } else { |
| 124 | in6_pktinfo* pktinfo = reinterpret_cast<in6_pktinfo*>(CMSG_DATA(cmsg)); |
| 125 | EXPECT_EQ(0, memcmp(&pktinfo->ipi6_addr, self_addr_str.c_str(), |
| 126 | self_addr_str.length())); |
| 127 | } |
| 128 | |
| 129 | cmsg = CMSG_NXTHDR(hdr, cmsg); |
| 130 | EXPECT_EQ(cmsg->cmsg_len, CMSG_LEN(sizeof(uint16_t))); |
| 131 | EXPECT_EQ(cmsg->cmsg_level, SOL_UDP); |
| 132 | EXPECT_EQ(cmsg->cmsg_type, UDP_SEGMENT); |
| 133 | EXPECT_EQ(gso_size, *reinterpret_cast<uint16_t*>(CMSG_DATA(cmsg))); |
| 134 | |
| 135 | EXPECT_EQ(nullptr, CMSG_NXTHDR(hdr, cmsg)); |
| 136 | } |
| 137 | |
| 138 | TEST_F(QuicLinuxSocketUtilsTest, QuicMsgHdr) { |
| 139 | QuicSocketAddress peer_addr(QuicIpAddress::Loopback4(), 1234); |
| 140 | char packet_buf[1024]; |
| 141 | |
| 142 | QuicMsgHdr quic_hdr(packet_buf, sizeof(packet_buf), peer_addr, nullptr, 0); |
| 143 | CheckMsghdrWithoutCbuf(quic_hdr.hdr(), packet_buf, sizeof(packet_buf), |
| 144 | peer_addr); |
| 145 | |
| 146 | for (bool is_ipv4 : {true, false}) { |
| 147 | QuicIpAddress self_addr = |
| 148 | is_ipv4 ? QuicIpAddress::Loopback4() : QuicIpAddress::Loopback6(); |
| 149 | char cbuf[kCmsgSpaceForIp + kCmsgSpaceForTTL]; |
| 150 | QuicMsgHdr quic_hdr(packet_buf, sizeof(packet_buf), peer_addr, cbuf, |
| 151 | sizeof(cbuf)); |
| 152 | msghdr* hdr = const_cast<msghdr*>(quic_hdr.hdr()); |
| 153 | |
| 154 | EXPECT_EQ(nullptr, hdr->msg_control); |
| 155 | EXPECT_EQ(0u, hdr->msg_controllen); |
| 156 | |
| 157 | quic_hdr.SetIpInNextCmsg(self_addr); |
| 158 | EXPECT_EQ(cbuf, hdr->msg_control); |
| 159 | const size_t ip_cmsg_space = |
| 160 | is_ipv4 ? kCmsgSpaceForIpv4 : kCmsgSpaceForIpv6; |
| 161 | EXPECT_EQ(ip_cmsg_space, hdr->msg_controllen); |
| 162 | |
| 163 | if (is_ipv4) { |
| 164 | *quic_hdr.GetNextCmsgData<int>(IPPROTO_IP, IP_TTL) = 32; |
| 165 | } else { |
| 166 | *quic_hdr.GetNextCmsgData<int>(IPPROTO_IPV6, IPV6_HOPLIMIT) = 32; |
| 167 | } |
| 168 | |
| 169 | CheckIpAndTtlInCbuf(hdr, cbuf, self_addr, 32); |
| 170 | } |
| 171 | } |
| 172 | |
| 173 | TEST_F(QuicLinuxSocketUtilsTest, QuicMMsgHdr) { |
| 174 | QuicCircularDeque<BufferedWrite> buffered_writes; |
| 175 | char packet_buf1[1024]; |
| 176 | char packet_buf2[512]; |
| 177 | buffered_writes.emplace_back( |
| 178 | packet_buf1, sizeof(packet_buf1), QuicIpAddress::Loopback4(), |
| 179 | QuicSocketAddress(QuicIpAddress::Loopback4(), 4)); |
| 180 | buffered_writes.emplace_back( |
| 181 | packet_buf2, sizeof(packet_buf2), QuicIpAddress::Loopback6(), |
| 182 | QuicSocketAddress(QuicIpAddress::Loopback6(), 6)); |
| 183 | |
| 184 | QuicMMsgHdr quic_mhdr_without_cbuf(buffered_writes.begin(), |
| 185 | buffered_writes.end(), 0, nullptr); |
| 186 | for (size_t i = 0; i < buffered_writes.size(); ++i) { |
| 187 | const BufferedWrite& bw = buffered_writes[i]; |
| 188 | CheckMsghdrWithoutCbuf(&quic_mhdr_without_cbuf.mhdr()[i].msg_hdr, bw.buffer, |
| 189 | bw.buf_len, bw.peer_address); |
| 190 | } |
| 191 | |
| 192 | QuicMMsgHdr quic_mhdr_with_cbuf( |
| 193 | buffered_writes.begin(), buffered_writes.end(), |
| 194 | kCmsgSpaceForIp + kCmsgSpaceForSegmentSize, |
| 195 | [](QuicMMsgHdr* mhdr, int i, const BufferedWrite& buffered_write) { |
| 196 | mhdr->SetIpInNextCmsg(i, buffered_write.self_address); |
| 197 | *mhdr->GetNextCmsgData<uint16_t>(i, SOL_UDP, UDP_SEGMENT) = 1300; |
| 198 | }); |
| 199 | for (size_t i = 0; i < buffered_writes.size(); ++i) { |
| 200 | const BufferedWrite& bw = buffered_writes[i]; |
| 201 | msghdr* hdr = &quic_mhdr_with_cbuf.mhdr()[i].msg_hdr; |
| 202 | CheckIpAndGsoSizeInCbuf(hdr, hdr->msg_control, bw.self_address, 1300); |
| 203 | } |
| 204 | } |
| 205 | |
| 206 | TEST_F(QuicLinuxSocketUtilsTest, WriteMultiplePackets_NoPacketsToSend) { |
| 207 | int num_packets_sent; |
| 208 | QuicCircularDeque<BufferedWrite> buffered_writes; |
| 209 | |
| 210 | EXPECT_CALL(mock_syscalls_, Sendmmsg(_, _, _, _)).Times(0); |
| 211 | |
| 212 | EXPECT_EQ(WriteResult(WRITE_STATUS_ERROR, EINVAL), |
| 213 | TestWriteMultiplePackets(1, buffered_writes.begin(), |
| 214 | buffered_writes.end(), &num_packets_sent)); |
| 215 | } |
| 216 | |
| 217 | TEST_F(QuicLinuxSocketUtilsTest, WriteMultiplePackets_WriteBlocked) { |
| 218 | int num_packets_sent; |
| 219 | QuicCircularDeque<BufferedWrite> buffered_writes; |
| 220 | buffered_writes.emplace_back(nullptr, 0, QuicIpAddress(), |
| 221 | QuicSocketAddress(QuicIpAddress::Any4(), 0)); |
| 222 | |
| 223 | EXPECT_CALL(mock_syscalls_, Sendmmsg(_, _, _, _)) |
| 224 | .WillOnce(Invoke([](int /*fd*/, mmsghdr* /*msgvec*/, |
| 225 | unsigned int /*vlen*/, int /*flags*/) { |
| 226 | errno = EWOULDBLOCK; |
| 227 | return -1; |
| 228 | })); |
| 229 | |
| 230 | EXPECT_EQ(WriteResult(WRITE_STATUS_BLOCKED, EWOULDBLOCK), |
| 231 | TestWriteMultiplePackets(1, buffered_writes.begin(), |
| 232 | buffered_writes.end(), &num_packets_sent)); |
| 233 | EXPECT_EQ(0, num_packets_sent); |
| 234 | } |
| 235 | |
| 236 | TEST_F(QuicLinuxSocketUtilsTest, WriteMultiplePackets_WriteError) { |
| 237 | int num_packets_sent; |
| 238 | QuicCircularDeque<BufferedWrite> buffered_writes; |
| 239 | buffered_writes.emplace_back(nullptr, 0, QuicIpAddress(), |
| 240 | QuicSocketAddress(QuicIpAddress::Any4(), 0)); |
| 241 | |
| 242 | EXPECT_CALL(mock_syscalls_, Sendmmsg(_, _, _, _)) |
| 243 | .WillOnce(Invoke([](int /*fd*/, mmsghdr* /*msgvec*/, |
| 244 | unsigned int /*vlen*/, int /*flags*/) { |
| 245 | errno = EPERM; |
| 246 | return -1; |
| 247 | })); |
| 248 | |
| 249 | EXPECT_EQ(WriteResult(WRITE_STATUS_ERROR, EPERM), |
| 250 | TestWriteMultiplePackets(1, buffered_writes.begin(), |
| 251 | buffered_writes.end(), &num_packets_sent)); |
| 252 | EXPECT_EQ(0, num_packets_sent); |
| 253 | } |
| 254 | |
| 255 | TEST_F(QuicLinuxSocketUtilsTest, WriteMultiplePackets_WriteSuccess) { |
| 256 | int num_packets_sent; |
| 257 | QuicCircularDeque<BufferedWrite> buffered_writes; |
| 258 | const int kNumBufferedWrites = 10; |
| 259 | static_assert(kNumBufferedWrites < 256, "Must be less than 256"); |
| 260 | std::vector<std::string> buffer_holder; |
| 261 | for (int i = 0; i < kNumBufferedWrites; ++i) { |
| 262 | size_t buf_len = (i + 1) * 2; |
| 263 | std::ostringstream buffer_ostream; |
| 264 | while (buffer_ostream.str().length() < buf_len) { |
| 265 | buffer_ostream << i; |
| 266 | } |
| 267 | buffer_holder.push_back(buffer_ostream.str().substr(0, buf_len - 1) + '$'); |
| 268 | |
| 269 | buffered_writes.emplace_back(buffer_holder.back().data(), buf_len, |
| 270 | QuicIpAddress(), |
| 271 | QuicSocketAddress(QuicIpAddress::Any4(), 0)); |
| 272 | |
| 273 | // Leave the first self_address uninitialized. |
| 274 | if (i != 0) { |
| 275 | ASSERT_TRUE(buffered_writes.back().self_address.FromString("127.0.0.1")); |
| 276 | } |
| 277 | |
| 278 | std::ostringstream peer_ip_ostream; |
| 279 | QuicIpAddress peer_ip_address; |
| 280 | peer_ip_ostream << "127.0.1." << i + 1; |
| 281 | ASSERT_TRUE(peer_ip_address.FromString(peer_ip_ostream.str())); |
| 282 | buffered_writes.back().peer_address = |
| 283 | QuicSocketAddress(peer_ip_address, i + 1); |
| 284 | } |
| 285 | |
| 286 | InSequence s; |
| 287 | |
| 288 | for (int expected_num_packets_sent : {1, 2, 3, 10}) { |
| 289 | SCOPED_TRACE(testing::Message() |
| 290 | << "expected_num_packets_sent=" << expected_num_packets_sent); |
| 291 | EXPECT_CALL(mock_syscalls_, Sendmmsg(_, _, _, _)) |
| 292 | .WillOnce(Invoke( |
| 293 | [&](int /*fd*/, mmsghdr* msgvec, unsigned int vlen, int /*flags*/) { |
| 294 | EXPECT_LE(static_cast<unsigned int>(expected_num_packets_sent), |
| 295 | vlen); |
| 296 | for (unsigned int i = 0; i < vlen; ++i) { |
| 297 | const BufferedWrite& buffered_write = buffered_writes[i]; |
| 298 | const msghdr& hdr = msgvec[i].msg_hdr; |
| 299 | EXPECT_EQ(1u, hdr.msg_iovlen); |
| 300 | EXPECT_EQ(buffered_write.buffer, hdr.msg_iov->iov_base); |
| 301 | EXPECT_EQ(buffered_write.buf_len, hdr.msg_iov->iov_len); |
| 302 | sockaddr_storage expected_peer_address = |
| 303 | buffered_write.peer_address.generic_address(); |
| 304 | EXPECT_EQ(0, memcmp(&expected_peer_address, hdr.msg_name, |
| 305 | sizeof(sockaddr_storage))); |
| 306 | EXPECT_EQ(buffered_write.self_address.IsInitialized(), |
| 307 | hdr.msg_control != nullptr); |
| 308 | } |
| 309 | return expected_num_packets_sent; |
| 310 | })) |
| 311 | .RetiresOnSaturation(); |
| 312 | |
| 313 | int expected_bytes_written = 0; |
| 314 | for (auto it = buffered_writes.cbegin(); |
| 315 | it != buffered_writes.cbegin() + expected_num_packets_sent; ++it) { |
| 316 | expected_bytes_written += it->buf_len; |
| 317 | } |
| 318 | |
| 319 | EXPECT_EQ( |
| 320 | WriteResult(WRITE_STATUS_OK, expected_bytes_written), |
| 321 | TestWriteMultiplePackets(1, buffered_writes.cbegin(), |
| 322 | buffered_writes.cend(), &num_packets_sent)); |
| 323 | EXPECT_EQ(expected_num_packets_sent, num_packets_sent); |
| 324 | } |
| 325 | } |
| 326 | |
| 327 | } // namespace |
| 328 | } // namespace test |
| 329 | } // namespace quic |