OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2012 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 "base/memory/ptr_util.h" |
| 6 #include "base/sha1.h" |
| 7 #include "base/strings/stringprintf.h" |
| 8 #include "net/quic/core/congestion_control/simulation/quic_endpoint.h" |
| 9 #include "net/quic/core/congestion_control/simulation/simulator.h" |
| 10 #include "net/quic/core/crypto/crypto_handshake_message.h" |
| 11 #include "net/quic/core/crypto/crypto_protocol.h" |
| 12 |
| 13 using base::StringPrintf; |
| 14 |
| 15 namespace net { |
| 16 namespace simulation { |
| 17 |
| 18 const QuicStreamId kDataStream = 3; |
| 19 const QuicByteCount kWriteChunkSize = 128 * 1024; |
| 20 |
| 21 // Takes a SHA-1 hash of the name and converts it into five 32-bit integers. |
| 22 static std::vector<uint32_t> HashNameIntoFive32BitIntegers(std::string name) { |
| 23 const std::string hash = base::SHA1HashString(name); |
| 24 |
| 25 std::vector<uint32_t> output; |
| 26 uint32_t current_number = 0; |
| 27 for (size_t i = 0; i < hash.size(); i++) { |
| 28 current_number = (current_number << 8) + hash[i]; |
| 29 if (i % 4 == 3) { |
| 30 output.push_back(i); |
| 31 current_number = 0; |
| 32 } |
| 33 } |
| 34 |
| 35 return output; |
| 36 } |
| 37 |
| 38 IPEndPoint GetAddressFromName(std::string name) { |
| 39 const std::vector<uint32_t> hash = HashNameIntoFive32BitIntegers(name); |
| 40 |
| 41 // Generate a random port between 1025 and 65535. |
| 42 const uint16_t port = 1025 + hash[0] % (65535 - 1025 + 1); |
| 43 |
| 44 // Generate a random 10.x.x.x address, where x is between 1 and 254. |
| 45 std::vector<uint8_t> ip_address; |
| 46 ip_address.push_back(10); |
| 47 for (size_t i = 1; i <= 4; i++) { |
| 48 ip_address.push_back(1 + hash[i] % 254); |
| 49 } |
| 50 |
| 51 return IPEndPoint(IPAddress(ip_address), port); |
| 52 } |
| 53 |
| 54 QuicEndpoint::QuicEndpoint(Simulator* simulator, |
| 55 std::string name, |
| 56 std::string peer_name, |
| 57 Perspective perspective, |
| 58 QuicConnectionId connection_id) |
| 59 : Endpoint(simulator, name), |
| 60 peer_name_(peer_name), |
| 61 writer_(this), |
| 62 nic_tx_queue_(simulator, |
| 63 StringPrintf("%s (TX Queue)", name.c_str()), |
| 64 kMaxPacketSize * kTxQueueSize), |
| 65 connection_(connection_id, |
| 66 GetAddressFromName(peer_name), |
| 67 simulator, |
| 68 simulator->GetAlarmFactory(), |
| 69 &writer_, |
| 70 false, |
| 71 perspective, |
| 72 CurrentSupportedVersions()), |
| 73 bytes_to_transfer_(0), |
| 74 bytes_transferred_(0), |
| 75 wrong_data_received_(false), |
| 76 transmission_buffer_(new char[kWriteChunkSize]) { |
| 77 connection_.SetSelfAddress(GetAddressFromName(name)); |
| 78 connection_.set_visitor(this); |
| 79 connection_.SetEncrypter(ENCRYPTION_FORWARD_SECURE, new NullEncrypter()); |
| 80 connection_.SetDecrypter(ENCRYPTION_FORWARD_SECURE, new NullDecrypter()); |
| 81 connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); |
| 82 |
| 83 // Configure the connection as if it received a handshake. This is important |
| 84 // primarily because |
| 85 // - this enables pacing, and |
| 86 // - this sets the non-handshake timeouts. |
| 87 std::string error; |
| 88 CryptoHandshakeMessage peer_hello; |
| 89 peer_hello.SetValue(kICSL, |
| 90 static_cast<uint32_t>(kMaximumIdleTimeoutSecs - 1)); |
| 91 QuicConfig config; |
| 92 QuicErrorCode error_code = config.ProcessPeerHello( |
| 93 peer_hello, perspective == Perspective::IS_CLIENT ? SERVER : CLIENT, |
| 94 &error); |
| 95 DCHECK_EQ(error_code, QUIC_NO_ERROR) << "Configuration failed: " << error; |
| 96 connection_.SetFromConfig(config); |
| 97 } |
| 98 |
| 99 QuicEndpoint::~QuicEndpoint() {} |
| 100 |
| 101 void QuicEndpoint::AddBytesToTransfer(QuicByteCount bytes) { |
| 102 if (bytes_to_transfer_ > 0) { |
| 103 Schedule(clock_->Now()); |
| 104 } |
| 105 |
| 106 bytes_to_transfer_ += bytes; |
| 107 WriteStreamData(); |
| 108 } |
| 109 |
| 110 void QuicEndpoint::AcceptPacket(std::unique_ptr<Packet> packet) { |
| 111 if (packet->destination != name_) { |
| 112 return; |
| 113 } |
| 114 |
| 115 QuicReceivedPacket received_packet(packet->contents.data(), |
| 116 packet->contents.size(), clock_->Now()); |
| 117 connection_.ProcessUdpPacket(connection_.self_address(), |
| 118 connection_.peer_address(), received_packet); |
| 119 } |
| 120 |
| 121 UnconstrainedPortInterface* QuicEndpoint::GetRxPort() { |
| 122 return this; |
| 123 } |
| 124 |
| 125 void QuicEndpoint::SetTxPort(ConstrainedPortInterface* port) { |
| 126 // Any egress done by the endpoint is actually handled by a queue on an NIC. |
| 127 nic_tx_queue_.set_tx_port(port); |
| 128 } |
| 129 |
| 130 // Return the data that |kDataStream| is supposed to have at offset |offset|. |
| 131 inline static uint8_t DataAtOffset(QuicStreamOffset offset) { |
| 132 return offset % 256; |
| 133 } |
| 134 |
| 135 void QuicEndpoint::OnPacketDequeued() { |
| 136 if (writer_.IsWriteBlocked() && |
| 137 (nic_tx_queue_.capacity() - nic_tx_queue_.bytes_queued()) >= |
| 138 kMaxPacketSize) { |
| 139 writer_.SetWritable(); |
| 140 connection_.OnCanWrite(); |
| 141 } |
| 142 } |
| 143 |
| 144 void QuicEndpoint::OnStreamFrame(const QuicStreamFrame& frame) { |
| 145 // Verify that the data received always matches the output of DataAtOffset(). |
| 146 DCHECK(frame.stream_id == kDataStream); |
| 147 for (size_t i = 0; i < frame.data_length; i++) { |
| 148 const QuicStreamOffset absolute_offset = frame.offset + i; |
| 149 if (frame.data_buffer[i] != DataAtOffset(absolute_offset)) { |
| 150 wrong_data_received_ = true; |
| 151 } |
| 152 } |
| 153 } |
| 154 void QuicEndpoint::OnCanWrite() { |
| 155 WriteStreamData(); |
| 156 } |
| 157 bool QuicEndpoint::WillingAndAbleToWrite() const { |
| 158 return bytes_to_transfer_ != 0; |
| 159 } |
| 160 bool QuicEndpoint::HasPendingHandshake() const { |
| 161 return false; |
| 162 } |
| 163 bool QuicEndpoint::HasOpenDynamicStreams() const { |
| 164 return true; |
| 165 } |
| 166 |
| 167 QuicEndpoint::Writer::Writer(QuicEndpoint* endpoint) |
| 168 : QuicDefaultPacketWriter(0), endpoint_(endpoint) {} |
| 169 |
| 170 QuicEndpoint::Writer::~Writer() {} |
| 171 |
| 172 WriteResult QuicEndpoint::Writer::WritePacket(const char* buffer, |
| 173 size_t buf_len, |
| 174 const IPAddress& self_address, |
| 175 const IPEndPoint& peer_address, |
| 176 PerPacketOptions* options) { |
| 177 DCHECK(!IsWriteBlocked()); |
| 178 DCHECK(options == nullptr); |
| 179 DCHECK(buf_len <= kMaxPacketSize); |
| 180 |
| 181 DCHECK(self_address == GetAddressFromName(endpoint_->name()).address()); |
| 182 DCHECK(peer_address == GetAddressFromName(endpoint_->peer_name_)); |
| 183 |
| 184 // Instead of losing a packet, become write-blocked when the egress queue is |
| 185 // full. |
| 186 if (endpoint_->nic_tx_queue_.packets_queued() > kTxQueueSize) { |
| 187 set_write_blocked(true); |
| 188 return WriteResult(WRITE_STATUS_BLOCKED, 0); |
| 189 } |
| 190 |
| 191 auto packet = base::MakeUnique<Packet>(); |
| 192 packet->source = endpoint_->name(); |
| 193 packet->destination = endpoint_->peer_name_; |
| 194 packet->tx_timestamp = endpoint_->clock_->Now(); |
| 195 |
| 196 packet->contents = std::string(buffer, buf_len); |
| 197 packet->size = buf_len; |
| 198 |
| 199 endpoint_->nic_tx_queue_.AcceptPacket(std::move(packet)); |
| 200 |
| 201 return WriteResult(WRITE_STATUS_OK, buf_len); |
| 202 } |
| 203 |
| 204 void QuicEndpoint::WriteStreamData() { |
| 205 // Instantiate a bundler which would normally be here due to QuicSession. |
| 206 QuicConnection::ScopedPacketBundler packet_bundler(&connection_, |
| 207 QuicConnection::SEND_ACK); |
| 208 |
| 209 while (bytes_to_transfer_ > 0) { |
| 210 // Transfer data in chunks of size at most |kWriteChunkSize|. |
| 211 const size_t transmission_size = |
| 212 std::min(kWriteChunkSize, bytes_to_transfer_); |
| 213 for (size_t i = 0; i < transmission_size; i++) { |
| 214 const QuicStreamOffset offset = bytes_transferred_ + i; |
| 215 transmission_buffer_[i] = DataAtOffset(offset); |
| 216 } |
| 217 |
| 218 iovec iov; |
| 219 iov.iov_base = transmission_buffer_.get(); |
| 220 iov.iov_len = transmission_size; |
| 221 |
| 222 QuicIOVector io_vector(&iov, 1, transmission_size); |
| 223 QuicConsumedData consumed_data = connection_.SendStreamData( |
| 224 kDataStream, io_vector, bytes_transferred_, false, nullptr); |
| 225 |
| 226 DCHECK(consumed_data.bytes_consumed <= transmission_size); |
| 227 bytes_transferred_ += consumed_data.bytes_consumed; |
| 228 bytes_to_transfer_ -= consumed_data.bytes_consumed; |
| 229 if (consumed_data.bytes_consumed != transmission_size) { |
| 230 return; |
| 231 } |
| 232 } |
| 233 } |
| 234 |
| 235 QuicEndpointMultiplexer::QuicEndpointMultiplexer( |
| 236 std::string name, |
| 237 std::initializer_list<QuicEndpoint*> endpoints) |
| 238 : Endpoint((*endpoints.begin())->simulator(), name) { |
| 239 for (QuicEndpoint* endpoint : endpoints) { |
| 240 mapping_.insert(std::make_pair(endpoint->name(), endpoint)); |
| 241 } |
| 242 } |
| 243 |
| 244 QuicEndpointMultiplexer::~QuicEndpointMultiplexer() {} |
| 245 |
| 246 void QuicEndpointMultiplexer::AcceptPacket(std::unique_ptr<Packet> packet) { |
| 247 auto key_value_pair_it = mapping_.find(packet->destination); |
| 248 if (key_value_pair_it == mapping_.end()) { |
| 249 return; |
| 250 } |
| 251 |
| 252 key_value_pair_it->second->GetRxPort()->AcceptPacket(std::move(packet)); |
| 253 } |
| 254 UnconstrainedPortInterface* QuicEndpointMultiplexer::GetRxPort() { |
| 255 return this; |
| 256 } |
| 257 void QuicEndpointMultiplexer::SetTxPort(ConstrainedPortInterface* port) { |
| 258 for (auto& key_value_pair : mapping_) { |
| 259 key_value_pair.second->SetTxPort(port); |
| 260 } |
| 261 } |
| 262 |
| 263 } // namespace simulation |
| 264 } // namespace net |
OLD | NEW |