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