Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(80)

Side by Side Diff: net/quic/core/congestion_control/simulation/quic_endpoint.cc

Issue 2323963002: Implement an event-based simulator for QuicConnection (Closed)
Patch Set: Adding missing files. Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698