Flow 3.0.0
Flow project: Full implementation reference.
peer_socket.cpp
Go to the documentation of this file.
1/* Flow
2 * Copyright 2023 Akamai Technologies, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the
5 * "License"); you may not use this file except in
6 * compliance with the License. You may obtain a copy
7 * of the License at
8 *
9 * https://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in
12 * writing, software distributed under the License is
13 * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
14 * CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing
16 * permissions and limitations under the License. */
17
18/// @file
25#include "flow/async/util.hpp"
26#include <boost/algorithm/string.hpp>
27#include <boost/tuple/tuple.hpp>
28#include <boost/unordered/unordered_set.hpp>
29#include <utility>
30
31namespace flow::net_flow
32{
33
34// Implementations.
35
36// Peer_socket implementations.
37
39 util::Task_engine* task_engine,
40 const Peer_socket_options& opts) :
41 Log_context(logger_ptr, Flow_log_component::S_NET_FLOW),
42 m_opts(opts),
43 m_active_connect(false), // Meaningless; set explicitly.
44 m_state(State::S_CLOSED), // Incorrect; set explicitly.
45 m_open_sub_state(Open_sub_state::S_DISCONNECTING), // Incorrect; set explicitly.
46 m_node(nullptr), // Incorrect; set explicitly.
47 m_rcv_buf(logger_ptr, 0), // Receive buffer mode: block size irrelevant (see Socket_buffer doc header).
48 // Send buffer mode: pack data into block-sized chunks for dequeueing speed. See Socket_buffer doc header.
49 m_snd_buf(logger_ptr, max_block_size()),
50 m_serialized_metadata(logger_ptr),
51 m_local_port(S_PORT_ANY), // Incorrect; set explicitly.
52 m_int_state(Int_state::S_CLOSED), // Incorrect; set explicitly.
53 m_rcv_syn_rcvd_data_cumulative_size(0), // Meaningless unless queue has elements but might as well initialize.
54 m_rcv_reassembly_q_data_size(0),
55 m_rcv_pending_acks_size_at_recv_handler_start(0),
56 m_snd_pending_rcv_wnd(0), // Meaningless originally but might as well initialize.
57 m_rcv_last_sent_rcv_wnd(0),
58 m_rcv_in_rcv_wnd_recovery(false),
59 m_rcv_delayed_ack_timer(*task_engine),
60 m_snd_flying_bytes(0),
61 m_snd_last_order_num(0),
62 m_snd_rexmit_q_size(0),
63 m_snd_remote_rcv_wnd(0),
64 m_snd_smoothed_round_trip_time(0),
65 m_round_trip_time_variance(0),
66 m_snd_drop_timeout(0),
67 m_snd_pacing_data(task_engine),
68 m_security_token(0), // Incorrect; set explicitly.
69 m_init_rexmit_count(0)
70{
71 // Only print pointer value, because most members are garbage at this point.
72 FLOW_LOG_TRACE("Peer_socket [" << static_cast<void*>(this) << "] created.");
73
74 // Log initial option values. Arguable if this should be INFO or TRACE. @todo Reconsider?
75 FLOW_LOG_TRACE("\n\n" << options());
76}
77
79{
80 /* Note that m_snd_cong_ctl, m_snd_bandwidth_estimator (etc.) and others store no Ptr{this},
81 * so this dtor will indeed execute (no circular shared_ptr problem). */
82
83 FLOW_LOG_TRACE("Peer_socket [" << this << "] destroyed.");
84}
85
87{
88 Lock_guard lock{m_mutex}; // State is liable to change at any time.
89 if (open_sub_state && (m_state == State::S_OPEN))
90 {
91 *open_sub_state = m_open_sub_state;
92 }
93 return m_state;
94}
95
97{
98 Lock_guard lock{m_mutex}; // m_node can simultaneously change to 0 if state changes to S_CLOSED.
99 return m_node;
100}
101
103{
104 Lock_guard lock{m_mutex};
105 return m_disconnect_cause;
106}
107
108bool Peer_socket::sync_send(std::nullptr_t, Error_code* err_code)
109{
110 return sync_send(nullptr, Fine_duration::max(), err_code);
111}
112
114{
115 // Similar to sync_send_impl(), so keeping comments light. Reminder: Goal is to wait until *this is Writable.
116
118
119 Lock_guard lock{m_mutex};
120
121 const Function<size_t (size_t)> empty_snd_buf_feed_func;
122 assert(empty_snd_buf_feed_func.empty());
123
124 lock.release();
125
126 // Intentionally pass empty function obj to indicate "reactor pattern" mode.
127 node_sync_send(empty_snd_buf_feed_func, wait_until, err_code);
128 return !*err_code; // Socket is Writable if and only if !*err_code (i.e., no timeout or other error while waiting).
129}
130
131size_t Peer_socket::node_send(const Function<size_t (size_t max_data_size)>& snd_buf_feed_func,
132 Error_code* err_code)
133{
134 // Everything is locked. (See send() template.)
135
136 const Ptr sock = shared_from_this();
137 if (!Node::ensure_sock_open(sock, err_code)) // Ensure it's open, so that we can access m_node.
138 {
139 return 0;
140 }
141 // else m_node is valid.
142
143 return m_node->send(sock, snd_buf_feed_func, err_code);
144}
145
146size_t Peer_socket::node_sync_send(const Function<size_t (size_t max_data_size)>& snd_buf_feed_func_or_empty,
147 const Fine_time_pt& wait_until,
148 Error_code* err_code)
149{
150 using boost::adopt_lock;
151
152 // Everything is locked. (See sync_send() template.)
153 Lock_guard lock{m_mutex, adopt_lock}; // Adopt already-locked mutex.
154
155 const Ptr sock = shared_from_this();
156 if (!Node::ensure_sock_open(sock, err_code)) // Ensure it's open, so that we can access m_node.
157 {
158 return 0;
159 }
160 // else m_node is valid.
161
162 /* Because all Node::sync_*() implementations would follow the same pattern (create Event_set,
163 * add Readable/Writable/Acceptable event, wait, try non-blocking op, if that fails try again with
164 * wait_until ever closer, etc.), for major code reuse we use the sync_op() function template and plug in
165 * the various Peer_socket/send-specific pieces as arguments.
166 *
167 * Performance cost: The only part about this that's not as fast as copy/pasting sync_op() N times, once
168 * for each type of socket/op, is the need to lambda the proper send() call into a function object.
169 * This amounts to storing and copying the arguments and the function pointer, which should not be
170 * too bad and is worth the code reuse IMO. */
171
172 lock.release(); // Again, release lock (mutex is still locked!).
173
174 /* Operating on Peer_sockets, returning size_t; Event_set socket set type is Peer_sockets.
175 * Object is sock; non-blocking operation is m_node->send(...) -- or N/A in "reactor pattern" mode.
176 * size_t(0) is the "would-block" return value for this operation. S_PEER_SOCKET_WRITABLE
177 * is the type of event to watch for here. */
178 return m_node
179 ->sync_op<Peer_socket, size_t>
180 (sock,
181 snd_buf_feed_func_or_empty.empty()
182 ? Function<size_t ()>() // Reactor pattern mode.
183 : Function<size_t ()>([this, sock, snd_buf_feed_func_or_empty, err_code]() -> size_t
184 { return m_node->send(sock, snd_buf_feed_func_or_empty, err_code); }),
186 wait_until, err_code);
187} // Peer_socket::node_sync_send()
188
189bool Peer_socket::sync_receive(std::nullptr_t, Error_code* err_code)
190{
191 return sync_receive(nullptr, Fine_duration::max(), err_code);
192}
193
195{
196 // Similar to sync_receive_impl(), so keeping comments light. Reminder: Goal is to wait until *this is Readable.
197
199
200 Lock_guard lock{m_mutex};
201
202 const Function<size_t ()> empty_rcv_buf_consume_func;
203 assert(empty_rcv_buf_consume_func.empty());
204
205 lock.release();
206
207 // Intentionally pass empty function obj to indicate "reactor pattern" mode.
208 node_sync_receive(empty_rcv_buf_consume_func, wait_until, err_code);
209 return !*err_code; // Socket is Readable if and only if !*err_code (i.e., no timeout or other error while waiting).
210}
211
212size_t Peer_socket::node_receive(const Function<size_t ()>& rcv_buf_consume_func,
213 Error_code* err_code)
214{
215 // Everything is locked. (See receive() template.)
216
217 const Ptr sock = shared_from_this();
218 if (!Node::ensure_sock_open(sock, err_code)) // Ensure it's open, so that we can access m_node.
219 {
220 return 0;
221 }
222 // else m_node is valid.
223
224 return m_node->receive(sock, rcv_buf_consume_func, err_code);
225}
226
227size_t Peer_socket::node_sync_receive(const Function<size_t ()>& rcv_buf_consume_func_or_empty,
228 const Fine_time_pt& wait_until,
229 Error_code* err_code)
230{
231 using boost::adopt_lock;
232
233 // Everything is locked. (See sync_send() template.)
234 Lock_guard lock{m_mutex, adopt_lock}; // Adopt already-locked mutex.
235
236 const Ptr sock = shared_from_this();
237 if (!Node::ensure_sock_open(sock, err_code)) // Ensure it's open, so that we can access m_node.
238 {
239 return 0;
240 }
241 // else m_node is valid.
242
243 lock.release(); // Again, release lock (mutex is still locked!).
244
245 // See comment in Peer_socket::node_sync_send().
246
247 /* Operating on Peer_sockets, returning size_t; Event_set socket set type is Peer_sockets.
248 * Object is sock; non-blocking operation is m_node->receive(...) -- or N/A in "reactor pattern" mode.
249 * size_t(0) is the "would-block" return value for this operation. S_PEER_SOCKET_READABLE
250 * is the type of event to watch for here. */
251 return m_node
252 ->sync_op<Peer_socket, size_t>
253 (sock,
254 rcv_buf_consume_func_or_empty.empty()
255 ? Function<size_t ()>() // Reactor pattern mode.
256 : Function<size_t ()>([this, sock, rcv_buf_consume_func_or_empty, err_code]() -> size_t
257 { return m_node->receive(sock, rcv_buf_consume_func_or_empty, err_code); }),
259 wait_until, err_code);
260} // Peer_socket::node_sync_receive()
261
263{
265 ([this](Error_code* actual_err_code) { close_abruptly(actual_err_code); },
266 err_code, FLOW_UTIL_WHERE_AM_I_STR()))
267 {
268 return;
269 }
270 // else
271
272 // We are in user thread U != W.
273
274 Lock_guard lock{m_mutex}; // Lock m_node/m_state; also it's a pre-condition for Node::close_abruptly().
275
276 const Ptr sock = shared_from_this();
277 if (!Node::ensure_sock_open(sock, err_code)) // Ensure it's open, so that we can access m_node.
278 {
279 // *err_code will be set to original close reason (m_disconnect_cause) in this case, as advertised.
280 return;
281 }
282 // else m_node is valid.
283
284 // Forward to Node, as is the general pattern for Peer_socket method implementations.
285 lock.release(); // Let go of the mutex (mutex is still LOCKED).
286 m_node->close_abruptly(sock, err_code);
287 // No m_mutex.unlock(): Node::close_abruptly() MUST take care of it.
288} // Peer_socket::close_abruptly()
289
291{
293 // ^-- Call ourselves and return if err_code is null. If got to present line, err_code is not null.
294
295 // We are in thread U != W.
296
297 Lock_guard lock{m_mutex}; // Lock m_node at least.
298
299 const Ptr sock = shared_from_this();
300 if (!Node::ensure_sock_open(sock, err_code)) // Ensure it's open, so that we can access m_node.
301 {
302 return false;
303 }
304 // else m_node is valid.
305
306 // As is typical elsewhere, pass the rest of the logic to a Node method.
307 return m_node->sock_set_options(sock, opts, err_code);
308} // Peer_socket::set_options()
309
311{
312 return opt(m_opts);
313}
314
316{
317 // We are in user thread U != W.
318
319 /* There are two cases. If the socket is open (not S_CLOSED), then an m_node owns it and may
320 * change the stats we want to copy in its thread W at any time. In this case we must copy it in
321 * thread W (which we do using a future and post(io_context&), as in listen() and other places in
322 * Node). In the socket is closed (S_CLOSED), then no m_node owns it, so there is no thread W
323 * applicable to this socket anymore, and we can just copy the data in thread U != W. */
324
325 Lock_guard lock{m_mutex}; // Lock m_node; also it's a pre-condition for Node::sock_info().
326
327 const Const_ptr sock = shared_from_this();
328
329 // See which case it is.
330 Error_code dummy;
331 if (!Node::ensure_sock_open(sock, &dummy))
332 {
333 // Socket is closed. Done and done. Return the final stats cached at S_CLOSED time.
334 return m_info_on_close;
335 }
336 // else m_node is valid.
337
338 // Forward to Node, as is the general pattern for Peer_socket method implementations.
339 lock.release(); // Let go of the mutex (mutex is still LOCKED).
340 return m_node->sock_info(sock);
341 // No m_mutex.unlock(): Node::sock_info() MUST take care of it.
342} // Peer_socket::info()
343
345{
347}
348
349size_t Peer_socket::max_block_size_multiple(const size_t& opt_val_ref,
350 const unsigned int* inflate_pct_val_ptr) const
351{
352 // Similar to opt() but specialized for this purpose. Lock once to get both values.
354
356 const unsigned int inflate_pct = inflate_pct_val_ptr ? (*inflate_pct_val_ptr) : 0;
357
358 /* We want N's nearest multiple M of B such that M >= N. M = ceil(N/B) * B (no actual floating-point math involved).
359 *
360 * Oh, and N is opt_val_ref inflated by K%, or opt_val_ref * (100 + K)%. */
361 return util::ceil_div(opt_val_ref * (100 + inflate_pct) / 100, max_block_size)
363}
364
366{
367 return opt(m_opts.m_st_rexmit_on);
368}
369
371{
372 // Can't change; no locking needed. Safe info even if S_CLOSED.
373 return m_remote_endpoint;
374}
375
377{
378 // Can't change; no locking needed. Safe (if outdated) info even if S_CLOSED.
379 return m_local_port;
380}
381
382size_t Peer_socket::get_connect_metadata(const boost::asio::mutable_buffer& buffer,
383 Error_code* err_code) const
384{
385 using std::memcpy;
386
388 // ^-- Call ourselves and return if err_code is null. If got to present line, err_code is not null.
389
390 // We are in user thread U != W.
391
392 Lock_guard lock{m_mutex}; // Lock m_serialized_metadata (it can be changed in sock_free_memory()).
393
394 if (!ensure_open(err_code)) // Ensure it's open; other m_serialized_metadata has been cleared.
395 {
396 return 0;
397 }
398 // else m_serialized_metadata is valid.
399
400 err_code->clear();
401 const size_t size = std::min(m_serialized_metadata.size(), buffer.size());
402 if (size != 0)
403 {
404 memcpy(buffer.data(), m_serialized_metadata.const_data(), size);
405 }
406
407 return size;
408} // Peer_socket::get_connect_metadata()
409
411{
412 return Node::ensure_sock_open(shared_from_this(), err_code);
413}
414
415std::string Peer_socket::bytes_blocks_str(size_t bytes) const
416{
418 using std::flush;
419
420 const auto block = max_block_size();
421 String_ostream os;
422 os.os() << bytes << '~' << (bytes / block);
423 if ((bytes % block) != 0)
424 {
425 os.os() << '+';
426 }
427 os.os() << flush;
428 return os.str();
429}
430
432 boost::shared_ptr<Data_packet> packet,
433 const Sent_when& sent_when) :
434 m_size(packet->m_data.size()),
435 m_sent_when({ sent_when }),
436 m_acks_after_me(0),
437 m_packet(rexmit_on ? packet : boost::shared_ptr<Data_packet>()) // Store packet only if we may have to rexmit later.
438{
439 // Nothing.
440}
441
443 m_size(size),
444 m_data(logger_ptr)
445{
446 if (src_data)
447 {
448 // Retransmission is on: save *src_data for later reassembly.
449 assert(m_size == size); // As promised in docs....
450
451 m_data = std::move(*src_data); // O(1) operation -- *src_data is probably cleared.
452 }
453}
454
455// Node implementations (dealing with individual Peer_sockets).
456
457// Static initializations.
458
459// Per RFC 5681 (Reno Fast Recovery; used in other congestion control specifications as well to detect drops).
461const uint8_t Node::S_DEFAULT_CONN_METADATA = 0; // Keep in sync with doc get_connect_metadata() doc header.
462
463// Implementations.
464
466 Peer_socket::Ptr sock,
467 boost::shared_ptr<const Syn_ack_packet> syn_ack)
468{
469 // We are in thread W.
470
471 /* We'd sent SYN and just got SYN_ACK. Assuming their SYN is valid, our side of connection can
472 * move to ESTABLISHED state. We can also complete the other side's connection by sending
473 * SYN_ACK_ACK. */
474
475 FLOW_LOG_INFO("NetFlow worker thread continuing active-connect of [" << sock << "]. "
476 "Received [" << syn_ack->m_type_ostream_manip << "] with "
477 "ISN [" << syn_ack->m_init_seq_num << "]; "
478 "security token [" << syn_ack->m_packed.m_security_token << "].");
479
480 // Send SYN_ACK_ACK to finish the handshake.
481
482 async_low_lvl_syn_ack_ack_send(sock, syn_ack);
483 /* send will happen asynchronously, and the registered completion handler will execute in this
484 * thread when done (NO SOONER than this method finishes executing). */
485
486 // Handle the logical SYN part of their SYN_ACK.
487
488 // Save the start of the sequence number series based on their initial sequence number.
489 sock->m_rcv_init_seq_num = syn_ack->m_init_seq_num;
490 sock->m_rcv_next_seq_num = sock->m_rcv_init_seq_num + 1;
491
492 // Move ourselves to connected state.
493
494 // Public state.
496 // Internal state. SYN_SENT -> ESTABLISHED.
497 sock_set_int_state(sock, Peer_socket::Int_state::S_ESTABLISHED);
498
499 // Got the acknowledgment to SYN, so cancel retransmits and the timeout for that SYN.
500 cancel_timers(sock);
501
502 // Setup the Drop Timeout engine (m_snd_drop_timer).
503 setup_drop_timer(socket_id, sock);
504
505 // Record initial rcv_wnd; it should be the entire size of the other side's Receive buffer.
506 sock->m_snd_remote_rcv_wnd = syn_ack->m_packed.m_rcv_wnd;
507
508 /* Since sock is now connected and has an empty Send buffer, it is certainly now Writable.
509 * Therefore we should soon inform anyone waiting on any Event_sets for sock to become Writable.
510 *
511 * Caveat: Similar to that in Node::handle_syn_ack_ack_to_syn_rcvd() at similar point in the
512 * code. */
513
514 // Accumulate the event into the Node store (note: not any Event_set yet).
515 if (m_sock_events[Event_set::Event_type::S_PEER_SOCKET_WRITABLE].insert(sock).second)
516 {
517 // Possibly inform the user for any applicable Event_sets right now.
518 event_set_all_check_delta(true);
519 /* ^-- defer_delta_check == true: because the only way to get to this method is from
520 * async_low_lvl_recv(), which will perform event_set_all_check_delta(false) at the end of itself,
521 * before the boost.asio handler exits. See Node::m_sock_events doc header for details. */
522 }
523} // Node::handle_syn_ack_to_syn_sent()
524
526 boost::shared_ptr<const Syn_ack_packet> syn_ack)
527{
528 // We are in thread W.
529
530 /* We're ESTABLISHED but got a duplicate (valid) SYN_ACK again. For reasons explained in
531 * handle_incoming() at the call to the current method, we simply give them a SYN_ACK_ACK again
532 * and continue like nothing happened. */
533
534 FLOW_LOG_INFO("NetFlow worker thread working on [" << sock << "]. "
535 "In [" << Peer_socket::Int_state::S_ESTABLISHED << "] state "
536 "received duplicate [" << syn_ack->m_type_ostream_manip << "] with "
537 "ISN [" << syn_ack->m_init_seq_num << "]; "
538 "security token [" << syn_ack->m_packed.m_security_token << "]. "
539 "Could be from packet loss.");
540
541 // Everything has already been validated.
542
543 async_low_lvl_syn_ack_ack_send(sock, syn_ack);
544} // Node::handle_syn_ack_to_established()
545
547 Peer_socket::Ptr sock,
548 boost::shared_ptr<Data_packet> packet,
549 bool syn_rcvd_qd_packet)
550{
551 /* This is a complex method that does many things. Therefore readability is hard to accomplish, as the logic
552 * makes sense when writing it, but the big picture is hard to see when reading it. The necessary heavy commenting
553 * further increases the size and therefore (along that dimension) decreases readability. For these reasons,
554 * many logically distinct parts were placed into helper methods -- not to increase code reuse but to help
555 * the aforementioned consideration. */
556
557 // We are in thread W.
558
559 /* Connection is open, and we got data from other side. Note: For maintainability, this method features
560 * (and should continue to feature) mainly high-level flow control and method calls, as opposed to tons of lower-level
561 * detail (this should be factored out into methods being called).
562 *
563 * Summary of below (assuming no misbehavior by other side; also ignoring that every action is categorized
564 * in sock->m_rcv_stats for statistical purposes):
565 *
566 * - Determine `dupe` (is packet a duplicate of previously received packet?) by checking against
567 * sock->m_rcv_{next_seq_num|packets_with_gaps}. If so:
568 * - (Op AAA) Acknowledge packet (ACK to other side).
569 * - Return (do not close connection).
570 * - Determine `slide` (are packet's data the next expected [first -- by seq. # -- not-yet-received] data?)
571 * by checking against sock->m_rcv_{next_seq_num|packets_with_gaps}.
572 * - If retransmission is off:
573 * - (Op ###) Pass packet's data to Receive buffer sock->m_rcv_buf!
574 * - Except if that would overflow sock->m_rcv_buf, then return (do not close connection).
575 * - (Op %%%) Inform the event subsystem that Receive buffer is readable!
576 * - (Op AAA)
577 * - If (!slide):
578 * - Save packet info (except packet->m_data itself!) in sock->m_rcv_packets_with_gaps.
579 * - But if that overflows sock->m_rcv_packets_with_gaps, then also pretend
580 * gap before start of sock->m_rcv_packets_with_gaps has all been filled: set `slide = true;`.
581 * (This will cause below to pop sock->m_rcv_packets_with_gaps to not overflow.)
582 * - If `slide`:
583 * - (Op ***) Update sock->m_rcv_{next_seq_num|packets_with_gaps} (increment the former,
584 * possibly pop-front contiguous packets from the other).
585 * - Else, if retransmission is on:
586 * - If `slide`:
587 * - (Op ###)
588 * - (Op ***)
589 * - Plus, for each packet popped from sock->m_rcv_packets_with_gaps, in increasing seq. # order:
590 * Pass packet's data to Receive buffer sock->m_rcv_buf!
591 * - (Op %%%)
592 * - Else if (!slide):
593 * - Save packet info (including packet->m_data itself!) in sock->m_rcv_packets_with_gaps (reassembly queue).
594 * - But if that WOULD overflow sock->m_rcv_packets_with_gaps, then don't;
595 * and return (do not close connection).
596 * - (Op AAA) */
597
598 /* Set up some short-hand references to commonly used sock members. This should also help
599 * performance a little by skipping the shared_ptr dereference. (Should be safe since sock
600 * cannot get ref-counted down to zero in this method, unless there is an error, at which point
601 * we return anyway.) Just remember these are not simply local variables -- nor const references -- but refer
602 * to on-the-heap stuff! */
603 const bool rexmit_on = sock->rexmit_on();
604 const Sequence_number& seq_num = packet->m_seq_num;
605
606 auto& data = packet->m_data; // NOT const, since we may well be _moving_ this into Receive buffer, etc.
607 assert(!data.empty()); // This should have been verified immediately in handle_incoming().
608 // Save this before we possibly destroy `data`'s contents below when _moving_ into Receive buffer, etc.
609 const size_t data_size = data.size();
610
611 // Register one packet with N bytes of data (not necessarily acceptable data).
612 Peer_socket_receive_stats_accumulator& rcv_stats = sock->m_rcv_stats;
613 rcv_stats.total_data_packet(data_size);
614
615 // Before potential changes, log.
616
617 FLOW_LOG_TRACE("NetFlow worker thread working on [" << sock << "]. "
618 "Received [" << packet->m_type_ostream_manip << "] with "
619 "sequence number [" << seq_num << "]; data size [" << data_size << "].");
620 // Very verbose and CPU-intensive!
621 FLOW_LOG_DATA("Data [" << util::buffers_dump_string(data.const_buffer(), "", size_t(-1)) << "].");
622 // In below TRACE logging we will omit most of the above details, since they'll be already logged.
623
624 log_rcv_window(sock); // Especially log this state.
625
626 /* Compute `dupe` and `slide[_size]`, bits of info that are key to how the incoming packet fits into the rcv window.
627 * Also, regardless of anything else we need to register N bytes worth of data in DATA packets via
628 * one rcv_stats.<...>_data_packet(data_size); we can determine the <...> based on dupe, slide, or lack thereof. */
629
630 /* True will means it's a duplicate packet -- ACK but don't give to the user again.
631 * False will mean it's a new packet -- ACK and save to a buffer for eventual consumption (unless overflow). */
632 bool dupe;
633 // Will mean this packet is the first (by seq. #) unreceived packet we want. Only applies if !dupe.
634 bool slide;
635 /* ^-- @todo Eliminate this; use slide_size == 0 to mean !slide? Less state is a good thing.
636 * Also, slide_size can be assumed to be data_size, except in one case below -- *never* via
637 * sock_categorize_data_to_established(); both of these improvements will lead to cleaner code. */
638 size_t slide_size; // If (slide), this will be how much to increment m_rcv_next_seq_num.
639
640 const Error_code cat_result = sock_categorize_data_to_established(sock, packet, &dupe, &slide, &slide_size);
641 if (cat_result)
642 {
643 // Register one packet with N bytes of data (not acceptable due to error).
644 rcv_stats.error_data_packet(data_size);
645
646 /* Close connection in our structures (inform user if necessary as well). Pre-conditions
647 * assumed by call: sock in m_socks and sock->state() == S_OPEN (yes, since m_int_state ==
648 * S_ESTABLISHED); 3rd arg contains the reason for the close (yes). This will empty the Send
649 * and Receive buffers. That is OK, because this is the abrupt type of close (error). */
650 rst_and_close_connection_immediately(socket_id, sock, cat_result, true);
651 // ^-- defer_delta_check == true: for similar reason as in handle_syn_ack_ack_to_syn_rcvd().
652 return;
653 }
654 // else
655
656 // If we got here, no error so far; `dupe` and `slide` are both set properly.
657
658 if (dupe)
659 {
660 /* It's a duplicate received packet. We should still acknowledge every valid packet, even if
661 * duplicate, since at least it helps the other side measure congestion. Is it "lying," since
662 * we're throwing this dupe away? No, because we DID receive it earlier; and in fact that
663 * earlier packet's ACK packet may have itself gotten lost by the network. (Example: A sends P
664 * to B; A receives and responds with ACK of P; that's lost; A receives dupe of P and responds
665 * with ACK; B receives that ACK. Good.) Anyway if the other side doesn't like it, it can just
666 * ignore it.
667 *
668 * It is also important to ack a duplicate packet, if retransmission is enabled. For example,
669 * sender may send packet X, and we'll ack it; but the ACK may be lost. Then the sender will
670 * retransmit X thinking X was lost; if we don't ACK the retransmitted one, the sender will
671 * retransmit again, until it runs out of retransmissions and closes connection... all because
672 * of one lousy lost ACK. */
673
674 // Plenty of TRACE logging about duplicate packets above; and here is probably too verbose for an INFO; => no log.
675
676 // Register one packet with N bytes of data (not acceptable into Receive buffer but probably legal, just late).
677 rcv_stats.late_or_dupe_data_packet(data_size);
678
679 // Register one individual acknowledgment of N bytes of data (will go out but acks late DATA).
680 rcv_stats.late_or_dupe_to_send_ack_packet(data_size);
681
682 // ACK will happen asynchronously (not in this handler, and at best once UDP net-stack considers itself writable).
683 async_acknowledge_packet(sock, seq_num, packet->m_rexmit_id, data_size); // rcv_stats kept inside.
684 return;
685 }
686 // else if (!dupe), i.e. data to be saved in Receive buffer or reassembly queue (unless overflow).
687
688 // Register one packet with N bytes of data (legal and acceptable into Receive buffer).
689 rcv_stats.good_data_packet(data.size());
690
691 /* Behavior is different at this point depending on whether retransmission is enabled or
692 * disabled. Many of the building blocks are the same and have been factored out into helpers. */
693
694 if (!rexmit_on)
695 {
696 /* No retransmission, so things are fairly simple. Firstly any new received data go
697 * straight to Receive buffer (out of order or not). */
698
699 if (!sock_data_to_rcv_buf_unless_overflow(sock, packet))
700 {
701 /* Not so fast. There's no space in the Receive buffer, so there's no choice except to drop the
702 * packet despite all of the above. Note that this means the packet was not "received" (and
703 * we can't slide the window forward either).
704 *
705 * Should we RST/close? Absolutely not. The sender did nothing wrong (except maybe they suck
706 * at detecting congestion caused by our user not reading the Receive buffer fast enough and
707 * thus letting it fill up, or maybe they just suck at congestion control). Our user is not
708 * consuming the Receive buffer in time. We drop packet and let chips fall where they may
709 * (reliability measures will handle it).
710 *
711 * Should we still acknowledge it? No. Dropping a packet at this late stage is still
712 * dropping a packet and indicates congestion of the network, of sorts; if we ACK it, the
713 * other side will assume the packet is being delivered and won't slow down its packet
714 * onslaught. So nothing else to. */
715 return;
716 }
717
718 /* DO NOT use `data` from this point forward -- it was just emptied by sock_data_to_rcv_buf_unless_overflow()!
719 * data_size is fine. */
720
721 /* Since sock now has a non-empty Receive buffer, it is certainly now Readable. Handle implications
722 * on relevant waiting Event_sets. */
723 sock_rcv_buf_now_readable(sock, syn_rcvd_qd_packet);
724
725 // Successfully wrote to Receive buffer. Can certainly acknowledge it at this point.
726
727 // Register one individual acknowledgment of N bytes of data (will go out and acks new, acceptable DATA).
728 rcv_stats.good_to_send_ack_packet(data_size);
729
730 // ACK will happen asynchronously (not in this handler, and at best once UDP net-stack considers itself writable).
731 async_acknowledge_packet(sock, seq_num, 0, data_size); // rcv_stats kept inside.
732
733 if (!slide)
734 {
735 /* !slide means new packet didn't resolve the first unreceived gap; hence by definition
736 * sock->m_rcv_packets_with_gaps must be updated. Due to certain overflow mechanisms, this may also
737 * cause the removal of part of the first gap, ironically! So pass in &slide, etc.
738 *
739 * Pass in data_size, since data.size() would run on an emptied `data` as noted above and be useless. */
740 sock_track_new_data_after_gap_rexmit_off(sock, packet, data_size, &slide, &slide_size);
741
742 // `slide` may now be true or not.
743 }
744
745 // `slide` may now be true or not.
746
747 /* Finally, update the window, since we've received a new packet. Maintain large invariant described in doc headers
748 * for Peer_socket::m_rcv_packets_with_gaps and related members. */
749
750 if (slide)
751 {
752 sock_slide_rcv_next_seq_num(sock, slide_size, false);
753 }
754 } // if (!rexmit_on)
755 else // if (rexmit_on)
756 {
757 /* Retransmission is on, so we have to deal with the reassembly queue. Namely if this packet
758 * fills the gap between stuff already given to Receive buffer and the first packet in the
759 * reassembly queue, then we should feed-to-user not just the new packet but also all contiguous packets
760 * at the front of the queue into Receive buffer. If it does not fill it, then we have to add
761 * it to reassembly queue in the proper spot. */
762
763 if (slide)
764 {
765 // New packet filled at least part of the first gap. So we should feed it to Receive buffer.
766
767 if (!sock_data_to_rcv_buf_unless_overflow(sock, packet))
768 {
769 /* Not so fast. If there's no space in the Receive buffer, there's no choice except to drop the
770 * packet despite all of the above. All comments from same spot in the no-retransmission
771 * code above apply (not repeating here). */
772 return;
773 }
774 // else
775
776 /* DO NOT use `data` from this point forward -- it was just emptied by sock_data_to_rcv_buf_unless_overflow().
777 * data_size is fine. */
778
779 /* Now update the receive window structure. Maintain invariants described in doc headers
780 * for m_rcv_packets_with_gaps and related members. Additionally, since retransmission is
781 * on, if the new packet bridged gap to the first packet(s) in the reassembly queue, then
782 * add their data to Receive buffer also (the `true` argument triggers this). */
783
784 sock_slide_rcv_next_seq_num(sock, slide_size, true);
785
786 /* Since sock nsqow has a non-empty Receive buffer, it is certainly now Readable. Handle implications
787 * on relevant waiting Event_sets. */
788 sock_rcv_buf_now_readable(sock, syn_rcvd_qd_packet);
789 } // if (slide)
790 else if (!sock_data_to_reassembly_q_unless_overflow(sock, packet)) // && (!slide)
791 {
792 /* Out-of-order packet. Couldn't feed to Receive buffer, so fed to reassembly queue (in sock_data_to_reass...()).
793 * However, if we're here, then that indicated we overflowed reassembly queue and decided to drop the packet
794 * instead. Bail out; which essentially just means don't acknowledge it, as that would occur just below. */
795 return;
796 }
797
798 // Either fed to Receive buffer or reassembly queue. Can certainly acknowledge it at this point.
799
800 // Register one individual acknowledgment of N bytes of data (will go out and acks new, acceptable DATA).
801 rcv_stats.good_to_send_ack_packet(data_size);
802
803 // ACK will happen asynchronously (not in this handler, and at best once UDP net-stack considers itself writable).
804 async_acknowledge_packet(sock, seq_num, packet->m_rexmit_id, data_size); // More rcv_stats kept inside.
805 } // else if (rexmit_on)
806
807 // After changes, log.
808 log_rcv_window(sock);
809} // Node::handle_data_to_established()
810
812 boost::shared_ptr<const Data_packet> packet,
813 bool* dupe, bool* slide, size_t* slide_size)
814{
815 assert(dupe && slide && slide_size);
816
817 /* Note this is a helper to handle_data_to_established() to make it more manageable. See comments and
818 * flow in that caller first.
819 *
820 * Note: not dealing with rcv_stats, as it's less code (assuming 1 call to us anyway) to do it based on our result. */
821
822 // See comment in same spot in handle_data_to_established().
823 Peer_socket_receive_stats_accumulator& rcv_stats = sock->m_rcv_stats;
824 const Sequence_number& rcv_next_seq_num = sock->m_rcv_next_seq_num;
825 const Peer_socket::Recvd_pkt_map& rcv_packets_with_gaps = sock->m_rcv_packets_with_gaps;
826
827 const auto& data = packet->m_data;
828 const Sequence_number& seq_num = packet->m_seq_num;
829
830 // Get the sequence number just past the last datum in this packet.
831 Sequence_number seq_num_end = seq_num;
832 advance_seq_num(&seq_num_end, data.size());
833
834 // If false, all received packets are followed by all unreceived ones. Otherwise there's at least 1 gap.
835 bool first_gap_exists;
836 // If true, then this is the sequence number of the first datum right after that first gap.
837 Sequence_number seq_num_after_first_gap;
838 rcv_get_first_gap_info(sock, &first_gap_exists, &seq_num_after_first_gap);
839
840 // Validate the 1st sequence number in DATA against the ISN.
841
842 if (seq_num <= sock->m_rcv_init_seq_num)
843 {
844 /* Sequence number precedes or equals the original SYN's sequence number. Either the other side
845 * is an a-hole, or somehow a socket_id was reused from a recent connection, which we do try to
846 * avoid like the plague. Therefore, send them an RST and abort connection. If they send more
847 * data packets to this port (which is quite possible; many could already be on the way),
848 * they'll get more RSTs still. */
849
850 // Interesting/rare enough to log a WARNING.
851 FLOW_LOG_WARNING("NetFlow worker thread working on [" << sock << "]. "
852 "Received [" << packet->m_type_ostream_manip << "] with "
853 "sequence number [" << seq_num << "]; data size [" << data.size() << "]; "
854 "sequence number precedes "
855 "ISN [" << sock->m_rcv_init_seq_num << "].");
856
857 return error::Code::S_SEQ_NUM_IMPLIES_CONNECTION_COLLISION; // Bad behavior from other side is fatal.
858 }
859 // else if (seq_num >= sock->m_rcv_init_seq_num)
860
861 if (seq_num < rcv_next_seq_num)
862 {
863 /* The packet claims to begin BEFORE the first gap (i.e., unreceived packet). This may be a
864 * valid duplicate packet. First, though, ensure it's not a "straddling" packet, i.e., that its
865 * last datum's sequence number is not past rcv_next_seq_num. If it is, that would imply one
866 * sequence number's datum is in two packets that are not duplicates of each other which is illegal. */
867
868 if (seq_num_end > rcv_next_seq_num)
869 {
870 // Interesting/rare enough to log a WARNING.
871 FLOW_LOG_WARNING("NetFlow worker thread working on [" << sock << "]. "
872 "Received [" << packet->m_type_ostream_manip << "] with "
873 "sequence numbers [" << seq_num << ", " << seq_num_end << "); "
874 "data size [" << data.size() << "]; "
875 "straddle first unreceived "
876 "sequence number [" << rcv_next_seq_num << "].");
877
878 // Yep, it straddles the boundary. Other side is behaving badly. RST/close as above.
880 }
881 // else ([seq_num, end seq_num] is before the first unreceived packet sequence, a/k/a gap)
882
883 FLOW_LOG_TRACE("Duplicate packet before first unreceived sequence number [" << rcv_next_seq_num << "].");
884
885 *dupe = true;
886 *slide = false;
887 return {};
888 } // if (seq_num < rcv_next_seq_num)
889 // else if (seq_num >= rcv_next_seq_num)
890
891 /* Packet claims to be in what TCP would call the receive window (somewhere at or after the
892 * first gap). Pin down in what part of that space it is, in order of increasing seq. #s. */
893
894 // First see if it's right at the start of the first gap.
895
896 if (seq_num == rcv_next_seq_num)
897 {
898 /* Great. It's at the start of the first gap, so we should be able to advance the window
899 * (increment rcv_next_seq_num). First check that it doesn't straddle the next received packet
900 * after the gap, if any. (Again, if it does that means one sequence number is inside 2
901 * packets that aren't dupes of each other, which is illegal.) */
902 if (first_gap_exists && (seq_num_end > seq_num_after_first_gap))
903 {
904 // Interesting/rare enough to log a WARNING.
905 FLOW_LOG_WARNING("NetFlow worker thread working on [" << sock << "]. "
906 "Received [" << packet->m_type_ostream_manip << "] with "
907 "sequence numbers [" << seq_num << ", " << seq_num_end << "); "
908 "data size [" << data.size() << "]; "
909 "supposed gap-filling data "
910 "straddle the boundary of packet [" << seq_num_after_first_gap << ", ...).");
911
913 }
914 // else legal -- can slide window to the right and save to Receive buffer.
915
916 FLOW_LOG_TRACE("Packet filled first [" << data.size() << "] unreceived sequence numbers "
917 "starting with [" << rcv_next_seq_num << "].");
918
919 *dupe = false;
920 *slide = true;
921 *slide_size = size_t(seq_num_end - seq_num);
922 assert(*slide_size == data.size());
923 return {};
924 }
925
926 // else if:
927 assert(seq_num > rcv_next_seq_num);
928
929 *slide = false; // This much is certain, as we're not filling the first gap from the front.
930
931 /* Packet doesn't fill that first gap. It's somewhere after the start of the first gap. Now
932 * there are 3 possibilities:
933 *
934 * -1- It's illegal: it straddles the boundary of one of the packets in m_rcv_packets_with_gaps,
935 * meaning some sequence number is inside 2 non-identical packets. RST/close as above.
936 *
937 * -2- It is a duplicate (same starting sequence number and length) of one of the packets
938 * past the first gap (i.e., of the packets in rcv_packets_with_gaps). Thus dupe =
939 * true (we should ACK but not save to Receive buffer).
940 *
941 * -3- It fits into one of the gaps; i.e. its sequence number range is either entirely
942 * before that of rcv_packets_with_gaps; entirely after it; or entirely before the
943 * first sequence number of an element of rcv_packets_with_gaps AND entirely after the
944 * last sequence number of the preceding element of rcv_packets_with_gaps. Thus we
945 * should ACK and save to Receive buffer.
946 *
947 * Determine which one it is.
948 *
949 * @todo Below technique is fun and all, but I now suspect the following might be simpler:
950 * 1, is seq_num in rcv_packets_with_gaps already? If so but different length, error; if so but
951 * but same length, *dupe is true. Otherwise: 2, insert a thing representing `packet` into rcv_packets_with_gaps
952 * as if for real; call inserted thing P. 3, check for straddling against right edge of prior(P), if any;
953 * if so, error. 4, check for straddling against left edge of next(P), if any; if so, error.
954 * 5, *dupe is false. The problem? It requires insertion, when this is supposed to not modify `packet` but only
955 * categorize it. Can of course remove it at the end, but that's cheesy. Can also modify our contract
956 * accordingly, but that reduces separation of concerns in caller's algorithm. Also, possibly the resulting
957 * algorithm might be easier to grok but not much shorter, if at all, anyway. Finally, could leave the
958 * straddling detection to later parts of the algorithm (again, changing our contract to be weaker though).
959 * In any case, not a top concern; and in terms of performance I doubt it would differ much from below. */
960
961 /* Find where we are compared to the various received packets past the first gap.
962 * This gets the first packet whose first sequence number is >= seq_num. There are 3 possibilities:
963 * that is equal to seq_num, past seq_num, or there is no such packet.
964 *
965 * Note that the lookup is O(log n) amortized, and then the subsequent checking is O(1).
966 * This is one of the reasons to use a sorted map by seq. #. */
967 const Peer_socket::Recvd_pkt_const_iter next_packet = rcv_packets_with_gaps.lower_bound(seq_num);
968
969 if (next_packet == rcv_packets_with_gaps.end())
970 {
971 /* There is no packet after ours, and there is no packet equal to ours. Thus we'll just
972 * insert our packet at the end. Check, however, that there is no straddling (-1- above).
973 * What packet's boundary can we straddle? At least the last one (assuming there's a gap). Its
974 * last number may be >= seq_num. (Its first is guaranteed to be < seq_num based on the
975 * above check.) If we don't straddle that boundary, we can't straddle any other packet's boundary,
976 * since all other packets precede the last one, so just check the last one (if exists). */
977 if (first_gap_exists)
978 {
979 const Peer_socket::Recvd_pkt_const_iter last_packet = prior(rcv_packets_with_gaps.end());
980 Sequence_number seq_num_last_end;
981 get_seq_num_range(last_packet, nullptr, &seq_num_last_end);
982
983 if (seq_num_last_end > seq_num) // (Corner case check: == means it contiguously precedes `packet`; no straddle.)
984 {
985 // Yep, packet straddles boundary of last_packet.
986
987 // Interesting/rare enough to log a WARNING.
988 FLOW_LOG_WARNING("NetFlow worker thread working on [" << sock << "]. "
989 "Received [" << packet->m_type_ostream_manip << "] with "
990 "sequence numbers [" << seq_num << ", " << seq_num_end << "); "
991 "data size [" << data.size() << "]; "
992 "supposed middle gap-filling packet data "
993 "straddle the boundary of last packet [..., " << seq_num_last_end << ").");
994
995 // Register one packet with N bytes of data (not acceptable due to error).
996 rcv_stats.error_data_packet(data.size());
998 }
999 // else OK, we're a new packet that happens to be the newest (by sequence number).
1000
1001 FLOW_LOG_TRACE("New packet is newest packet after unreceived gap; "
1002 "sequence numbers [" << seq_num << ", " << seq_num_end << "); "
1003 "first unreceived packet [" << rcv_next_seq_num << "].");
1004 }
1005 else // if (!first_gap_exists)
1006 {
1007 // OK, we're a new packet that happens to be the packet that forms the first gap by being after that gap.
1008
1009 FLOW_LOG_TRACE("New packet forms gap; sequence numbers [" << seq_num << ", " << seq_num_end << "); "
1010 "first unreceived packet [" << rcv_next_seq_num << "].");
1011 }
1012
1013 *dupe = false;
1014 return {};
1015 } // if (next_packet does not exist)
1016 // else if (next_packet exists at the same or later sequence number as seq_num)
1017
1018 // Get the [range) of sequence numbers in the packet that starts at or after seq_num.
1019 Sequence_number seq_num_next_start, seq_num_next_end;
1020 get_seq_num_range(next_packet, &seq_num_next_start, &seq_num_next_end);
1021
1022 if (seq_num_next_start == seq_num)
1023 {
1024 /* Our first datum has same sequence number as next_packet. Thus it's a duplicate.
1025 * Check, however, that their last sequence numbers are also identical. Otherwise, again,
1026 * one datum is in two different packets, which is illegal. */
1027 if (seq_num_next_end != seq_num_end)
1028 {
1029 // Yep, not a valid duplicate.
1030
1031 // Interesting/rare enough to log a WARNING.
1032 FLOW_LOG_WARNING("NetFlow worker thread working on [" << sock << "]. "
1033 "Received [" << packet->m_type_ostream_manip << "] with "
1034 "sequence numbers [" << seq_num << ", " << seq_num_end << "); "
1035 "data size [" << data.size() << "]; "
1036 "do not match supposed "
1037 "duplicate packet [" << seq_num << ", " << seq_num_next_end << ").");
1038
1040 }
1041 // else
1042
1043 /* @todo With rexmit_on we can also/instead compare `data` against actual data payload in next_packet -- not just
1044 * the sequence numbers. With !rexmit_on, there's no need to store the payloads, as they're always fed directly
1045 * to user upon receipt, even out of order. */
1046
1047 FLOW_LOG_TRACE("Duplicate packet after unreceived data; "
1048 "sequence numbers [" << seq_num << ", " << seq_num_end << ").");
1049
1050 *dupe = true;
1051 return {};
1052 } // if (seq_num_next_start == seq_num)
1053 // else if:
1054 assert(seq_num_next_start > seq_num); // lower_bound() is not horrifically broken.
1055
1056 // We've eliminated all dupe possibilities above. It's either error or not, at this point.
1057 *dupe = false;
1058
1059 /* Since next_packet starts after `packet`, the best outcome is that packet is entirely
1060 * before next_packet and entirely after prev_packet, where prev_packet == prior(next_packet) (if
1061 * such a thing exists). So we must check that we don't straddle
1062 * either next_packet's starting boundary or prev_packet's ending boundary. All other
1063 * preceding boundaries are straddled if and only if the prev_packet end is, and all
1064 * succeding boundaries iff next_packet start is. */
1065
1066 if (seq_num_end > seq_num_next_start) // Corner case check: == means `packet` contiguously precedes next_packet.
1067 {
1068 // Straddle one or more succeding packets. RST/close as above.
1069
1070 // Interesting/rare enough to log a WARNING.
1071 FLOW_LOG_WARNING("NetFlow worker thread working on [" << sock << "]. "
1072 "Received [" << packet->m_type_ostream_manip << "] with "
1073 "sequence numbers [" << seq_num << ", " << seq_num_end << "); "
1074 "data size [" << data.size() << "]; "
1075 "supposed middle gap-filling packet data "
1076 "straddle the left boundary of packet "
1077 "[" << seq_num_next_start << ", " << seq_num_next_end << ").");
1078
1079 return error::Code::S_SEQ_NUM_ARITHMETIC_FAILURE; // Bad behavior is fatal to connection, as above.
1080 }
1081 // else succeding packets OK. Check preceding packets.
1082
1083 if (next_packet == rcv_packets_with_gaps.begin())
1084 {
1085 FLOW_LOG_TRACE("New packet partially fills first gap without sliding window; "
1086 "sequence numbers [" << seq_num << ", " << seq_num_end << "); "
1087 "first unreceived packet [" << rcv_next_seq_num << "].");
1088 return {}; // There are none. We're good.
1089 }
1090
1091 const Peer_socket::Recvd_pkt_const_iter prev_packet = prior(next_packet);
1092 Sequence_number seq_num_prev_start, seq_num_prev_end;
1093 get_seq_num_range(prev_packet, &seq_num_prev_start, &seq_num_prev_end);
1094
1095 if (seq_num_prev_end > seq_num) // Corner case check: == means prev_packet contiguously precedes `packet`.
1096 {
1097 // Straddling one or more preceding packets. RST/close as above.
1098
1099 // Interesting/rare enough to log a WARNING.
1100 FLOW_LOG_WARNING("NetFlow worker thread working on [" << sock << "]. "
1101 "Received [" << packet->m_type_ostream_manip << "] with "
1102 "sequence numbers [" << seq_num << ", " << seq_num_end << "); "
1103 "data size [" << data.size() << "]; "
1104 "supposed middle gap-filling packet data "
1105 "straddle the right boundary of packet "
1106 "[" << seq_num_prev_start << ", " << seq_num_prev_end << ").");
1107
1108 return error::Code::S_SEQ_NUM_ARITHMETIC_FAILURE; // Bad behavior is fatal to connection, as above.
1109 }
1110 // else preceding packets OK.
1111
1112 FLOW_LOG_TRACE("New packet fills some middle gap; "
1113 "sequence numbers [" << seq_num << ", " << seq_num_end << "); "
1114 "first unreceived packet [" << rcv_next_seq_num << "].");
1115
1116 return {};
1117} // Node::sock_categorize_data_to_established()
1118
1120 boost::shared_ptr<Data_packet> packet)
1121{
1122 using util::Blob;
1123
1124 /* Note this is a helper to handle_data_to_established() to make it more manageable. See comments and
1125 * flow in that caller first. */
1126
1127 // See comment in same spot in handle_data_to_established().
1128 Peer_socket_receive_stats_accumulator& rcv_stats = sock->m_rcv_stats;
1129 Blob& data = packet->m_data; // NOT const due to Socket_buffer::feed*(). See below.
1130 // Save this before we possibly destroy data's contents below (for performance).
1131 const size_t data_size = data.size();
1132
1133 size_t buf_size;
1134 {
1135 // Receive Buffer can be consumed by user threads (not W) at the same time. Must lock.
1136 Peer_socket::Lock_guard lock{sock->m_mutex};
1137
1138 /* First we must check if block will fit into sock->m_rcv_buf. Why not just use feed_buf_move()'s
1139 * max_data_size argument? Because that would allow to partially enqueue the block, if there's
1140 * space for some but not all of the block. Since we can't partially ACK a packet, we have to
1141 * drop the whole thing in that case.
1142 *
1143 * Round up to a multiple of max-block-size to ensure we never fragment a max-block-size-sized
1144 * chunk of data when they're using unreliable mode! Also apply the slack % to account for
1145 * the fact that rcv_wnd sent to the other side may lag behind reality (the key is to NOT
1146 * apply the slack % when sending rcv_wnd, so that it is more conservative). */
1147 if ((sock->m_rcv_buf.data_size() + data_size)
1148 > sock->max_block_size_multiple(sock->m_opts.m_st_rcv_buf_max_size,
1149 &sock->m_opts.m_st_rcv_buf_max_size_slack_percent))
1150 {
1151 // Receive buffer overflow.
1152
1153 // Register one packet of N bytes of acceptable data that we unfortunately have to drop due to buffer overflow.
1154 rcv_stats.good_data_dropped_buf_overflow_packet(data_size);
1155
1156 // Not an error but interesting. Might be too verbose for INFO but what the hell.
1157 FLOW_LOG_INFO("NetFlow worker thread working on [" << sock << "]. "
1158 "Received [" << packet->m_type_ostream_manip << "] with "
1159 "sequence numbers [" << packet->m_seq_num << ", " << (packet->m_seq_num + data_size) << "); "
1160 "data size [" << data_size << "]; "
1161 "dropping because Receive buffer full.");
1162 return false;
1163 }
1164 // else can successfully write to Receive buffer (enough space for entire block).
1165
1166 /* Let's make data available to user! This is a constant-time operation that MOVES
1167 * packet.data's contents into m_rcv_buf (via swap). That's why packet is Ptr and not
1168 * Const_ptr. Note that after that we no longer work with packet -- it's a goner; data.empty()
1169 * is true.
1170 *
1171 * No need to provide max buffer size -- we already checked that's not an issue above. */
1172
1173#ifndef NDEBUG
1174 const size_t written =
1175#endif
1176 sock->m_rcv_buf.feed_buf_move(&data, std::numeric_limits<size_t>::max());
1177 // `data` is now empty.
1178 assert(written == data_size);
1179
1180 buf_size = sock->m_rcv_buf.data_size();
1181 } // lock{sock->m_mutex}
1182
1183 // Register one packet of N bytes of acceptable data that we accepted -- did not drop.
1184 rcv_stats.good_data_accepted_packet(data_size);
1185 // Register one packet of N bytes of acceptable data that we delivered to user.
1186 rcv_stats.good_data_delivered_packet(data_size);
1187 // Register that the Receive buffer grew.
1188 rcv_stats.buffer_fed(buf_size);
1189
1190 // They've sent reasonable data -- so handle the implications on rcv_wnd recovery (if any).
1191 receive_wnd_recovery_data_received(sock);
1192
1193 return true;
1194} // Node::sock_data_to_rcv_buf_unless_overflow()
1195
1196void Node::sock_rcv_buf_now_readable(Peer_socket::Ptr sock, bool syn_rcvd_qd_packet)
1197{
1198 /* We are told sock now has a non-empty Receive buffer and is thus Readable. Therefore we
1199 * should soon inform anyone waiting on any Event_sets for sock to become Readable.
1200 *
1201 * Caveat: Similar to that in Node::handle_syn_ack_ack_to_syn_rcvd() at similar point in the
1202 * code.
1203 *
1204 * Also: why do this outside the locked block that likely preceded this to actually write to the
1205 * Receive buffer? Avoid possibility of deadlock, since there
1206 * are two mutexes at play: sock->m_mutex (locked in the likely Receive buffer
1207 * update and in event_set_all_check_delta()) and Event_set::m_mutex (which is locked in
1208 * event_set_all_check_delta()). Different mutexes should always be locked in the same order,
1209 * and other threads lock in the sock->m_mutex/event_set->m_mutex order.
1210 *
1211 * Finally: if this packet was not received in ESTABLISHED but rather in SYN_RCVD and saved
1212 * until ESTABLISHED, then we skip this (syn_rcvd_qd_packet).
1213 * Why? Answer: in this case the socket has not yet been
1214 * given to the user (they need to call accept() or equivalent). Therefore, they could not have
1215 * added it to an Event_set and thus are not interested in Readable status on it. (For
1216 * background on this queueing, see handle_data_to_syn_rcvd(). */
1217
1218 // Accumulate the event into the Node store (note: not any Event_set yet) (if received during ESTABLISHED).
1219 if ((!syn_rcvd_qd_packet) &&
1220 m_sock_events[Event_set::Event_type::S_PEER_SOCKET_READABLE].insert(sock).second)
1221 {
1222 // Possibly inform the user for any applicable Event_sets right now.
1223 event_set_all_check_delta(true);
1224 /* ^-- defer_delta_check == true: because the only way to get to this method is from
1225 * async_low_lvl_recv(), which will perform event_set_all_check_delta(false) at the end of itself,
1226 * before the boost.asio handler exits. See Node::m_sock_events doc header for details. */
1227 }
1228} // Node::sock_rcv_buf_now_readable()
1229
1231 boost::shared_ptr<const Data_packet> packet,
1232 size_t data_size,
1233 bool* slide, size_t* slide_size)
1234{
1235 using std::make_pair;
1236
1237 /* Note this is a helper to handle_data_to_established() to make it more manageable. See comments and
1238 * flow in that caller first. */
1239
1240 *slide = false;
1241 *slide_size = 0;
1242
1243 // See comment in same spot in handle_data_to_established().
1244 Peer_socket_receive_stats_accumulator& rcv_stats = sock->m_rcv_stats;
1245 Peer_socket::Recvd_pkt_map& rcv_packets_with_gaps = sock->m_rcv_packets_with_gaps;
1246 const Sequence_number& seq_num = packet->m_seq_num;
1247
1248 /* Since we may increase rcv_packets_with_gaps size below, we may exceed the limit as described
1249 * in m_rcv_packets_with_gaps doc header. (The limit is due to memory concerns.) Let's compute
1250 * that limit. */
1251 const size_t max_packets_after_unrecvd_packet = sock_max_packets_after_unrecvd_packet(sock);
1252
1253 /* A pre-condition is: The received packet is NOT the first (earliest) unreceived packet we're waiting
1254 * for; in other words it is not the packet at the start of the first gap. So we should save
1255 * the packet into rcv_packets_with_gaps. (This will elsewhere help us, at least, detect if this
1256 * packet comes in again [duplicate]. See sock_categorize_data_to_established().) */
1257#ifndef NDEBUG
1258 const auto insert_result =
1259#endif
1260 rcv_packets_with_gaps.insert
1261 (make_pair(seq_num,
1263 {new Peer_socket::Received_packet{get_logger(), data_size, nullptr}}));
1264 // m_rcv_reassembly_q_data_size untouched because !rexmit_on.
1265 assert(!sock->rexmit_on());
1266 assert(insert_result.second); // If was already there, there's some serious bug in above logic.
1267 // No other part of the invariant is violated, so that's it.
1268
1269 bool first_gap_exists;
1270 // The sequence number of the first datum right after the first unreceived gap.
1271 Sequence_number seq_num_after_first_gap;
1272
1273 rcv_get_first_gap_info(sock, &first_gap_exists, &seq_num_after_first_gap);
1274 assert(first_gap_exists);
1275
1276 /* We would be done here, except we need to protect against rcv_packets_with_gaps growing too
1277 * large. This is explained in detail in the m_rcv_packets_with_gaps doc comment. Long story
1278 * short: if we exceed a certain length in this structure, pretend we have "received" the entire
1279 * first gap, which will allow us to slide the window forward and eliminate all the contiguous
1280 * received packets following this gap, of which there will be at least one
1281 * (rcv_packets_with_gaps.begin()), bringing the structure's size back to the limit. */
1282
1283 if (rcv_packets_with_gaps.size() == max_packets_after_unrecvd_packet + 1)
1284 {
1285 // Use these output knobs to reduce rcv_packets_with_gaps.size() after all to avoid overflow.
1286 *slide = true;
1287 *slide_size = size_t(seq_num_after_first_gap - sock->m_rcv_next_seq_num);
1288
1289 // Register unknown # of packets with N bytes of data, which we are assuming are dropped.
1290 rcv_stats.presumed_dropped_data(data_size);
1291
1292 // Not an error but interesting. Might be too verbose for INFO but what the hell.
1293 FLOW_LOG_INFO("NetFlow worker thread working on [" << sock << "]. "
1294 "Received [" << packet->m_type_ostream_manip << "] with "
1295 "sequence numbers [" << packet->m_seq_num << ", " << (packet->m_seq_num + data_size) << "); "
1296 "exceeded max gapped packet list size [" << max_packets_after_unrecvd_packet << "]; "
1297 "assuming Dropped; "
1298 "will fake receiving all [" << slide_size << "] sequence numbers in the first unreceived gap.");
1299 }
1300 else
1301 {
1302 // Our logic shouldn't be allowing the max to be exceeded by more than 1 at any time; we "wrist-slap" it above at 1.
1303 assert(rcv_packets_with_gaps.size() <= max_packets_after_unrecvd_packet);
1304 }
1305} // Node::sock_track_new_data_after_gap_rexmit_off()
1306
1308 boost::shared_ptr<Data_packet> packet)
1309{
1310 using std::make_pair;
1311
1312 /* Note this is a helper to handle_data_to_established() to make it more manageable. See comments and
1313 * flow in that caller first. */
1314
1315 Peer_socket_receive_stats_accumulator& rcv_stats = sock->m_rcv_stats;
1316 Peer_socket::Recvd_pkt_map& rcv_packets_with_gaps = sock->m_rcv_packets_with_gaps;
1317 const Sequence_number& seq_num = packet->m_seq_num;
1318
1319 auto& data = packet->m_data; // NOT const due to the move into Received_packet; see below.
1320 // Save this before we possibly destroy data's contents below (for performance).
1321 const size_t data_size = data.size();
1322
1323 /* Since we will increase rcv_packets_with_gaps size below, we may exceed the limit as
1324 * described in m_rcv_packets_with_gaps doc header. (The limit is due to memory concerns.)
1325 * Let's compute that limit. */
1326 size_t max_packets_after_unrecvd_packet = sock_max_packets_after_unrecvd_packet(sock);
1327
1328 /* Update: Actually, that limit is (as noted in the doc header for Peer_socket::m_rcv_packets_with_gaps, whose
1329 * growth we are constraining here) more of a formality, as in practice things like sender's CWND or
1330 * sender's following our rcv-wnd guidance should keep the size of this retranmission queue much lower than
1331 * the limit that was just computed. However! There IS a retransmission-enabled-exclusive limit we should
1332 * apply here, and it may at times be applied in practice, unlike what we just computed. Namely, consider
1333 * that if we receive N in-order, fully populated (up to max-block-size) DATA packets, and NxMBS exceeds
1334 * max-on-Receive-buffer, then indeed we will drop the overflowing portion and not put into Receive buffer;
1335 * but if we don't receive 1 in-order packet, get the next (N - 1) packets, and then finally get the one
1336 * missing DATA packet, then they will all be delivered to Receive buffer without a problem. (The next in-order
1337 * packet would indeed hit overflow, unless user dequeues some. This only highlights the oddness.)
1338 * Why? Because the above-computed limit is far higher than the equivalent max-on-Receive-buffer configuration
1339 * (typically), so the reassembly queue would be loaded up with stuff without hitting any limit, and the
1340 * code that dequeues from reassembly queue into Receive buffer does not follow any overflow logic (nor can it,
1341 * really, since by that point those DATA packets have long since been ACKed, and we do not renege ACKs).
1342 * Long story short, that is not good, and we should simply apply the max-on-Receive-buffer to not just
1343 * the Receive buffer but to this reassembly queue PLUS the Receive buffer.
1344 *
1345 * Caution! This policy means the rcv-wnd advertisements to the other side must follow this policy too.
1346 *
1347 * OK, make the computation as described. First compute the max-on-Receive-buffer, same as when actually computing
1348 * that when enqueueing that structure. Then subtract how much of it we've used in actual Receive buffer.
1349 * What remains is what's allowed for rcv_packets_with_gaps:
1350 *
1351 * Rbufdata + Rqdata <= Rbufmax <==> Rqdata <= Rbufmax - Rbufdata = S.
1352 * S_blocks = floor(S / max-block-size).
1353 * Ensure Rcurdata_blocks + 1 <= S_blocks.
1354 *
1355 * This is about right but actually slightly oversimplified, because that limit assumes the data are packed
1356 * in max-block-sized packets except possibly the last one. In reality the existing payload of the reassembly queue
1357 * may be not stored so efficiently (who knows how stuff got packetized or supplied by user or both?). To compute
1358 * this quite carefully (maybe overkill, but I feel deterministically understood to be correct = a good thing), we
1359 * model it as the queue already storing what it's storing; and we must allow a certain number of packets
1360 * on top of that and no more; and the question is whether that's enough for the incoming 1 DATA packet.
1361 * So then, we want this:
1362 *
1363 * Ensure Rqcurdata_blocks + 1 <= Rqcurdata_blocks + Sleft_blocks.
1364 * Sleft_blocks = # additional packets allowed by policy = floor(Sleft / max-block-size).
1365 * Sleft = max(Rbufmax - Rqcurdata - Rbufdata, 0).
1366 *
1367 * So we're doctoring it: we know Rqcurdata_blocks = rcv_packets_with_gaps.size() are already used; so we will
1368 * allow some # of packets beyond that, and the question is what is that # according to our policy? Well, it's just
1369 * the configured limit minus the used Receive buffer in bytes and minus the sum of rcv_packets_with_gaps's bytes.
1370 * Since we're using bytes there, that's the maximum possible accuracy, without any inefficiency being assumed to
1371 * not exist. Note that we have Rqcurdata* being subtracted from Rqcurdata* on one side, and that may seem like
1372 * those should cancel each other out to zero, but no -- that was the case in the simpler model above, but the more
1373 * realistic one means those are (sligthly, potentially) different. */
1374 size_t max_packets_in_reassembly_q
1375 = sock->max_block_size_multiple(sock->m_opts.m_st_rcv_buf_max_size,
1376 &sock->m_opts.m_st_rcv_buf_max_size_slack_percent);
1377 // We have to momentarily lock sock due to access to sock->m_rcv_buf.
1378 size_t rcv_buf_size;
1379 {
1381 rcv_buf_size = sock->m_rcv_buf.data_size(); // This access requires locking.
1382 }
1383 util::subtract_with_floor(&max_packets_in_reassembly_q, rcv_buf_size) && // [sic]
1384 util::subtract_with_floor(&max_packets_in_reassembly_q, sock->m_rcv_reassembly_q_data_size);
1385 // Convert from bytes to max-block-sizes. Note this is the floor of the division (so it is strict).
1386 max_packets_in_reassembly_q /= sock->max_block_size();
1387 /* Okay, we have Sleft in blocks now; add this for direct comparison to the left side, which will be .size() + 1,
1388 * where the 1 is the incoming packet `packet`. Full-circle, this is `Rqcurdata_blocks + Sleft_blocks` from
1389 * the above big comment. */
1390 max_packets_in_reassembly_q += rcv_packets_with_gaps.size();
1391
1392 // The final limit is the lower of the two limits; realistically we expect max_packets_in_reassembly_q to "win."
1393 if (max_packets_in_reassembly_q < max_packets_after_unrecvd_packet)
1394 {
1395 max_packets_after_unrecvd_packet = max_packets_in_reassembly_q;
1396 }
1397 else
1398 {
1399 // Not an error but pretty weird configuration (but too verbose for INFO, if it really does occur).
1400 FLOW_LOG_TRACE("Unexpected Receive buffer limits: safety net [" << max_packets_after_unrecvd_packet << "] <= "
1401 "real limit [" << max_packets_in_reassembly_q << "], but the opposite is typical. "
1402 "See details just below."); // See next log message.
1403 }
1404
1405 if (rcv_packets_with_gaps.size() + 1 > max_packets_after_unrecvd_packet)
1406 {
1407 /* Overflow. Drop this new packet instead of queueing it. Note that this is different
1408 * from the handling of the same situation in the no-retransmit case. In that case, this
1409 * situation is probably more common under loss, since once a packet is considered Dropped by sender, it is NEVER
1410 * re-sent; thus Receiver eventually also considers it Dropped and (instead of dropping
1411 * the new packet, which would be a disastrous policy) simply pretends the gap has been
1412 * filled, thus consolidating the front of rcv_packets_with_gaps. */
1413
1414 // Register one packet of N bytes of acceptable data that we unfortunately have to drop due to overflow.
1416
1417 // This is an error, though not our fault.
1418 FLOW_LOG_WARNING("NetFlow worker thread working on [" << sock << "]. "
1419 "Received [" << packet->m_type_ostream_manip << "] with "
1420 "sequence numbers [" << packet->m_seq_num << ", " << (packet->m_seq_num + data_size) << "); "
1421 "exceeded max gapped packet list size [" << max_packets_after_unrecvd_packet << "]; "
1422 "dropping packet.");
1423 return false;
1424 }
1425 // else we can insert into reassembly queue (priority queue by seq. #) rcv_packets_with_gaps.
1426
1427 FLOW_LOG_TRACE("NetFlow worker thread working on [" << sock << "]. "
1428 "Enqueueing [" << packet->m_type_ostream_manip << "] payload onto reassembly queue with "
1429 "sequence numbers [" << packet->m_seq_num << ", " << (packet->m_seq_num + data_size) << ") "
1430 "of size [" << data_size << "]; "
1431 "successfully fit into max gapped packet list size [" << max_packets_after_unrecvd_packet << "]; "
1432 "could have fit [" << (max_packets_after_unrecvd_packet - rcv_packets_with_gaps.size()) << "] more.");
1433
1434 // This decimates `data` but is constant time, much like the buffer enqueueing done elsewhere.
1435#ifndef NDEBUG
1436 const auto insert_result =
1437#endif
1438 rcv_packets_with_gaps.insert
1439 (make_pair(seq_num, // Decimation occurs in here: ------------------v, hence the `&`: -------------v.
1441 sock->m_rcv_reassembly_q_data_size += data_size;
1442 assert(insert_result.second); // If was already there, there's some serious bug in above logic.
1443 // No other part of the invariant is violated, so that's it.
1444
1445 // DO NOT use `data` from this point forward -- it was just emptied by moving into the new Received_packet.
1446
1447 // Register one packet of N bytes of acceptable data that we accepted -- did not drop.
1448 rcv_stats.good_data_accepted_packet(data_size);
1449 // Register one packet of N bytes of acceptable data that we queued for reassembly -- not yet in Receive buffer.
1450 rcv_stats.good_data_first_qd_packet(data_size);
1451
1452 // They've sent reasonable data -- so handle the implications on rcv_wnd recovery (if any).
1453 receive_wnd_recovery_data_received(sock);
1454
1455 return true;
1456} // Node::sock_data_to_reassembly_q_unless_overflow()
1457
1458void Node::sock_slide_rcv_next_seq_num(Peer_socket::Ptr sock, size_t slide_size, bool reassembly_in_progress)
1459{
1460 /* Note this is a helper to handle_data_to_established() to make it more manageable. See comments and
1461 * flow in that caller first. */
1462
1463 // See comment in same spot in handle_data_to_established().
1464 Peer_socket_receive_stats_accumulator& rcv_stats = sock->m_rcv_stats;
1465 Peer_socket::Recvd_pkt_map& rcv_packets_with_gaps = sock->m_rcv_packets_with_gaps;
1466 Sequence_number& rcv_next_seq_num = sock->m_rcv_next_seq_num;
1467
1468 /* OK, caller determined that the front of the gap between rcv_next_seq_num and
1469 * seq_num_after_first_gap has been received. Indeed mark this fact by sliding the former to a higher value,
1470 * indicating sliding right of the left edge of the receive window, in TCP terminology. */
1471 rcv_next_seq_num += slide_size; // Use op+= over advance_seq_num(): slide_size is of Sequence_numbers, not bytes.
1472
1473 FLOW_LOG_TRACE("First unreceived packet pointer moved from "
1474 "[" << (rcv_next_seq_num - slide_size) << "] to "
1475 "[" << rcv_next_seq_num << "].");
1476
1477 /* Now update the receive window structure. Maintain invariant described in doc headers for
1478 * for m_rcv_packets_with_gaps and related members. Additionally, IF retranmission-related
1479 * reassembly is in progress (presumably, because retransmission is enabled), and if the new packet bridged
1480 * gap to the first seq.-#-contiguous packet(s) in the reassembly queue, then add their data to Receive buffer
1481 * also. */
1482
1483 // Start of range to delete.
1484 const Peer_socket::Recvd_pkt_iter start_contig_it = rcv_packets_with_gaps.begin();
1485 // End of range to delete (just past last element to delete).
1486 Peer_socket::Recvd_pkt_iter end_contig_it;
1487 size_t total_written = 0;
1488
1489 // The following loop is O(n) worst case.
1490 for (end_contig_it = start_contig_it;
1491 /* Search until the infinite gap is found; or the first finite gap is found.
1492 * Note invariant at entry to each loop iteration: rcv_next_seq_num is seq. # just past last received
1493 * packet's data (so for contiguousness, it must equal the 1st seq. # in next packet). */
1494 (end_contig_it != rcv_packets_with_gaps.end()) && (end_contig_it->first == rcv_next_seq_num);
1495 ++end_contig_it)
1496 {
1497 Peer_socket::Received_packet& rcvd_packet = *end_contig_it->second;
1498
1499 if (reassembly_in_progress)
1500 {
1501 /* Receive Buffer can be consumed by user threads (not W) at the same time. Must lock.
1502 * @todo Probably possible to make the critical section smaller.
1503 *
1504 * Conversely, maybe it's better to lock around the entire while () loop, for potentially less
1505 * locking/unlocking while another thread is reading from buffer, which intuitively "feels" churn-y.
1506 * Arguments against: the loop may have 0 iterations, meaning the locking was a waste; also, locking
1507 * once per packet is no worse in aggregate than if we'd received these packets in order without
1508 * needing reassembly -- and that's the much more typical state of affairs; so it's not like we're
1509 * adding some unusually excessive amount of locking/unlocking by locking once per packet during
1510 * reassembly. */
1511 size_t written;
1512 size_t buf_size;
1513 {
1514 Peer_socket::Lock_guard lock{sock->m_mutex};
1515
1516 /* Reassemble! This is constant-time. Note we don't check for overflow here, but that's because we
1517 * checked for it cleverly in first enqueueing this in rcv_packets_with_gaps
1518 * (see sock_data_to_reassembly_q_unless_overflow()). */
1519 written = sock->m_rcv_buf.feed_buf_move(&rcvd_packet.m_data, std::numeric_limits<size_t>::max());
1520 // rcvd_packet.m_data is now empty.
1521 buf_size = sock->m_rcv_buf.data_size();
1522 }
1523 total_written += written;
1524
1525 // Similarly to when receiving a first-gap-filling (or just in-order, if there is no gap) DATA packet:
1526 rcv_stats.good_data_delivered_packet(written);
1527 rcv_stats.buffer_fed(buf_size);
1528
1529 assert(written != 0);
1530 }
1531
1532 advance_seq_num(&rcv_next_seq_num, rcvd_packet.m_size);
1533
1534 FLOW_LOG_TRACE("First unreceived packet pointer moved again to "
1535 "[" << rcv_next_seq_num << "]; packet subsumed by this move.");
1536 } // while (keep encountering contiguous packets)
1537
1538 // The following, according to STL requirements, is O(k + log n), where k is # erased; thus O(n) worst case.
1539 rcv_packets_with_gaps.erase(start_contig_it, end_contig_it); // Does nothing if end_contig_it == start_contig_it.
1540 sock->m_rcv_reassembly_q_data_size -= total_written;
1541} // Node::sock_slide_rcv_next_seq_num()
1542
1544{
1545 /* The limit itself is not an option but rather computed from other options to be
1546 * more dynamic. Let N be the desired max ratio of rcv_packets_with_gaps.size() * max-block-size
1547 * to the max Receive buffer size, expressed in percent. Then the max
1548 * rcv_packets_with_gaps.size() value is N% * <max Receive buffer size> / max-block-size / 100%.
1549 * N is the option m_st_rcv_max_packets_after_unrecvd_packet_ratio_percent. */
1550 return uint64_t(sock->opt(sock->m_opts.m_st_rcv_max_packets_after_unrecvd_packet_ratio_percent)) *
1551 uint64_t(sock->opt(sock->m_opts.m_st_rcv_buf_max_size)) /
1552 uint64_t(sock->max_block_size()) /
1553 100;
1554}
1555
1557 bool* first_gap_exists, Sequence_number* seq_num_after_first_gap)
1558{
1559 // If false, all received packets are followed by all unreceived ones. Otherwise there's at least 1 gap.
1560 *first_gap_exists = !sock->m_rcv_packets_with_gaps.empty();
1561 // If true, then this is the sequence number of the first datum right after that first gap.
1562 if (*first_gap_exists)
1563 {
1564 *seq_num_after_first_gap = sock->m_rcv_packets_with_gaps.begin()->first;
1565 }
1566}
1567
1568void Node::async_acknowledge_packet(Peer_socket::Ptr sock, const Sequence_number& seq_num, unsigned int rexmit_id,
1569 size_t data_size)
1570{
1571 // We are in thread W.
1572
1573 // Plenty of info logged in caller, so don't re-log.
1574 FLOW_LOG_TRACE("Accumulating for acknowledgment.");
1575
1576 // Register one packet with N bytes of data (not necessarily acceptable data).
1577 sock->m_rcv_stats.total_to_send_ack_packet(data_size);
1578
1579 const size_t acks_pending_before_this = sock->m_rcv_pending_acks.size();
1580
1581 static_assert(std::is_aggregate_v<Peer_socket::Individual_ack>,
1582 "We want it to be direct-initializable.");
1583 static_assert((!std::is_copy_constructible_v<Peer_socket::Individual_ack>)
1584 && (!std::is_copy_assignable_v<Peer_socket::Individual_ack>),
1585 "We want it to be noncopyable but rather passed-around via its ::Ptr.");
1586
1587 /* Just the starting sequence number sufficient to identify a single packet. The time point saved
1588 * here is subtracted from time_now() at ACK send time, to compute the artificial delay introduced
1589 * by ACK delaying (explained just below). This helps other side calculate a more accurate RTT by
1590 * substracting the ACK delay from its RTT measurement. */
1591 sock->m_rcv_pending_acks.push_back
1593 {new Peer_socket::Individual_ack{ seq_num, rexmit_id, Fine_clock::now(), data_size }});
1594
1595 /* m_rcv_pending_acks now stores at least one packet to acknowledge. We can acknowledge it
1596 * immediately (modulo UDP layer availability of course). However, suppose there is a fast stream
1597 * of packets coming in, such that several DATA packets were read in within one
1598 * low_lvl_recv_and_handle() call. Then each DATA packet will result in one ACK packet.
1599 * This introduces a ton of overhead, as the header is quite large given that the payload is just
1600 * a Sequence_number. Instead we would want to pack all the DATA packets' acknowledgments into
1601 * one ACK packet (unless it overflows, in which case create more ACK packets as needed). So we
1602 * only accumulate the individual acknowledgments here; we will possibly send the actual ACK(s) in
1603 * perform_accumulated_on_recv_tasks(), which runs at the end of low_lvl_recv_and_handle() (or its
1604 * bro, the async part of async_wait_latency_then_handle_incoming()).
1605 *
1606 * Caveat: The above is rock-solid if the different DATA packets being acked were contiguous to
1607 * each other chronologically. What if there is another type of packet between some two of these
1608 * DATAs? Well, it depends on what it is. Ignoring the misbehaving/duplicate/whatever packets
1609 * (SYN, for example) -- which will just be discarded basically -- let's consider the
1610 * possibilities. If the packet is ACK, then it is irrelevant; NetFlow (like TCP) is full-duplex
1611 * (actually more so, since there's no DATA+ACK piggy-backing), therefore the micro-ordering of
1612 * traffic in opposite directions is irrelevant. If the packet is RST, then that means the socket
1613 * will get closed (no longer ESTABLISHED) before we get a chance to send any of the individual
1614 * acknowledgments. However, that is more or less OK; if the other side sent RST, then they won't
1615 * accept any ACKs we may send them anyway. The only other possibility has to with graceful close,
1616 * but that is not yet implemented.
1617 * @todo Revisit this when graceful close is implemented. (Preliminary idea: force immediate ACK
1618 * handling when FIN/etc. detected? Or something.) */
1619
1620 if (m_socks_with_accumulated_pending_acks.insert(sock).second)
1621 {
1622 /* First acknowledgment to be accumulated in this handler (low_lvl_recv_and_handle() or
1623 * async part of async_wait_latency_then_handle_incoming()). So mark down whether at that time there were
1624 * already timer-delayed acknowledgments pending (and how many). See
1625 * sock_perform_accumulated_on_recv_tasks() for details on delayed ACKs. */
1626 sock->m_rcv_pending_acks_size_at_recv_handler_start = acks_pending_before_this;
1627 }
1628 // else already had registered pending acknowledgment in this handler.
1629} // Node::async_acknowledge_packet()
1630
1632{
1633 using boost::chrono::milliseconds;
1634 using boost::chrono::microseconds;
1635 using boost::chrono::duration_cast;
1636 using boost::chrono::round;
1637 using std::vector;
1638
1639 // We are in thread W.
1640
1641 // For background see Node::perform_accumulated_on_recv_tasks().
1642
1643 // For brevity and speed:
1644 vector<Peer_socket::Individual_ack::Ptr>& pending_acks = sock->m_rcv_pending_acks;
1645
1646 if (sock->m_int_state != Peer_socket::Int_state::S_ESTABLISHED)
1647 {
1648 // For example, we got DATA and then RST on the same socket almost simultaneously.
1649 FLOW_LOG_TRACE("Was about to perform accumulated acknowledgment tasks on [" << sock << "] but skipping because "
1650 "state is now [" << sock->m_int_state << "].");
1651 return;
1652 }
1653
1654 // Check explicit pre-condition.
1655 assert(!pending_acks.empty());
1656
1657 /* Deal with any accumulated acknowledgments. Naively, we'd simply call async_low_lvl_ack_send()
1658 * here, which would take pending_acks and bundle them up into as few as possible ACK
1659 * packets and send them off.
1660 *
1661 * However, we potentially instead use delayed ACKing as in typical TCP implementations (based on
1662 * various standard RFCs). The idea is that a few DATA packets have come in around the same time,
1663 * but not close enough to be handled in one receive handler. So upon detecting the first DATA
1664 * packet in the steady state, start a timer; until it fires accumulate more packets in
1665 * pending_acks; and when it fires finally assemble and flush (send) the ACK(s). Something else may trigger
1666 * the flushing of the ACK(s) ahead of this timer or even immediately.
1667 *
1668 * These are situations where we must short-circuit the timer and send the ACK(s)
1669 * immediately:
1670 *
1671 * 1. From TCP (RFC 5681-4.2), which says that an ACK should be generated for at
1672 * least every second full-sized (data size = MSS) incoming data segment. The reasoning is
1673 * two-fold: causing bursty sending by the receiver of the ACKs; and slowing down slow start
1674 * in Reno (and others) congestion control. The latter is not really a problem for us (since
1675 * ACKs are not cumulative but selective and handled as such by our congestion control logic);
1676 * but the former is definitely an easily demonstrable issue. @todo This paragraph is difficult
1677 * to understand right now. There might be 1 or more unintentional meaning inversions, wherein
1678 * I mean to say X is good, but instead say X is bad, or vice vera, or at least it's unclear. Research;
1679 * rephrase.
1680 *
1681 * 2. Also from TCP (RFC 5681-3.2), which says that an ACK should be
1682 * immediately generated upon detecting an out-of-order data segment. This is to inform
1683 * congestion control of any loss event as soon as possible (Fast Recovery algorithm).
1684 *
1685 * Note that TCP RFCs don't account for the implementation detail that several packets can be
1686 * received "simultaneously" (in one handler in our case), nor for selective ACKs (in this
1687 * context), so when they say we must send an ACK for every 2 incoming segments at least, we do
1688 * not take this literally. Instead, we just say that if (here, after a full receive handler has
1689 * run) there are at least 2 full blocks' worth of pending acknowledgments (there could be many
1690 * more in theory) and/or there's an out-of-order DATA packet, then we send immediate ACK(s), thus
1691 * following the spirit of the rules in the RFC. The spirit of the rule is to short-circuit the
1692 * timer the moment at least 2 full packets can be acknowledged.
1693 *
1694 * We detect both of these situations below and act accordingly. We also start the delayed ACK
1695 * timer, if necessary, otherwise. Oh, and there's a mode to disable delayed ACKs.
1696 *
1697 * @todo We may also force immediate ACKing during graceful shutdown. Revisit when graceful
1698 * shutdown is implemented.... */
1699
1700 const Fine_duration delayed_ack_timer_period = sock->opt(sock->m_opts.m_st_delayed_ack_timer_period);
1701
1702 bool force_ack = delayed_ack_timer_period == Fine_duration::zero(); // Delayed ACKs disabled.
1703
1704 if (force_ack)
1705 {
1707 ("Delayed [ACK] feature disabled on [" << sock << "]; forcing immediate [ACK]. "
1708 "Receive window state: [" << sock->m_rcv_init_seq_num << ", " << sock->m_rcv_next_seq_num << ") "
1709 "| " << sock->m_rcv_packets_with_gaps.size() << ":{...}.");
1710 }
1711 else if (!sock->m_rcv_packets_with_gaps.empty())
1712 {
1713 /* Scan to see if there was an out-of-order DATA packet. That is to say, have we received a
1714 * DATA packet -- i.e., have we queued a pending acknowledgment in this receive handler -- that
1715 * follows at least one unreceived packet in the sequence number space.
1716 *
1717 * There is a gap in the received sequence number space, so this is potentially possible. Scan
1718 * only the DATA packets (acknowledgments) accumulated in THIS handler (since previous ones
1719 * have already been checked, and unreceived gaps can't just appear out of nowhere later). If
1720 * any is past the first gap, it qualifies. (The reverse is true. If it's past any gap, it's
1721 * past the first gap.) */
1723 for (size_t ack_idx = sock->m_rcv_pending_acks_size_at_recv_handler_start;
1724 ack_idx != pending_acks.size(); ++ack_idx)
1725 {
1726 ack = pending_acks[ack_idx];
1727 if (ack->m_seq_num > sock->m_rcv_next_seq_num)
1728 {
1729 force_ack = true;
1730 break;
1731 }
1732 }
1733
1734 if (force_ack)
1735 {
1737 ("On [" << sock << "] "
1738 "received out-of-order packet [" << ack->m_seq_num << ", size " << ack->m_data_size << ", "
1739 "rexmit " << ack->m_rexmit_id << "]; "
1740 "forcing immediate [ACK]. "
1741 "Receive window state: [" << sock->m_rcv_init_seq_num << ", " << sock->m_rcv_next_seq_num << ") "
1742 "| " << sock->m_rcv_packets_with_gaps.size() << ":{...}.");
1743 }
1744 }
1745 if (!force_ack)
1746 {
1747 // No out-of-order stuff. See if there are at least N * max-block-size bytes pending to be acknowledged.
1748
1749 const size_t limit // Default 2.
1750 = sock->opt(sock->m_opts.m_st_max_full_blocks_before_ack_send) * sock->max_block_size();
1751 size_t bytes = 0;
1752 for (Peer_socket::Individual_ack::Const_ptr ack : pending_acks)
1753 {
1754 bytes += ack->m_data_size;
1755 if (bytes >= limit)
1756 {
1757 force_ack = true;
1758 break;
1759 }
1760 }
1761
1762 if (force_ack)
1763 {
1764 FLOW_LOG_TRACE("On [" << sock << "] "
1765 "accumulated at least [" << limit << "] bytes to acknowledge; "
1766 "forcing immediate [ACK].");
1767 }
1768 }
1769
1770 // OK; force_ack is set finally.
1771
1772 if (force_ack)
1773 {
1774 /* Yep, must send ACK(s) now. There are two possibilities. One, a delayed ACK timer may
1775 * already be running. If so, we should cancel it and send immediately. If the cancel fails
1776 * (returns 0 tasks canceled), then it was already queued to fire very soon, so we should
1777 * just let the ACKing happen that way instead of sending immediately.
1778 *
1779 * Two, a timer is not running, so we shouldn't cancel and should just send immediately.
1780 *
1781 * How to determine if timer is currently running? If
1782 * m_rcv_pending_acks_size_at_recv_handler_start == 0, then the timer was either never scheduled
1783 * (only scheduled when pending_acks.empty()) or was triggered and handled before the current
1784 * handler; therefore it is not running. Otherwise, there were pending acks to send, yet they
1785 * were not sent by the end of the last handler, which means the timer must be running.
1786 *
1787 * (There may be some corner case I'm not imagining such that the timer was running even while
1788 * m_rcv_pending_acks_size_at_recv_handler_start == 0, but even then the worst that will happen is
1789 * that we will perform the ACKing here, not cancel that wait, and that timer will
1790 * harmlessly expire with the timer handler doing nothing.) */
1791
1792 if (sock->m_rcv_pending_acks_size_at_recv_handler_start != 0)
1793 {
1794 FLOW_LOG_TRACE("On [" << sock << "] "
1795 "canceling delayed [ACK] timer due to forcing "
1796 "immediate [ACK]; would have fired "
1797 "in [" << round<milliseconds>(sock->m_rcv_delayed_ack_timer.expiry() - Fine_clock::now()) << "] "
1798 "from now.");
1799
1800 if (sock->m_rcv_delayed_ack_timer.cancel() == 0)
1801 {
1802 /* Unlikely but legitimate; timer was queued to trigger very soon, so we could not
1803 * cancel it. No problem -- just let the ACKing happen per timer. Log INFO due to
1804 * rarity of this situation. */
1805 FLOW_LOG_INFO("On [" << sock << "] "
1806 "tried to cancel delayed [ACK] timer while "
1807 "forcing [ACK], but it was already just about to fire.");
1808 force_ack = false;
1809 }
1810 } // if (m_rcv_pending_acks_size_at_recv_handler_start != 0) [timer was running]
1811
1812 // If still forcing immediate ACK, finally do it.
1813 if (force_ack)
1814 {
1815 async_low_lvl_ack_send(sock);
1816 assert(pending_acks.empty());
1817 }
1818 } // if (force_ack)
1819 else // if (!force_ack)
1820 {
1821 /* There are pending individual acks but no reason to send them off right now. The only
1822 * remaining question is whether we need to schedule the delayed ACK timer to send them
1823 * later. That depends on whether the timer is already running. If
1824 * m_rcv_pending_acks_size_at_recv_handler_start == 0, then the timer was either never scheduled
1825 * or was triggered and handled before the current handler; therefore it is not running. So
1826 * in that case we should start it, as we've just received our first ackable DATA since
1827 * we've sent off our last ACK. If m_rcv_pending_acks_size_at_recv_handler_start != 0, then the
1828 * timer must be running, because there were pending acks to send, yet they were not send by
1829 * the end of the last handler (which would have caused this very code to schedule the
1830 * timer).
1831 *
1832 * (There may be some corner case I'm not imagining such that the timer was running even while
1833 * m_rcv_pending_acks_size_at_recv_handler_start == 0, but even then it can't possibly be set to
1834 * the right time [which is S_DELAYED_ACK_TIMER_PERIOD for now], so we need to re-set it
1835 * anyway. [Re-setting the expiry time will cancel that running timer wait. Even if that
1836 * somehow fails, the worst case is that the ACK(s) will be sent prematurely.]) */
1837
1838 if (sock->m_rcv_pending_acks_size_at_recv_handler_start == 0)
1839 {
1840 // First individual acknowledgment accumulated: start countdown to send the next batch of acknowledgments.
1841
1842 sock->m_rcv_delayed_ack_timer.expires_after(delayed_ack_timer_period);
1843
1844 FLOW_LOG_TRACE("On [" << sock << "] "
1845 "scheduled delayed [ACK] timer to fire "
1846 "in [" << round<milliseconds>(delayed_ack_timer_period) << "].");
1847
1848 // When triggered or canceled, call this->async_low_lvl_ack_send(sock, <error code>).
1849 sock->m_rcv_delayed_ack_timer.async_wait([this, socket_id, sock](const Error_code& sys_err_code)
1850 {
1851 async_low_lvl_ack_send(sock, sys_err_code);
1852 });
1853 // ^-- defer_delta_check == false: for similar reason as in send_worker_check_state() calling send_worker().
1854 }
1855 // else the timer is already started, so just accumulating onto pending_acks is enough. Done.
1856 } // if (!force_ack)
1857
1858 // Register the current # of DATA packets to acknowledge. Note that we're near the end of current handler.
1859 sock->m_rcv_stats.current_pending_to_ack_packets(pending_acks.size());
1860} // Node::sock_perform_accumulated_on_recv_tasks()
1861
1862void Node::log_rcv_window(Peer_socket::Const_ptr sock, bool force_verbose_info_logging) const
1863{
1864 using std::vector;
1865 using std::string;
1866 using boost::algorithm::join;
1867
1868 // We're in thread W.
1869
1870 // For brevity and a little speed:
1871 const Peer_socket::Recvd_pkt_map& rcv_packets_with_gaps = sock->m_rcv_packets_with_gaps;
1872
1873 // force_verbose_info_logging => log the most detail, as INFO (if INFO logging enabled).
1874
1875 auto const logger_ptr = get_logger();
1876 if (((!logger_ptr) || (!logger_ptr->should_log(log::Sev::S_DATA, get_log_component()))) &&
1877 (!(force_verbose_info_logging && logger_ptr->should_log(log::Sev::S_INFO, get_log_component()))))
1878 {
1879 // Can't print entire In-flight data structure, but can print a summary, if TRACE enabled.
1881 ("Receive window state for [" << sock << "]: "
1882 "[" << sock->m_rcv_init_seq_num << ", " << sock->m_rcv_next_seq_num << ") "
1883 "| " << rcv_packets_with_gaps.size() << ":{...}.");
1884 return;
1885 }
1886 // else
1887
1888 /* Construct full printout of the packets we've received past the first unreceived gap.
1889 *
1890 * Very verbose and slow! Even so, if it gets beyond a certain size it's absurd, so skip some in
1891 * that case even though DATA logging is sanctioned. (That amount of data cannot really be useful
1892 * in any case.) */
1893
1894 vector<string> pkt_strs;
1895 pkt_strs.reserve(rcv_packets_with_gaps.size());
1896
1897 const size_t MAX_TO_SHOW = 100;
1898 bool skipped_some = false;
1899 size_t count = 0;
1900
1901 for (Peer_socket::Recvd_pkt_const_iter pkt_it = rcv_packets_with_gaps.begin();
1902 pkt_it != rcv_packets_with_gaps.end();
1903 ++pkt_it)
1904 {
1905 const bool last_iteration = (count == rcv_packets_with_gaps.size() - 1);
1906
1907 if ((!skipped_some) && (count > MAX_TO_SHOW) && (!last_iteration))
1908 {
1909 // First packet past the limit we can print. Start skipping mode.
1910 skipped_some = true;
1911 ++count;
1912 continue;
1913 }
1914 // else either we are in skipping more from before, or we are not in skipping mode.
1915
1916 string pkt_str;
1917
1918 if (skipped_some)
1919 {
1920 // We are in skipping mode from before.
1921 if (!last_iteration)
1922 {
1923 // Since it's not the last iteration, skip: print nothing.
1924 ++count;
1925 continue;
1926 }
1927 // else we are in skipping more from before, and this is the last iteration. Print the placeholder.
1928 pkt_str = "[...skipped...] ";
1929 }
1930 // Either we are not in skipping mode (just print the thing) or we are and it's last iteration (also print it).
1931
1932 Sequence_number start, end;
1933 get_seq_num_range(pkt_it, &start, &end);
1934
1935 util::ostream_op_to_string(&pkt_str, '[', start, ", ", end, ')');
1936 pkt_strs.push_back(pkt_str);
1937
1938 ++count;
1939 } // for (packets in rcv_packets_with_gaps)
1940
1942 (force_verbose_info_logging ? log::Sev::S_INFO : log::Sev::S_DATA,
1943 "Receive window state for [" << sock << "]: "
1944 "[" << sock->m_rcv_init_seq_num << ", " << sock->m_rcv_next_seq_num << ") "
1945 "| " << rcv_packets_with_gaps.size() << ":{" << join(pkt_strs, " ") << "}.");
1946} // Node::log_rcv_window()
1947
1949 boost::shared_ptr<const Ack_packet> ack)
1950{
1951 // We are in thread W.
1952
1953 /* packet is an ACK, so its payload consists of at least m_rcv_wnd (the current advertised Receive
1954 * buffer space on the receiver) and packet->m_rcv_acked_packets, which is basically a list of ZERO or
1955 * more sequence numbers, each of which represents a packet we'd (hopefully) sent that the
1956 * receiver has received. Naively we'd just handle the window update and each individual ack here
1957 * in a loop, then inform congestion control, etc. etc. However there is an optimization to make.
1958 * Suppose in the calling low_lvl_recv_and_handle() or async-part-of-async_wait_latency_then_handle_incoming()
1959 * there are several more ACKs for this socket sock that will be received. This may well happen in
1960 * high traffic; for instance the sender may have had too many individual acks for one ACK and
1961 * thus sent several; or maybe the UDP net-stack had a few packets ready by the time boost.asio was
1962 * free in thread W. In this case, it is better to collect all the individuals acks in these
1963 * several ACKs, and then handle them all at the same time. Why? Answer: it will update our
1964 * sender state (what's ACKed, what's dropped) entirely in one go instead of doing it in two or
1965 * more steps. Because congestion control activities ("on drop event," "on acknowledgment") are
1966 * performed after handling all the available acks, it gives a truer, simpler picture to the
1967 * congestion control module, when compared to giving it one picture and then almost instantly
1968 * giving it another. Another way to think of it is simply that since the different ACKs arrived
1969 * at the same time, and all an ACK is is a collection of individual acks that could fit into the
1970 * ACK packet, then conceptually this is no different from being one super-ACK with all the
1971 * individual acks contained in it. Therefore it is at least not worse.
1972 *
1973 * (In addition, m_rcv_wnd also affects the decision on whether to send more data over the wire,
1974 * as can_send() is part of that same algorithm.)
1975 *
1976 * Caveat: The above is rock-solid if the different ACKs being combined were contiguous to each
1977 * other chronologically. What if there is another type of packet between some two of these ACKs?
1978 * Well, it depends on what it is. Ignoring the misbehaving/duplicate/whatever packets (SYN, for
1979 * example) -- which will just be discarded basically -- let's consider the possibilities. If
1980 * the packet is DATA, then it is irrelevant; NetFlow (like TCP) is full-duplex (actually more so,
1981 * since there's no DATA+ACK piggy-backing), therefore the micro-ordering of traffic in opposite
1982 * directions is irrelevant. If the packet is RST, then that means the socket will get closed (no
1983 * longer ESTABLISHED) before we get a chance to process any of the individual acknowledgments.
1984 * However, that is more or less OK; if the other side sent RST, then they won't accept any
1985 * further data we may send after processing the acknowledgments anyway. The only other
1986 * possibility has to with graceful close, but that is not yet implemented.
1987 * @todo Revisit this when graceful close is implemented. (Preliminary idea: accumulate DATA and
1988 * FIN/etc. packets and always handle them after handling ACKs. Then the DATA/FIN stream will not
1989 * have a chance to disrupt (by initiating closing the connection) the ACK handling, while the ACK
1990 * handling should have no bearing on the DATA/FIN stream.)
1991 *
1992 * So, let's accumulate the individual acks in packet->m_rcv_acked_packets into a big
1993 * sock->m_rcv_acked_packets to be handled from perform_accumulated_on_recv_tasks() at the end of the
1994 * current handler. Similarly save m_rcv_wnd into sock->m_pending_rcv_wnd. To let that method
1995 * know sock has a new m_pending_rcv_wnd and possibly non-empty sock->m_rcv_acked_packets, insert sock
1996 * into m_socks_with_accumulated_acks. */
1997
1998 /* Note: We're not setting the actual sock->m_snd_remote_rcv_wnd until
1999 * perform_accumulated_on_recv_tasks().
2000 *
2001 * Also note: the latest ACK to arrive in this receive handler will contain the most up-to-date
2002 * rcv_wnd value (previous ones are overwritten by this). */
2003 sock->m_snd_pending_rcv_wnd = ack->m_rcv_wnd;
2004
2005 // It's a (ref-counted) pointer copy. Note there may be 0 elements there, if it's just an m_rcv_wnd update alone.
2006 sock->m_rcv_acked_packets.insert(sock->m_rcv_acked_packets.end(), // Append.
2007 ack->m_rcv_acked_packets.begin(), ack->m_rcv_acked_packets.end());
2008 m_socks_with_accumulated_acks.insert(sock); // May already be in there.
2009
2010 FLOW_LOG_TRACE("NetFlow worker thread working on [" << sock << "]. "
2011 "Received and accumulated [" << ack->m_type_ostream_manip << "] with "
2012 "[" << ack->m_rcv_acked_packets.size() << "] individual acknowledgments "
2013 "and rcv_wnd = [" << ack->m_rcv_wnd << "]; total for this socket in this "
2014 "receive handler is [" << sock->m_rcv_acked_packets.size() << "] individual acknowledgments.");
2015
2016 sock->m_snd_stats.received_low_lvl_ack_packet(ack->m_rcv_acked_packets.empty());
2017} // Node::handle_ack_to_established()
2018
2020{
2021 using std::min;
2022 using std::vector;
2023 using boost::tuple;
2024 using boost::unordered_set;
2025 using boost::chrono::round;
2026 using boost::chrono::milliseconds;
2027 using boost::chrono::seconds;
2028
2029 /* This is a complex method that does many things. Therefore readability is hard to accomplish, as the logic
2030 * makes sense when writing it, but the big picture is hard to see when reading it. The necessary heavy commenting
2031 * further increases the size and therefore (along that dimension) decreases readability. For these reasons,
2032 * many logically distinct parts were placed into helper methods -- not to increase code reuse but to help
2033 * the aforementioned consideration. */
2034
2035 // We are in thread W.
2036
2037 log_accumulated_acks(sock);
2038 // Below TRACE messages omit most of the just-logged detail, since it's already logged now.
2039
2040 // For brevity and a little speed:
2041 using Acks = vector<Ack_packet::Individual_ack::Ptr>;
2042 Acks& acked_packets = sock->m_rcv_acked_packets;
2043 /* To not put already-handled acknowledgments up for handling again in the next run of this method
2044 * (which would be wrong), we must clear acked_packets before exiting this method. To be safe,
2045 * make sure acked_packets.clear() runs no matter how this method exits. */
2046 const auto cleanup = util::setup_auto_cleanup([&]() { acked_packets.clear(); });
2047
2048 /* Handle all the acknowledgments we've received in this receive handler. Background on the
2049 * accumulation tactic is in handle_ack_to_established(). As explained in that method, some
2050 * packet between the first and last ACK received in this handler may have changed state away from
2051 * ESTABLISHED. For example, there could have been an RST. Check for that. */
2052 if (sock->m_int_state != Peer_socket::Int_state::S_ESTABLISHED)
2053 {
2054 // Rare/interesting enough for INFO.
2055 FLOW_LOG_INFO("NetFlow worker thread working on [" << sock << "]. "
2056 "Accumulated [ACK] packets with [" << acked_packets.size() << "] "
2057 "individual acknowledgments, but state is now [" << sock->m_int_state << "]; ignoring ACKs forever.");
2058 return;
2059 }
2060 // else OK. Handle the accumulated acknowledgments.
2061 assert(sock->m_int_state == Peer_socket::Int_state::S_ESTABLISHED);
2062
2063 /* The individual acknowledgments are (sequence number, ACK delay in unit X, retransmission ID)
2064 * triples, where the latter is always zero unless retransmission is enabled. Let's handle each
2065 * one by updating m_snd_flying_pkts* (i.e., removing that packet from m_snd_flying_pkts*) and
2066 * informing congestion control. Before continuing reading the method please look at the large
2067 * comments for Peer_socket::m_snd_flying_pkts_by_{sent_when|seq_num} (drawing a diagram might also help).
2068 *
2069 * Before continuing, quick discussion of corner cases:
2070 *
2071 * Any two given such triples may have equal sequence number/retransmission ID entries. This
2072 * means that during the last ACK delay timer or boost.asio handler, while accumulating the
2073 * acknowledgments for this ACK packet, the receiver received the same packet twice (duplicate).
2074 * (This can happen due to network misbehavior; and due to ACK loss and other conditions when
2075 * retransmits are enabled.) Call 2 such packets P1 and P2, where P1 was received first and thus
2076 * appears earlier in acked_packets. How do we handle this?
2077 *
2078 * Suppose instead of being in the same ACK, P1 and P2 were in different ACKs that arrived in that
2079 * order, P1 and P2 (something that certainly could happen depending on how the delayed ACK timer
2080 * works out). That situation is basically the same (except that if they're in one ACK there's
2081 * the added guarantee that we KNOW what P1 is acknowledging arrived to the receiver before
2082 * what P2 was acknowledging did, which is even more solid knowledge). Therefore, it makes sense
2083 * to simply handle each acknowledgment in the ACK in the order they're listed in acked_packets.
2084 * The 2nd, 3rd, etc. occurrence will thus be treated the same way as if it arrived in a later
2085 * ACK. */
2086
2087 /* Congestion control: First see introduction to this topic in class Congestion_control_strategy
2088 * doc header. Then resume here.
2089 *
2090 * Since information stored in ACKs is of paramount importance to how congestion control views the
2091 * pipe, congestion control is very relevant in this method: this method is the main (but not
2092 * only) source of events for m_snd_cong_ctl.
2093 *
2094 * These ACK-based events are of interest to m_snd_cong_ctl:
2095 *
2096 * - on_acks(N, M): N bytes in M packets have just been converted from In-flight to
2097 * Acknowledged. Note that these packets have NOT been previously Acknowledged or considered
2098 * Dropped (they are In-flight just before the ACK).
2099 * - This should also be immediately preceded with M on_individual_ack(N', T, CWND) events, where N'
2100 * is the # of bytes in the individual acked packet; and T is the RTT of the packet, and CWND is the
2101 * # of bytes in cwnd that was used when the acked data pkt was sent.
2102 * In the rest of the discussion I omit this event, as it can be thought of as part of
2103 * on_acks() for purposes of the discussion.
2104 * - on_loss_event(N', M'): N' bytes in M' packets have just been converted from In-flight to
2105 * Dropped.
2106 *
2107 * The basic idea is to determine which of these events are implied by the acks passed to this
2108 * method, inform m_snd_cong_ctl, and then check if the new m_snd_cong_ctl->congestion_window_bytes()
2109 * value (a/k/a CWND) -- if it has changed -- allows us to now send more bytes (if we have any).
2110 *
2111 * An important decision (and one sadly not very explicitly exposed [perhaps as an exercise to the
2112 * reader, or to avoid being too rigid] in the various TCP RFCs) is how to group these events and
2113 * in what order. In other words, do we call on_acks(N, 1) for each acknowledged packet? Do we
2114 * then check for drops and call on_loss_event(N', M') immediately, or wait to process all acked
2115 * packets first?
2116 *
2117 * The answer we choose is simple. First, scan all individual (i.e., for each sent packet) acks
2118 * given to us and update m_snd_flying_pkts_by_seq_num (the "scoreboard"). While doing so keep track of
2119 * the cumulative N and M. Having done that, we will also expose zero or more In-flight packets
2120 * as Dropped. (In this method, a given packet is exposed as Dropped if the total number of
2121 * acknowledged packets AFTER that packet exceeds a constant like 2. So basically if the acks we
2122 * process here make that counter exceed that limit for a given packet P, P is Dropped and removed
2123 * from m_snd_flying_pkts_by_seq_num.) So after the ack scanning phase, tally up all packets now
2124 * considered Dropped, which gives us N' and M'.
2125 *
2126 * Finally, call on_loss_event(N', M') (assuming N' and M' are not zero). And then call
2127 * on_acks(M, N) (assuming N and M are not zero).
2128 *
2129 * Let's justify this. First, assume it's correct to tally these things up and then just
2130 * call each method once. Is the "report loss, report acks" order right? Yes. Intuitively,
2131 * m_snd_cong_ctl wants to know about events in the chronological order they occur. While the Drop(s)
2132 * are detected at the same time as the Ack(s), the actual packet dropping INFERRED from the
2133 * Ack(s) occurred in the past; we're only deducing it now. The received Acks are in fact for
2134 * packets AFTER the now-Dropped packets. Hence this is the right order.
2135 *
2136 * Now the only remaining thing is to justify combining the ack and drop events in one (each). For
2137 * acknowledgments, it's straightforward: so far, most Congestion_control_strategy modules
2138 * don't need to know about each individual ack, so for simplicity/efficiency we can just combine
2139 * them. (However, some algorithms do need it; e.g., FAST would need it; still, many don't.
2140 * Other modules, like Send_bandwidth_estimator, may also care about individual acks.)
2141 *
2142 * What about the drop events? Why combine all the drops into one? Should we include all the
2143 * drops into the one? To answer, I use as a reference DCCP CCID 2 RFC 4341 (standards track)
2144 * which describes a protocol similar to ours and implies the following model. Basically, over
2145 * time, the pipe experiences a series of 0 or more congestion events (more accurately loss
2146 * events). Two loss events cannot overlap in this implied model. Thus any given Dropped packet
2147 * belongs to exactly one loss event. Here is how the RFC (section 5) more or less formally
2148 * defines whether 2 packets belong to one event: "As in TCP, two losses [...] are considered part
2149 * of a single congestion event when the second packet was sent before the loss [...] of the first
2150 * packet was detected." Presumably the text also assumes that the "second" packet was
2151 * found to be dropped either at the same or later time as the "first" packet was found to be
2152 * dropped (otherwise the text makes no sense, as the very earliest Dropped packet would be in the
2153 * same congestion event as the very last Dropped packed in a very long session). Let's build an
2154 * algorithm inductively based on this definition.
2155 *
2156 * At first there are no loss events. We get a group of acks which render another group of
2157 * packets P1, P2, ... (in order of increasing sequence number) Dropped. Certainly P1 is in a
2158 * loss event; call it L1. P2 was found to be dropped at the same or later time as P1; and it was
2159 * obviously sent before L1 was detected (which was NOW; call it T1). So P2 is in loss event L1.
2160 * Similarly, so is P3, P4, .... Now let's say some time passes and we get more acks and thus
2161 * dropped packets P7, P8, P9, .... Suppose P7 was sent before T1 (but found Dropped at T2 > T1),
2162 * which is quite possible (e.g., T2 could be just after T1). Then by the above definition P7 is
2163 * in loss event L1 (no new loss event). P8 could be in the same situation. In fact, all Dropped
2164 * packets from this ack group may be in L1. Suppose, conversely, that P9 was sent AFTER T1. By
2165 * the above definition, it is part of a new loss event L2, detected at T2. Now P10, is certainly
2166 * in L2 as well, since it was sent before T2, obviously. Thus we can, for each Dropped packet P,
2167 * determine whether it's part of the preceding loss event or part of a new one.
2168 *
2169 * Intuitively, it makes sense as well. If, say, we got 5 dropped packets at the same time, and
2170 * informed Congestion_control_classic (Reno) with 5 calls to on_loss_event(), then CWND would get
2171 * halved 5 times! Intuitively that's not right (and way too conservative). More likely the 5
2172 * packets belong to the same congestion or loss event, so CWND should only be halved once. Then
2173 * the only question is how to group packets into separate loss events. The above algorithm,
2174 * roughly speaking, considers two packets as part of the same loss event if they're within an RTT
2175 * of each other (indeed RFC 4341 says one can use the SRTT to approximate the above algorithm,
2176 * although we choose to use the exact definition instead).
2177 *
2178 * Therefore the final algorithm is justified and is as follows:
2179 *
2180 * 0. Before the current method is ever called, set time stamp m_snd_last_loss_event_when =
2181 * -infinity.
2182 * 1. Scan all acknowledgments, updating m_snd_flying_pkts* and m_snd_flying_bytes.
2183 * Keep track of total acknowledgment stats (bytes and packets). (Inform side modules like
2184 * Send_bandwidth_estimator with any required individual ack info like RTTs.)
2185 * Ignore acks of packets not in m_snd_flying_pkts* (not In-flight).
2186 * 2. Tally up which packets are exposed as Dropped by the above m_snd_flying_pkts* updates.
2187 * Keep track of total loss stats (bytes and packets). However, when doing the latter ignore
2188 * any packet P for which P.m_sent_when < m_snd_last_loss_event_when.
2189 * 3. If at least 1 packet exposed as Dropped in step 2, call
2190 * m_snd_cong_ctl->on_loss_event(...stats...); and set m_snd_last_loss_event_when to the current time,
2191 * marking this the start of a new loss event.
2192 * 4. If at least 1 packet exposed as Acknowledged in step 1, call
2193 * m_snd_cong_ctl->on_acks(...stats...). */
2194
2195 // Set up work state and save certain "before" values.
2196
2197 // For RTT at least. Use steady, high-res clock. Use one coherent value for entire method to simulate simultaneity.
2198 const Fine_time_pt time_now = Fine_clock::now();
2199
2200 // For brevity and a little speed:
2201 const bool rexmit_on = sock->rexmit_on();
2202 auto& snd_stats = sock->m_snd_stats;
2203 auto& snd_flying_pkts_by_when = sock->m_snd_flying_pkts_by_sent_when;
2204 /* These guys are only stored in Peer_socket (instead of creating locally here) for a bit of performance. Reuse now.
2205 * Note that clear() is a very fast operation; it will essentially just set the internal element count to 0. */
2206 auto& pkts_marked_to_drop = sock->m_snd_temp_pkts_marked_to_drop;
2207 pkts_marked_to_drop.clear();
2208
2209 // To check, at the end, whether we've changed can_send() false => true.
2210 const bool could_send_before_acks = can_send(sock);
2211 // To check, at the end, whether we've changed snd_deqable() false => true.
2212 const bool had_rexmit_data_before_acks = !sock->m_snd_rexmit_q.empty();
2213
2214 /* Includes each order number (unique packet ID) for which the packet was acknowledged.
2215 * Used for Drop_timer events to register at the bottom; and also to feed the priority queue high_ack_count_q
2216 * (explained below in detail).
2217 *
2218 * @todo Consider using unordered_flat_set, generally though of as faster than unordered_set. */
2219 unordered_set<Peer_socket::order_num_t> flying_now_acked_pkts;
2220
2221 // These are the N, M arguments to on_acks() described just above in big comment.
2222 size_t clean_acked_bytes = 0;
2223 size_t clean_acked_packets = 0;
2224
2225 /* The are the individual T, N' (RTT, acked_bytes, sent_cwnd_bytes) arguments to pass to on_individual_ack() described
2226 * just above in big comment. We will be accumulating these across all the acks in the loop below. */
2227 using Clean_acked_packet = tuple<Fine_duration, size_t, size_t>;
2228 vector<Clean_acked_packet> clean_acked_packet_events;
2229 clean_acked_packet_events.reserve(min(acked_packets.size(), snd_flying_pkts_by_when.size())); // Small optimization.
2230
2231 /* Handle each acknowledgment in the order that the corresponding packet was received by other
2232 * side (earliest to latest) per above discussion. */
2233 for (const Ack_packet::Individual_ack::Const_ptr ack : acked_packets)
2234 {
2235 /* Use helper to classify this individual ack as one of the following:
2236 * - Malformed/illegal. => error_ack is true. Else:
2237 * - Legal but referring to an already-acknowledged packet, or arriving too late. => dupe_or_late is true.
2238 * - The packet being acknowledged is unknown. => flying_pkt_it == past_oldest() (a/k/a end()).
2239 * - The packet being acknowledged is known. => flying_pkt_it points to that acked packet.
2240 * - Legal and acking a not-yet-acked packet, arriving in time. => dupe_or_late is false.
2241 * => flying_pkt_it points to that acked packet.
2242 * Note: The helper takes care of snd_stats updating, closing socket on error, and relevant logging. */
2243
2245 bool dupe_or_late;
2246
2247 const bool error_ack = !categorize_individual_ack(socket_id, sock, ack, &dupe_or_late, &flying_pkt_it);
2248 if (error_ack)
2249 {
2250 return; // Fatal error for entire socket (malformed ack, etc.). Socket is closed; all logged; bail out now.
2251 }
2252 // else
2253
2254 // Note these may never be initialized.
2255 Fine_duration round_trip_time;
2257 const Peer_socket::Sent_packet::Sent_when* sent_when;
2258
2259 // Compute RTT, assuming we ID'ed the original DATA. (RTT logged even if we still throw away the ack just below.)
2260 if (flying_pkt_it != snd_flying_pkts_by_when.past_oldest())
2261 {
2262 // Use helper to compute RTT and, as a side effect, get `Sent_when* sent_when` set to point to appriate structure.
2263 flying_pkt = flying_pkt_it->second;
2264 round_trip_time = compute_rtt_on_ack(flying_pkt, time_now, ack, &sent_when); // It logs details.
2265 } // Done computing (if possible) RTT and logging it.
2266
2267 if (dupe_or_late)
2268 {
2269 continue; // Do NOT return! There may well be valid individual acks after it. All logged; get out now.
2270 }
2271
2272 // else it's an in-time acking of DATA packet that has not yet been acked (is considered In-flight)!
2273 assert(!dupe_or_late);
2274 // The following is guaranteed by helper above, since !dupe_or_late. Hence, also, flying_pkt, sent_when, RTT set.
2275 assert(flying_pkt_it != snd_flying_pkts_by_when.past_oldest());
2276 assert(flying_pkt);
2277
2278 // Update SRTT, etc.
2279 new_round_trip_time_sample(sock, round_trip_time);
2280
2281 /* Similarly, inform congestion control (see big comment at top of method). Some strategies
2282 * use individual acks to gauge the pipe's properties. Save the info to
2283 * later call on_individual_ack(). Why not just call
2284 * it here? Answer: Congestion_control_strategy interface specifies that
2285 * on_individual_ack() must be called AFTER on_loss_event() (which can only be called once
2286 * we've fully updated snd_flying_pkts, thus handled all acks). It also specifies that
2287 * snd_flying_pkts must be updated to reflect the handled ack. So we have no choice but
2288 * to save it. (@todo Performance?) */
2289 const size_t bytes_acked = flying_pkt->m_size;
2290 const size_t cwnd_bytes = sent_when->m_sent_cwnd_bytes;
2291 clean_acked_packet_events.emplace_back(round_trip_time, bytes_acked, cwnd_bytes);
2292
2293 // Maintain invariant. Packet acknowledged, so remove from In-flight packet list and related structures.
2294 snd_flying_pkts_erase_one(sock, flying_pkt_it);
2295
2296 // Bona fide In-flight->Acknowledged data; accumulate to inform congestion control below.
2297 clean_acked_bytes += bytes_acked;
2298 ++clean_acked_packets;
2299
2300 /* If we got here, then it is in fact what we consider a valid acknowledgment of packet
2301 * sent at time sent_when. Therefore, we should increment m_acks_after_me for any packet that has NOT
2302 * been acknowledged that was sent earlier than sent_when. (Later we'd consider Dropped any
2303 * packets for which this value is too high, as in TCP Fast Recovery/Retransmit.) Note that if
2304 * retransmission is off, that's the same as all packets with a lower first sequence number.
2305 * However if retransmission is on, then a packet may have a lower sequence number but be sent
2306 * later. Thus we use sent_when and not seq_num.
2307 *
2308 * Naively, we could just have a for () loop here to increment all such data members. However
2309 * that's inefficient -- order O(k * n), where k = acked_packets.size() and n =
2310 * snd_flying_pkts*.size(), in the worst case. Moreover, some of the Sent_packet structs in
2311 * which we increment m_acks_after_me may be acknowledged and thus erased from snd_flying_pkts*
2312 * in subsequent iterations of the for () loop we are in, wasting that work.
2313 *
2314 * So instead we count the individual acks in a hash map that maps sent_when to the number of
2315 * times (in this ACK) that sequence number's packet was validly acknowledged. This is O(k)
2316 * amortized total. Then elsewhere we use that hash map to more efficiently update m_acks_after_me
2317 * where appropriate. In addition, this hash map is used to register certain Drop_timer
2318 * at the end of the method. */
2319
2320 /* Note that we track these by "order number"; each sent packet (no matter if retransmitted or
2321 * not) gets a unique order number, higher than all previous. Since no two packets will have
2322 * the same order number, we keep a set of order numbers. */
2323 flying_now_acked_pkts.insert(sent_when->m_order_num);
2324 } // for (all acked_packets)
2325
2326 /* snd_flying_pkts* is updated w/r/t removing the In-flight-now-acked packets. Now, realize that
2327 * for a given packet P still In-flight, if packets sent BEFORE it have just become acked, intuitively
2328 * it raises the probability P has been lost and should be considered Dropped. In fact, as explained in
2329 * helper categorize_pkts_as_dropped_on_acks(), if one finds the latest-sent such packet P, then all
2330 * packets sent before it should all be dropped as well. So, let's find this latest-sent P: */
2331 const Peer_socket::Sent_pkt_ordered_by_when_iter last_dropped_pkt_it
2332 = categorize_pkts_as_dropped_on_acks(sock, flying_now_acked_pkts);
2333
2334 /* OK, so P and all In-flight packets sent before it must be dropped. This helper takes all the actions
2335 * necessary (or at least records data we use to take such actions below) w/r/t all those packets.
2336 * Namely: erases them from snd_flying_pkts*; accumulates packet and bytes counts to do with these
2337 * dropped packets; saves the packet IDs from Drop timer purposes into pkts_marked_to_drop. */
2338 size_t dropped_pkts;
2339 size_t dropped_bytes;
2340 size_t cong_ctl_dropped_bytes;
2341 size_t cong_ctl_dropped_pkts;
2342 if (!drop_pkts_on_acks(sock, last_dropped_pkt_it,
2343 &cong_ctl_dropped_pkts, &cong_ctl_dropped_bytes,
2344 &dropped_pkts, &dropped_bytes, &pkts_marked_to_drop))
2345 {
2346 return; // Already closed/logged/etc. (too many retransmissions probably).
2347 }
2348
2349 // As long promised since the top of this method, let congestion control (and B/W estimator) know what happened!
2350
2351 /* Bandwidth estimation: It can be useful to estimate the available outgoing bandwidth (available
2352 * meaning the total bandwidth of the empty pipe minus any other traffic other than this
2353 * connection [NetFlow or otherwise] currently occupying this pipe). Mostly it's useful for certain
2354 * congestion control strategies like Congestion_control_classic_with_bandwidth_est, but it may be
2355 * good information to have if only for the user's general information. Therefore we keep an
2356 * independent m_snd_bandwidth_estimator regardless of the congestion control strategy in use.
2357 * Like Congestion_control_strategy, it updates its state based on events. It currently cares
2358 * about at least one event: on_acks(N), where N is the number of bytes acknowledged. This is
2359 * very similar to the on_acks(N, M) event for congestion control (see above). None of the other
2360 * aspects of the above discussion (such as loss events) apply to m_snd_bandwidth_estimator. */
2361
2362 // Note that the order is as required by Congestion_control_strategy() API: loss, individual acks, consolidated acks.
2363
2364 // Report loss event info to congestion control.
2365 if (dropped_pkts != 0)
2366 {
2367 // @todo Might be too verbose to keep it as INFO!
2368 FLOW_LOG_INFO("NetFlow worker thread working on [" << sock << "]. "
2369 "Considering Dropped: [" << dropped_bytes << "] bytes = [" << dropped_pkts << "] packets.");
2370
2371 if (cong_ctl_dropped_pkts != 0) // Again, cong_ctl_dropped_pkts != dropped_pkts, potentially.
2372 {
2373 // New loss event!
2374 assert(cong_ctl_dropped_bytes != 0); // Empty blocks not allowed (should have been eliminated by now).
2375
2376 FLOW_LOG_INFO("cong_ctl [" << sock << "] update: loss event: "
2377 "Dropped [" << cong_ctl_dropped_bytes << "] bytes "
2378 "= [" << cong_ctl_dropped_pkts << "] packets.");
2379
2380 sock->m_snd_cong_ctl->on_loss_event(cong_ctl_dropped_bytes, cong_ctl_dropped_pkts);
2381 sock->m_snd_last_loss_event_when = Fine_clock::now();
2382
2383 // As a silver lining, we probably got some nice new acknowledgments following that drop.
2384 }
2385 }
2386 else
2387 {
2388 assert(dropped_pkts == 0);
2389 assert(cong_ctl_dropped_pkts == 0);
2390 }
2391
2392 if (clean_acked_packets != 0)
2393 {
2394 assert(clean_acked_bytes != 0); // Empty blocks not allowed (should have been eliminated by now).
2395 assert(!clean_acked_packet_events.empty());
2396
2397 // Report individual (clean) acks to congestion control.
2398 for (const auto& [rtt, bytes, cwnd_bytes] : clean_acked_packet_events)
2399 {
2400 FLOW_LOG_TRACE("cong_ctl [" << sock << "] update: clean individual acknowledgment: "
2401 "[" << sock->bytes_blocks_str(bytes) << "] with RTT [" << round<milliseconds>(rtt) <<
2402 "] and sent_cwnd_bytes [" << cwnd_bytes << "].");
2403
2404 sock->m_snd_cong_ctl->on_individual_ack(rtt, bytes, cwnd_bytes);
2405 }
2406
2407 FLOW_LOG_TRACE("cong_ctl/bw_est [" << sock << "] update: clean acknowledgments: "
2408 "[" << sock->bytes_blocks_str(clean_acked_bytes) << "] = "
2409 "[" << clean_acked_packets << "] packets.");
2410
2411 // Report the totality of (clean) acks to congestion control and bandwidth estimator.
2412 sock->m_snd_bandwidth_estimator->on_acks(clean_acked_bytes);
2413 sock->m_snd_cong_ctl->on_acks(clean_acked_bytes, clean_acked_packets);
2414 }
2415
2416 /* For debugging it can be useful to log socket state right after loss and handling everything.
2417 * Do so but only if the last time we so logged was some time ago; this is a CPU-intensive
2418 * operation.
2419 *
2420 * Also, register dropped data in snd_stats. */
2421 if (dropped_pkts != 0)
2422 {
2423 // Register that we have convered N bytes over M packets from In-flight to Acknowledged.
2424 snd_stats.dropped_data(dropped_bytes, dropped_pkts);
2425
2426 const seconds MIN_TIME_BETWEEN_LOGS(1);
2427 const Fine_duration since_last_loss_sock_log = Fine_clock::now() - m_last_loss_sock_log_when;
2428
2429 if (since_last_loss_sock_log > MIN_TIME_BETWEEN_LOGS)
2430 {
2431 FLOW_LOG_INFO("Will log socket state on loss, because last such loss-driven logging was "
2432 "[" << round<milliseconds>(since_last_loss_sock_log) << " >"
2433 " " << MIN_TIME_BETWEEN_LOGS << "] ago.");
2434 sock_log_detail(sock);
2435 m_last_loss_sock_log_when = Fine_clock::now();
2436 }
2437 else
2438 {
2439 FLOW_LOG_INFO("Will NOT log socket state on loss, because last such loss-driven logging was "
2440 "[" << round<milliseconds>(since_last_loss_sock_log) << " <="
2441 " " << MIN_TIME_BETWEEN_LOGS << "] ago.");
2442 }
2443 }
2444
2445 // Log the send window state after the above changes (if at least TRACE enabled).
2446 log_snd_window(sock);
2447
2448 /* Handle possible effect of above activities on the Drop Timer. (It may get disabled or restarted anew.)
2449 * Why not just do this right when we erase the associated packets from snd_flying_pkts*? Answer: We don't want to
2450 * trigger disruptive behavior like possibly retransmitting everything in the middle of all that accounting
2451 * which is not yet complete. Now it's complete, so it's the right time to handle this.
2452 *
2453 * Recall that snd_flying_pkts* have been updated and no longer contain the associated packets' info. */
2454
2455 const Drop_timer::Ptr drop_timer = sock->m_snd_drop_timer;
2456 drop_timer->start_contemporaneous_events();
2457
2458 for (const auto pkt_order_num : flying_now_acked_pkts)
2459 {
2460 drop_timer->on_ack(pkt_order_num);
2461 drop_timer->on_packet_no_longer_in_flight(pkt_order_num);
2462 }
2463 for (const auto pkt_order_num : pkts_marked_to_drop)
2464 {
2465 drop_timer->on_packet_no_longer_in_flight(pkt_order_num);
2466 }
2467
2468 drop_timer->end_contemporaneous_events();
2469
2470 /* As avertised, handle the rcv_wnd update: the latest ACK we are handling here contains the
2471 * latest info about the Receive buffer space on the other side that is available. */
2472 if (sock->m_snd_pending_rcv_wnd != sock->m_snd_remote_rcv_wnd)
2473 {
2474 FLOW_LOG_TRACE("Other side advertised "
2475 "rcv_wnd change [" << sock->m_snd_remote_rcv_wnd << "] => [" << sock->m_snd_pending_rcv_wnd << "].");
2476 sock->m_snd_remote_rcv_wnd = sock->m_snd_pending_rcv_wnd;
2477 /* Why have this intermediate m_snd_pending_rcv_wnd thing at all then? Answer: can_send(),
2478 * checked at the start of this method and saved into could_send_before_acks, uses the "before
2479 * handling the ACKs" state, which should not yet include the receive window update. Then
2480 * since we update m_snd_remote_rcv_wnd after that is computed, but before can_send() is
2481 * re-checked just below, we are able to see if the ACKs have changed can_send() from false to
2482 * true. */
2483
2484 /* Register whether after this window update, if we had a packet to send and no data In-flight,
2485 * we would be able to send at least one full DATA packet or not (i.e., can_send() would return
2486 * true). That is, register whether Receive window is ~0. */
2487 sock->m_snd_stats.updated_rcv_wnd(sock->m_snd_remote_rcv_wnd < sock->max_block_size());
2488 }
2489
2490 /* We've received ACKs and thus have quite likely reduced the number of bytes we
2491 * consider In-flight. Moreover we may have increased CWND. Moreover we may have added packets
2492 * to retransmit queue (if retransmission is on). Moreover we may have increased m_snd_remote_rcv_wnd.
2493 * Therefore can_send() may now return true while at the beginning of the method it returned
2494 * false; and similarly for snd_deqable(). So have send_worker() check and send more if possible.
2495 * See Node::send() for discussion of overall strategy on this topic. */
2496 if ((!could_send_before_acks) || (rexmit_on && (!had_rexmit_data_before_acks)))
2497 {
2498 send_worker(sock, true);
2499 /* ^-- defer_delta_check == true: because the only way to get to this method is from
2500 * async_low_lvl_recv(), which will perform event_set_all_check_delta(false) at the end of itself,
2501 * before the boost.asio handler exits. See Node::m_sock_events doc header for details. */
2502 }
2503} // Node::handle_accumulated_acks()
2504
2507 bool* dupe_or_late, Peer_socket::Sent_pkt_ordered_by_when_iter* acked_pkt_it)
2508{
2509 assert(dupe_or_late);
2510 assert(acked_pkt_it);
2511
2512 /* This helper of handle_accumulated_acks() exists to make the latter method briefer/readable, not for code reuse
2513 * as of this writing. It figures out whether the given individual ack is invalid, valid but duplicate/late, or
2514 * valid and on-time. Results go into the return value and *dupe_or_late and *acked_pkt_it. */
2515
2516 /* Now to discuss what happens when an ACK is received, with a seemingly valid sequence number
2517 * (i.e., in [m_snd_init_seq_num + 1, m_snd_next_seq_num - 1] range) -- but the corresponding
2518 * packet is not in m_snd_flying_pkts_by_seq_num. What does this mean? One, unlikely, possibility is
2519 * that it's a fake/wrong acknowledgment, not pertaining to any packet we'd sent but in the range
2520 * of sequence numbers we did send (in other words, the sequence number is in the right range but
2521 * doesn't correspond to a first sequence number of a packet we'd really sent). Unfortunately we
2522 * have no way to detect that fully, since it's not in m_snd_flying_pkts_by_seq_num, and that's basically the only
2523 * place we store packet boundaries of sent packets. Suppose we eliminate that possibility.
2524 *
2525 * Then the only remaining possibility is that this acknowledgment is a duplicate of a previous
2526 * one, which had caused us to remove that packet from m_snd_flying_pkts_by_seq_num. So, how DO we handle
2527 * a duplicate acknowledgment? We already know they got packet, as we've already measured RTT
2528 * from the previous copy of this ack, so there's nothing useful for us. Conclusion: ignore
2529 * duplicate acknowledgments.
2530 *
2531 * Note that the above discussion pertains to a dupe ack where both the sequence number and the
2532 * retransmission ID are the same as a previous one. If the retransmission ID is different (only
2533 * legal when retransmission is enabled), that's a different situation -- the acknowledgment is
2534 * not duplicate but rather acknowledging a different send attempt for the same-numbered packet.
2535 * That is less of a corner case and is handled below explicitly.
2536 *
2537 * Sent, unacknowledged packets are eventually considered Dropped. In terms of our data structures
2538 * they are handled just like acknowledged ones. Therefore, an acknowledgment of such a Dropped
2539 * packet may arrive. This is a "late" acknowledgment. It is treated just like a duplicate
2540 * acknowledgment (in fact, there is no way to tell them apart). (Note that a packet is still
2541 * considered Dropped even if retransmission is on -- it's just that in that case it's also queued
2542 * on the retransmission queue to be re-sent when possible.)
2543 *
2544 * Another caveat is that two acknowledgments that are duplicates of each other can get
2545 * mis-ordered and thus arrive in opposite order. Thus the one with the longer one-way time would
2546 * yield the higher RTT, while the shorter one would get ignored. However, RTT measurement is an
2547 * art, not a science, so this is acceptable.
2548 *
2549 * @todo Acknowledgments themselves could actually be identified with something other other
2550 * than sequence numbers and retransmission IDs; e.g., with reflected sender time stamps. Then
2551 * one could do fancier stuff... but let's not overdo it for now. */
2552
2553 // For brevity and a little speed:
2554 const bool rexmit_on = sock->rexmit_on();
2555 auto& snd_flying_pkts_by_when = sock->m_snd_flying_pkts_by_sent_when;
2556 auto& snd_flying_pkts_by_seq = sock->m_snd_flying_pkts_by_seq_num;
2557 auto& snd_stats = sock->m_snd_stats;
2558
2559 // First sequence number in acknowledged packet.
2560 const Sequence_number& seq_num = ack->m_seq_num;
2561 // Retransmission ID (0 = first attempt, 1 = 1st retransmission, 2 = 2nd, ...).
2562 const unsigned int rexmit_id = ack->m_rexmit_id;
2563 assert(rexmit_on || (rexmit_id == 0)); // Should be guaranteed by deserialization.
2564
2565 // Register one individual acknowledgment of unknown # of bytes of data (may or may not be acceptable).
2566 snd_stats.received_ack();
2567
2568 /* Ensure it's within the range of sequence numbers we've already sent.
2569 * Note that this doesn't really guarantee its validity. It could be in that range but still
2570 * not correspond to any packet we'd actually sent. We try to detect that below. */
2571
2572 if (!util::in_open_open_range(sock->m_snd_init_seq_num, seq_num, sock->m_snd_next_seq_num))
2573 {
2574 /* Either the other side is an a-hole, or somehow a socket_id was reused from a recent
2575 * connection, which we do try to avoid like the plague. Therefore, send them an RST and
2576 * abort connection. If they send more data packets to this port (which is quite possible;
2577 * many could already be on the way), they'll get more RSTs still. */
2578
2579 // Interesting/rare enough to log a WARNING.
2580 FLOW_LOG_WARNING("NetFlow worker thread working on [" << sock << "]. "
2581 "Received [ACK]; "
2582 "acknowledgment [" << seq_num << ", ...) is outside (ISN, snd_next) "
2583 "range (" << sock->m_snd_init_seq_num << ", " << sock->m_snd_next_seq_num << ").");
2584
2585 // Register one individual acknowledgment of unknown # of bytes of data (not acceptable due to error).
2586 snd_stats.error_ack();
2587
2588 /* Close connection in our structures (inform user if necessary as well). Pre-conditions
2589 * assumed by call: sock in m_socks and sock->state() == S_OPEN (yes, since m_int_state ==
2590 * S_ESTABLISHED); 3rd arg contains the reason for the close (yes). This will empty the Send
2591 * and Receive buffers. That is OK, because this is the abrupt type of close (error). */
2592 rst_and_close_connection_immediately(socket_id, sock,
2594 /* ^-- defer_delta_check == true: because the only way to get to this method is from
2595 * async_low_lvl_recv(), which will perform event_set_all_check_delta(false) at the end of itself,
2596 * before the boost.asio handler exits. See Node::m_sock_events doc header for details. */
2597 return false; // Other out-params are meaningless.
2598 }
2599 // else within sane range.
2600
2601 // Check if the sequence number matches that of one of the packets we've sent and want acnowledged.
2602 *acked_pkt_it = snd_flying_pkts_by_when.find(seq_num);
2603 if (*acked_pkt_it == snd_flying_pkts_by_when.past_oldest()) // A/k/a end().
2604 {
2605 /* No such packet. Assuming no foul play/dumbassery, it's probably a duplicate acknowledgment
2606 * (i.e., we've already sent and got the ack, removing that packet from snd_flying_pkts*)
2607 * or a late acknowledgment (i.e., we've already sent and eventually considered Dropped the
2608 * the packet, removing it from snd_flying_pkts*).
2609 *
2610 * There is a corner case if retransmission is on. Suppose we sent packet P, consider it
2611 * Dropped (removing it from snd_flying_pkts*), and thus we place it on retransmission
2612 * queue. Suppose there is not enough CWND space to send it right away, so while it's pending
2613 * on that queue, we now get a late ack for it. Ideally in this case we'd remember it was in
2614 * retransmission queue, remove it from there, and basically act as if we hadn't removed it
2615 * from snd_flying_pkts* and got the ack for it. Instead we're just going to ignore this
2616 * information and needlessly retransmit. So why do this? Answer: It is troublesome to
2617 * design and code this. The part where we wouldn't retransmit it is fairly straightforward
2618 * and is a nice @todo. However acting as if it was normally ACKed after all is complex; for
2619 * instance, since we thought it was Dropped, we already informed m_cong_ctl of the loss event
2620 * -- how can we undo that in a clean way? It does not seem worth it. Again, checking
2621 * and updating the retransmission queue, though, is a nice @todo (but would ideally need fast
2622 * lookup into that linked list so not totally trivial).
2623 *
2624 * So, let's say that the concession described in the previous paragraph is OK.
2625 *
2626 * Could also be invalid. We only know seq_num (one boundary of packet), so how do we detect
2627 * it's invalid? One case where we know it's invalid is if this left boundary happens to be
2628 * straddled by a sequence number range in an element of snd_flying_pkts_by_seq. That would mean
2629 * that the same sequence number is in two different packets, which is in no way legal.
2630 * Example: we sent [5, 10), then received ACK with [7, ...). 7 is inside [5, 10) and is
2631 * thus illegal. */
2632
2633 /* Here's the technique we use. snd_flying_pkts_by_seq.upper_bound(S) gets the first packet
2634 * [U1, U2) such that U1 > S. Let prev(P) denote the packet preceding P in
2635 * snd_flying_pkts_by_seq; let prev([U1, U2)) = [L1, L2). Note that [U1, U2) may not exist
2636 * -- i.e., nothing after S is in the map. If so, [U1, U2) == snd_flying_pkts_by_seq.end(). Even
2637 * in that case [L1, L2) = prev([U1, U2)) MAY still exist; it is the last element of
2638 * snd_flying_pkts_by_seq in that situation.
2639 *
2640 * Given that, here are all the situations that mean P is straddled by a packet:
2641 *
2642 * - S inside [U1, U2) or any packet after it.
2643 * - Impossible. U1 > S by definition; so S is not inside any packet at U1 or later.
2644 * - S inside [L1, L2).
2645 * - Possible. We know S > L1, since otherwise S <= L1, which means we can't be inside this
2646 * if (and we are), or snd_flying_pkts_by_seq.upper_bound(S) == [L1, L2) (not true, since
2647 * snd_flying_pkts_by_seq.upper_bound(S) == [U1, U2), which != [L1, L2)). So, since S > L1,
2648 * we must check for S < L2. If true, S is straddled.
2649 * - S inside some packet [K1, K2) before [L1, L2).
2650 * - Impossible. Suppose S is inside [K1, K2) immediately preceding [L1, L2). Then
2651 * snd_flying_pkts_by_seq.upper_bound(S) == [L1, L2). But we already know
2652 * snd_flying_pkts_by_seq.upper_bound(S) == [U1, U2) (which != [L1, L2)). So that's
2653 * impossible. Repeat this logic for all packets [K1, K2) preceding [L1, L2) to show that
2654 * it can't be straddled by any of those either.
2655 *
2656 * Therefore, S is straddled by a packet if and only if:
2657 * - prev(snd_flying_pkts_by_seq.upper_bound(S)) exists; call it [L1, L2); and
2658 * - S < L2.
2659 *
2660 * This can be further restated as:
2661 * - snd_flying_pkts_by_seq.upper_bound(S) != snd_flying_pkts_by_seq.begin(); and
2662 * - (letting [L1, L2) = prev(snd_flying_pkts_by_seq.upper_bound(S)))
2663 * S < L2.
2664 *
2665 * So check for that. */
2666
2667 // Find U.
2668 Peer_socket::Sent_pkt_ordered_by_seq_const_iter pkt_it = snd_flying_pkts_by_seq.upper_bound(seq_num);
2669 // Check that prev(U) exists.
2670 if (pkt_it != snd_flying_pkts_by_seq.begin())
2671 {
2672 // prev(U) = L exists. Compute L.
2673 --pkt_it;
2674 // Compute [L1, L2), and check for straddling: S < L2. pkt_it->second points into snd_flying_pkts_by_when.
2675 Sequence_number l1, l2;
2676 get_seq_num_range(pkt_it->second, &l1, &l2);
2677
2678 assert(l1 < seq_num); // Sanity-check of upper_bound().
2679 if (seq_num < l2)
2680 {
2681 // Straddles. Other side is sending us bad stuff. As above, warn and RST/close.
2682
2683 // Register one individual acknowledgment of unknown # of bytes of data (not acceptable due to error).
2684 snd_stats.error_ack();
2685
2686 FLOW_LOG_WARNING("NetFlow worker thread working on [" << sock << "]. "
2687 "Received [ACK]; "
2688 "acknowledgment [" << seq_num << ", ...) is at least partially inside "
2689 "packet [" << l1 << ", " << l2 << ").");
2690 rst_and_close_connection_immediately(socket_id, sock, error::Code::S_SEQ_NUM_ARITHMETIC_FAILURE, true);
2691 /* ^-- defer_delta_check == true: because the only way to get to this method is from
2692 * async_low_lvl_recv(), which will perform event_set_all_check_delta(false) at the end of itself,
2693 * before the boost.asio handler exits. See Node::m_sock_events doc header for details. */
2694 return false; // Other out-params are meaningless.
2695 }
2696 // else if (seq_num >= l2) { It's past [L1, L2); does not straddle. }
2697 }
2698 // else { Legit because there is no packet L that could possibly straddle seq_num. }
2699
2700 /* OK, so NOW do we know it's a duplicate/late acknowledgment? Well, no. Suppose we sent packet
2701 * [5, 10) and get ACK with [5, ...). That's fine. So we erase [5, 10) from
2702 * snd_flying_pkts_by_seq. Now say we get ACK with [7, ...). Well, that's in the
2703 * [m_snd_next_seq_num, m_snd_next_seq_num) range certainly; and it doesn't get straddled by
2704 * any member of snd_flying_pkts_by_seq. Yet it's certainly invalid: we never sent (and could've
2705 * never sent) [7, ...). We can't know that, however, since [5, 10) is gone from
2706 * snd_flying_pkts_by_seq. Is this OK? More or less, yes. What do we do with a duplicate/late
2707 * acknowledgment just below? We log and ignore it. That doesn't seem harmful. NORMALLY
2708 * when something is invalid we'd RST and close connection, but here we can't know we should
2709 * do that; however ignoring it still seems fine and better than doggedly inventing data
2710 * structures to detect this corner case.
2711 *
2712 * What about m_snd_cong_ctl? Should we report this in m_snd_cong_ctl->on_acks()?
2713 * No. on_acks() specifically documents that it wants info on
2714 * In-flight->Acknowledged acknowledgments, not duplicates. (Briefly,
2715 * that's because it's measuring sent data in the pipe; acknowledgment duplication has unclear
2716 * implications about what it's acknowledging; it is unlikely that it represents more pipe
2717 * being available than if only one acknolwedgment had been received. In any case this should
2718 * hopefully be pretty rare and thus not too significant either way.)
2719 *
2720 * Same reasoning for not counting it in m_snd_bandwidth_estimator->on_acks(). */
2721
2722 // Per above discussion, ignore duplicate (or maybe invalid, but we can't know/assume that) acknowledgment.
2723
2724 // Register one individual acknowledgment of unknown # of bytes of data (late, dupe, or maybe invalid).
2725 snd_stats.late_or_dupe_ack();
2726
2727 FLOW_LOG_INFO("NetFlow worker thread working on [" << sock << "]. "
2728 "Acknowledged packet [" << seq_num << ", ...) is duplicate or late (or invalid). "
2729 "RTT unknown. Ignoring.");
2730
2731 // Ensure out-params indicating a dupe/late ack such that the packet being acked is not known.
2732 *dupe_or_late = true;
2733 assert(*acked_pkt_it == snd_flying_pkts_by_when.past_oldest()); // A/k/a end().
2734 return true;
2735 } // if (seq_num is not in snd_flying_pkts*) // i.e., duplicate/late acknowledgment with unknown acked packet.
2736 // else if (seq_num IS in snd_flying_pkts*): *acked_pkt_it points to snd_flying_pkts_by_when[seq_num].
2737 assert(*acked_pkt_it != snd_flying_pkts_by_when.past_oldest());
2738
2739 // It's an ack of sequence number we'd sent, but if retransmission is on it may not be of the one we LAST sent.
2740
2741 const Peer_socket::Sent_packet& acked_pkt = *((*acked_pkt_it)->second);
2742 const unsigned int acked_rexmit_id = rexmit_on ? acked_pkt.m_packet->m_rexmit_id : 0;
2743 Sequence_number seq_num_end; // Get sequence number just past last datum in packet.
2744 get_seq_num_range(*acked_pkt_it, nullptr, &seq_num_end);
2745
2746 // Note that both rexmit_id and acked_rexmit_id are guaranteed 0 at this point if !rexmit_on.
2747
2748 if (rexmit_id > acked_rexmit_id)
2749 {
2750 // This is entirely illegal. Can't acknowledge a packet copy we hadn't sent yet.
2751 FLOW_LOG_WARNING("NetFlow worker thread working on [" << sock << "]. "
2752 "Acknowledged packet [" << seq_num << ", " << seq_num_end << ") "
2753 "rexmit_id [" << int(rexmit_id) << "] "
2754 "exceeds highest sent rexmit_id [" << int(acked_rexmit_id) << "].");
2755 rst_and_close_connection_immediately(socket_id, sock, error::Code::S_SEQ_NUM_ARITHMETIC_FAILURE, true);
2756 /* ^-- defer_delta_check == true: because the only way to get to this method is from
2757 * async_low_lvl_recv(), which will perform event_set_all_check_delta(false) at the end of itself,
2758 * before the boost.asio handler exits. See Node::m_sock_events doc header for details. */
2759 return false; // Other out-params are meaningless.
2760 }
2761 // else if (rexmit_id <= acked_rexmit_id)
2762
2763 if (rexmit_id != acked_rexmit_id)
2764 {
2765 assert(rexmit_id < acked_rexmit_id);
2766
2767 /* This is legal: it's possible we had sent packet P, considered it Dropped, retransmitted it
2768 * (thus incrementing rexmit_id), and have now received a late acknowledgment of the
2769 * PREVIOUS attempt to send P (before retransmission). We could actually consider this
2770 * entirely equivalent to simply getting the last attempt acked. In fact I specifically kept
2771 * an array for m_sent_when, so that we can even compute accurate RTT. Yet, at least for now,
2772 * I am going to ignore such an acknowledgment. Reasons:
2773 *
2774 * - The RTT may be an outlier affected by some random event; we considered it Dropped, so
2775 * if those heuristics are generally sound, getting a late ack is suspicious.
2776 *
2777 * - Suppose I do take the RTT and report to congestion control, use for SRTT computation,
2778 * and remove from snd_flying_pkts*. I've in effect recorded a loss but then also
2779 * reported a successful retransmission, even though the ack is not for the retransmission
2780 * but more like a correction on the original loss. That's potentially fine, but chances
2781 * are I will soon receive the ack for the latest transmission, which is what I was really
2782 * expecting. That one will now be considered a late ack and will be ignored, even though
2783 * that RTT is actually probably more accurate, since chances are it arrived before the
2784 * retransmission would've been considered Dropped as well. So, basically, we're kind of
2785 * trying to use the "two wrongs make a right" philosophy, which seems messy.
2786 *
2787 * - Earlier in the method, I mentioned that if we detect P as dropped and queue it for
2788 * retransmission but get P acked *before* we get a chance to retransmit, then we consider
2789 * that ack as late and ignore it (and will still retransmit P). The reasons for that are
2790 * given in that comment. However, given that we made that decision, it would seem
2791 * strange to follow a different philosophy just because we did happen to get to
2792 * retransmit P. That would be inconsistent.
2793 *
2794 * - Keeping it in perspective, it should be fairly rare that a packet we considered Dropped
2795 * is acked after all. So it is perhaps not worth the trouble to go crazy about this
2796 * corner case.
2797 *
2798 * Nevertheless, a @todo would be to experimentally measure the effect of this policy and
2799 * decide whether it is sound. In that case also consider the aforementioned "P is acked
2800 * after queued for retransmission but before retransmitted" corner case. */
2801
2802 // Register one individual acknowledgment of unknown # of bytes of data (late).
2803 snd_stats.late_or_dupe_ack();
2804
2805 FLOW_LOG_INFO("NetFlow worker thread working on [" << sock << "]. "
2806 "Acknowledged packet [" << seq_num << ", " << seq_num_end << ") "
2807 "order_num [" << acked_pkt.m_sent_when[rexmit_id].m_order_num << "] "
2808 "rexmit_id [" << int(rexmit_id) << "] "
2809 "is less than highest sent [" << int(acked_rexmit_id) << "]. Ignoring.");
2810
2811 // Ensure out-params indicating a dupe/late ack of a specific known sent packet.
2812 *dupe_or_late = true;
2813 assert(*acked_pkt_it != snd_flying_pkts_by_when.past_oldest()); // A/k/a end().
2814 return true;
2815 }
2816 // else
2817 assert(rexmit_id == acked_rexmit_id);
2818
2819 // Do not log this mainstream case; only the exceptions above. RTT will probably be logged separately.
2820
2821 // Register one individual acknowledgment of N bytes of data (converts from In-flight to Acknowledged).
2822 snd_stats.good_ack(acked_pkt.m_size);
2823
2824 // Ensure out-params indicating an in-time, first ack of a specific known sent packet.
2825 *dupe_or_late = false;
2826 assert(*acked_pkt_it != snd_flying_pkts_by_when.past_oldest()); // A/k/a end().
2827 return true;
2828} // Node::categorize_individual_ack()
2829
2831 const Fine_time_pt& time_now,
2833 const Peer_socket::Sent_packet::Sent_when** sent_when) const
2834{
2835 using boost::chrono::milliseconds;
2836 using boost::chrono::round;
2837
2838 Fine_duration round_trip_time;
2839
2840 /* This helper of handle_accumulated_acks() exists to make the latter method briefer/readable, not for code reuse
2841 * as of this writing. It computes the RTT implied by the given individual ack and also returns the Sent_when
2842 * (which contains info on when the original packet was sent) structure as an out-param. */
2843
2844 /* RTT subtleties:
2845 *
2846 * How long did the other side, upon receiving the acked packet, wait before sending this
2847 * containing ACK with that individual acknowledgment? Why do we care? For RTT. Why do we
2848 * want RTT? To measure how long it takes for a sent packet to reach the receiver (one-way trip
2849 * time, or OWTT). Since mesuring OWTT is quite hard/impossible due to lack of absolute clock
2850 * synchronization between us and the receiver, RTT/2 is used as the next best way to get OWTT.
2851 * We can measure RTT by subtracting our recorded packet send time from the current time (ACK
2852 * receipt time). However, the ACK delay introduced by the receiver to reduce ACK overhead has
2853 * nothing to do with OWTT; it just (randomly, from the other side's point of view) inflates the RTT.
2854 * Thus we subtract the ACK delay from the RTT to get the actual RTT we use for congestion control, etc. */
2855
2856 const unsigned int rexmit_id = ack->m_rexmit_id;
2857 // Get the RTT for the transmission attempt that is actually being acknowledged (always 0 if retransmission off).
2858 *sent_when = &(flying_pkt->m_sent_when[rexmit_id]);
2859 const Peer_socket::order_num_t order_num = (*sent_when)->m_order_num;
2860
2861 /* ?second-resolution value (ack_delay) subtracted from max-resolution values. If ack_delay is
2862 * also in the max-resolution time unit, then there is no loss of precision. Otherwise we lose
2863 * precision by subtracting a number with fewer significant digits from one with more
2864 * significant digits. So Ack_delay_time_unit should ideally be Fine_duration, for precise RTT
2865 * values (especially for queueing delay-based congestion control algorithms); however that
2866 * decision is discussed elsewhere (Low_lvl_packet). */
2867 const auto& ack_delay = ack->m_delay;
2868 round_trip_time = time_now - (*sent_when)->m_sent_time - ack_delay;
2869
2870 if (round_trip_time.count() < 0)
2871 {
2872 /* Because this combines measurements on both sides, and each may have some error (plus or
2873 * minus a few hundred microseconds, possibly), and the result can be quite close to zero in
2874 * extremely low-latency situations, this may come out to be negative. So assume zero and
2875 * log a TRACE message at most.
2876 *
2877 * @todo Should we put also a ceiling on the RTT?
2878 * @todo For the floor, maybe it's better to use a higher guess than zero? */
2879 FLOW_LOG_TRACE("Acknowledged packet [" << ack->m_seq_num << ", ...) "
2880 "order_num [" << order_num << "] has negative "
2881 "RTT [" << round_trip_time << "]; assuming zero. "
2882 "Sent at [" << (*sent_when)->m_sent_time << "]; "
2883 "received at [" << time_now << "]; "
2884 "receiver-reported ACK delay [" << ack_delay << "].");
2885 round_trip_time = Fine_duration::zero();
2886 }
2887 FLOW_LOG_TRACE("Acknowledged packet [" << ack->m_seq_num << ", ...) "
2888 "order_num [" << order_num << "] "
2889 "has RTT [" << round<milliseconds>(round_trip_time) << "] "
2890 "(ACK delay [" << round<milliseconds>(ack_delay) << "]).");
2891
2892 return round_trip_time;
2893} // Node::compute_rtt_on_ack()
2894
2897 const boost::unordered_set<Peer_socket::order_num_t>& flying_now_acked_pkts)
2898{
2899 using std::priority_queue;
2900
2901 /* This helper of handle_accumulated_acks() exists to make the latter method briefer/readable, not for code reuse
2902 * as of this writing. The background is that once a set of individual acks has been processed in the sense that
2903 * sock->m_snd_flying_pkts* (which tracks In-flight outbound DATA packets) has been updated by removing the
2904 * acked packets (they are no longer In-flight), it's time to also recategorize certain further In-flight
2905 * packets as Dropped -- the intuition being that once N packets sent LATER than a given packet P have been
2906 * acked, it's highly probable that P has been Dropped by the network. This method determines the packets to drop
2907 * in that fashion.
2908 *
2909 * Now, as explained below, when ack set S causes packet set P' to be Dropped, this (possibly null) set P'
2910 * always has the following form: there is some particular packet P which is the most-recently-sent one
2911 * that is in P'; and therefore ALL other In-flight packets sent before P must be dropped too and also are in P'.
2912 * Thus P is necessary/sufficient to specify P'. Thus this method simply finds and returns a thing pointing to P. */
2913
2914 // For brevity and a little speed:
2915 auto& snd_flying_pkts_by_when = sock->m_snd_flying_pkts_by_sent_when;
2916
2917 /* OK, snd_flying_pkts* has been updated, in that we've removed any Sent_packet entries
2918 * corresponding to valid acknolwedgments in this ACK. As promised elsewhere we should also update
2919 * the remaining Sent_packets' m_acks_after_me entries and erase any Sent_packets that we consider
2920 * Dropped due to too high m_acks_after_me values. (As in TCP Fast Retransmit/Recovery, an
2921 * unacknowledged packet is considered Dropped based on the heuristic that a few packets with
2922 * higher sequence numbers have been acknowledged. Except since we have Sent_when, that should be
2923 * even better than using sequence number ordering as TCP would.)
2924 *
2925 * (Warning: below and nearby, I make pseudo-code-y leaps, such as saying flying_now_acked_pkts stores
2926 * Sent_whens when really it stores order_nums; just bear with me by trusting that it makes the logic
2927 * easier to explain, and that the actual structure in code is sufficiently
2928 * similar to the wording here to not make a salient difference in practice.)
2929 *
2930 * Consider the two structures we have now. snd_flying_pkts_by_when (call
2931 * it F) is a collection of Sent_packets, each with Sent_packet::m_acks_after_me, ordered by decreasing
2932 * Sent_when. flying_now_acked_pkts (call it C) is an unordered collection that contains each Sent_when
2933 * (i.e., reference to a send-packet attempt) that has been ACKed. That is, flying_now_acked_pkts tells us
2934 * by Sent_when which exact send attempts from the past are acknowledged in this set of accumulated acks.
2935 *
2936 * Even less formally -- just for sanity's sake -- F are In-flight packets; C are just-acked packets that were
2937 * very recently in F. C may be interleaved among F if viewed in increasing Sent_when order:
2938 * e.g., [ F F F F C F C F C C ] (where F represents a still-In-flight send attempt, or an F element;
2939 * C a just-acked send attempt, thus a C element; and the order is from earlier/lower Sent_when to
2940 * later/higher Sent_when).
2941 *
2942 * Note that, conceptually, the key sets (Sent_when values) in F and C are disjoint,
2943 * since each send attempt has a unique Sent_when value (because it at least consists of a unique m_order_num).
2944 * How do we correctly yet efficiently increment m_acks_after_me (call it A) for each
2945 * element in F to represent the new ackage? First observe that if F[t].A is incremented by N, then
2946 * F[prev(t)].A should be incremented by N PLUS the number of acks for all packets sent at times in range
2947 * (prev(t), t), where prev(t) is the element of C with the next lower (ealier) Sent_when.
2948 * Consider the example scoreboard above, [ F F F F C F# C F* C C ]. F*.A is incremented by 2, because
2949 * plainly there are two Cs after it. Therefore, the preceding F, which is F#,
2950 * is also incremented by 2; plus another 1, because there is another C (recall, simply another acknowledgment)
2951 * between F# and F*. And so it goes for all the Fs. Side/sanity note: The range is (prev[t], t), not
2952 * [prev(t), t), simply because F and C are disjoint; and prev(t) by definition is in F (hence not in C, hence
2953 * no ack for that seq. #).
2954 *
2955 * This suggests a simple inductive algorithm, wherein the latest F element's F[t].A is incremented by I, which
2956 * is the count of C elements with Sent_when > t; memorize I; now for each progressively older F[t],
2957 * count C elements in (t, next(t)) and increment F[T].A by I += <that count>. Repeat until all Fs incremented.
2958 * Ultimately I = # of new valid, acknowledgments. (Recall: scoreboard cannot begin with any Cs, [C C ... ], as
2959 * such a C would be acking a non-In-flight send attempt, so a dupe, and we specifically eliminate dupes from
2960 * consideration before inserting into C.) So that's O(F.size()) increment operations.
2961 *
2962 * OK, but how do we get this "count of acks between t and next(t)"? Let t be the last element of
2963 * F. For it, that count is the count of all keys > t in C (i.e., the total # of acks for all
2964 * packets sent after t). Let the lowest such key (Sent_when value) be `s`. Now let t' = prev(t) as before.
2965 * For t', the count of acks sent in (t', t) is the count of all elements in C with keys
2966 * in (s', s), where s' is again the lowest key > t'. Having counted that, set s = s, and repeat for each key t' of F.
2967 *
2968 * Of course, for that to be practical, C would need to be sorted by Sent_when. Since in reality it's not sorted,
2969 * we could first sort it in O(k log k) operations, worst-case, k = C.size(). More convenient, however, is to
2970 * construct a priority queue (heap) from C; then keep popping the Sent_whens down to and
2971 * including s at each step. That's O(k) to make the heap and O(k log k) total time spent
2972 * popping it.
2973 *
2974 * The above explanation strikes me as somewhat cryptic, but hopefully the code will clarify it; I was just
2975 * trying to explain why the code works. */
2976
2977 /* Make heap out of flying_now_acked_pkts; top()/pop() will return the element with the highest (latest) Sent_when.
2978 * Just store the Sent_when values directly in the heap; std::pair::operator<() will do
2979 * the right thing since no element's Sent_when equals another element's Sent_when (they were
2980 * stored in a uniquely-keyed dictionary in the first place).
2981 *
2982 * Let cur_sent_pkt be the element of snd_flying_pkts_by_sent_when we're currently
2983 * considering, and it starts at F.newest() and progresses accordingly through F.
2984 * Then, invariant: high_ack_count_q contains the acks for all send attempts P where
2985 * P.m_sent_when < cur_sent_pkt.m_sent_when. In particular, P.m_sent_when.top < cur_sent_pkt.m_sent_when. */
2986 priority_queue<Peer_socket::order_num_t>
2987 high_ack_count_q{flying_now_acked_pkts.begin(), flying_now_acked_pkts.end()};
2988
2989 // Invariant: this will be the m_acks_after_me increment applied to the just-considered packet in snd_flying_pkts*.
2990 using ack_count_t = Peer_socket::Sent_packet::ack_count_t;
2991 ack_count_t ack_increment_after_me = 0;
2992
2993 // As explained above, start with the first (latest send time) unacked packet and go forward (earlier and earlier).
2995 for (last_dropped_pkt_it = snd_flying_pkts_by_when.newest();
2996 last_dropped_pkt_it != snd_flying_pkts_by_when.past_oldest();
2997 ++last_dropped_pkt_it) // Up to k repetitions.
2998 {
2999 Peer_socket::Sent_packet& cur_sent_pkt = *(last_dropped_pkt_it->second);
3000 const Peer_socket::Sent_packet::Sent_when& cur_pkt_sent_when = cur_sent_pkt.m_sent_when.back();
3001
3002 /* We will increment cur_sent_pkt.m_acks_after_me by ack_increment_after_me + X, where X is
3003 * the total number of acks for packets with send times between cur_pkt_sent_when and the
3004 * cur_pkt_sent_when in the last loop iteration (or infinity if this is the first loop
3005 * iteration). The high_ack_count_q invariant we maintain is that high_ack_count_q holds the
3006 * ack counts for all packets with Sent_when values EXCEPT those >= the previous
3007 * iteration's cur_pkt_sent_when. Therefore, we need only find all elements of high_ack_count_q
3008 * whose Sent_whens are > our cur_pkt_sent_when. Since high_ack_count_q.top() is always the ack
3009 * count with the highest sent_when in that structure (priority queue), we just pop and sum
3010 * until high_ack_count_q.top() < cur_pkt_sent_when. */
3011
3012 // We've just assigned cur_sent_pkt, breaking invariant; pop until it holds again.
3013 while ((!high_ack_count_q.empty()) &&
3014 // Compare order numbers -- they are always unique.
3015 (high_ack_count_q.top() > cur_pkt_sent_when.m_order_num))
3016 {
3017 // Found acked packet with sent_when > cur_pkt_sent_when (but < previous iteration's cur_pkt_sent_when).
3018 ++ack_increment_after_me; // So add that packet's ack.
3019
3020 // And remove it, bringing the next highest entry to the top. O(log k).
3021 high_ack_count_q.pop(); // Note this maintains the invariant that defines high_ack_count_q.
3022 }
3023 // Note we've maintained the invariant defining ack_increment_after_me.
3024
3025 // Hence this many more acks for packets after us have occurred within this ack set.
3026 cur_sent_pkt.m_acks_after_me += ack_increment_after_me;
3027
3028 if (cur_sent_pkt.m_acks_after_me > S_MAX_LATER_ACKS_BEFORE_CONSIDERING_DROPPED)
3029 {
3030 /* Ah ha! For this packet we've exceeded the limit -- we will consider it Dropped. What
3031 * about the next (meaning, earlier-sent) unacknowledged packets? Observe that packets with
3032 * earlier send times MUST (if we were to continue the loop in this manner) end up with
3033 * equal or larger cur_sent_pkt.m_acks_after_me. (Intuitively: any acknowledgment after
3034 * packet P is also after any packet preceding P in the sent_when ordering.) Therefore, we
3035 * can break out of the loop and consider Dropped ALL packets from last_dropped_pkt_it to
3036 * snd_flying_pkts_by_when.past_oldest(). Yay! */
3037
3038 auto const logger_ptr = get_logger();
3039 if (logger_ptr && logger_ptr->should_log(log::Sev::S_TRACE, get_log_component()))
3040 {
3041 Sequence_number cur_pkt_seq_num, cur_pkt_seq_num_end;
3042 get_seq_num_range(last_dropped_pkt_it, &cur_pkt_seq_num, &cur_pkt_seq_num_end);
3043
3045 ("Unacknowledged packet [" << cur_pkt_seq_num << ", " << cur_pkt_seq_num_end << ") "
3046 "order_num [" << cur_pkt_sent_when.m_order_num << "] has "
3047 "had [" << cur_sent_pkt.m_acks_after_me << "] acknowledgments "
3048 "for later packets; considering it and "
3049 "all unacknowledged packets sent earlier as Dropped.");
3050 }
3051
3052 break;
3053 }
3054 // else
3055
3056 // ack_increment_after_me and high_ack_count_q invariants hold, so the next iteration can proceed.
3057 } // for (all elements in snd_flying_pkts_by_when, in decreasing m_sent_when order: newest -> oldest)
3058
3059 return last_dropped_pkt_it;
3060} // Node::categorize_pkts_as_dropped_on_acks()
3061
3063 const Peer_socket::Sent_pkt_ordered_by_when_iter& last_dropped_pkt_it,
3064 size_t* cong_ctl_dropped_pkts, size_t* cong_ctl_dropped_bytes,
3065 size_t* dropped_pkts, size_t* dropped_bytes,
3066 std::vector<Peer_socket::order_num_t>* pkts_marked_to_drop)
3067{
3068 // using boost::next; // Still ambiguous for some reason (in clang at least).
3069
3070 /* This helper of handle_accumulated_acks() exists to make the latter method briefer/readable, not for code reuse
3071 * as of this writing. The background is that once a set of individual acks has been processed in the sense that
3072 * sock->m_snd_flying_pkts* (which tracks In-flight outbound DATA packets) has been updated by removing the
3073 * acked packets (they are no longer In-flight), it's time to also recategorize certain further In-flight
3074 * packets as Dropped -- the intuition being that once N packets sent LATER than a given packet P have been
3075 * acked, it's highly probable that P has been Dropped by the network. This method does that (dropping
3076 * all such packets P) and certain related tasks such as tracking the associated loss event(s) for congestion
3077 * control.
3078 *
3079 * Now, as explained elsewhere, when ack set S causes packet set P' to be Dropped, this (possibly null) set P'
3080 * always has the following form: there is some particular packet P which is the most-recently-sent one
3081 * that is in P'; and therefore ALL other In-flight packets sent before P must be dropped too and also are in P'.
3082 * Thus P is necessary/sufficient to specify P'. last_droppped_pkt_it argument points to P' and is determined
3083 * elsewhere and used by this helper. */
3084
3085 // For brevity and a little speed:
3086 const bool rexmit_on = sock->rexmit_on();
3087 auto& snd_flying_pkts_by_when = sock->m_snd_flying_pkts_by_sent_when;
3088 auto& snd_stats = sock->m_snd_stats;
3089
3090 /* Pre-condition: all elements starting with (inclusive) last_dropped_pkt_it (within
3091 * snd_flying_pkts_by_when) should be considered Dropped. If last_dropped_pkt_it ==
3092 * snd_flying_pkts_by_when.past_oldest() a/k/a end(), then none should be considered
3093 * Dropped (i.e., no m_acks_after_me became high enough).
3094 *
3095 * Given that, we have a number of tasks remaining:
3096 *
3097 * 1. Count the total # of packets and bytes now considered Dropped and pass this to congestion control.
3098 * Omit those packets/bytes heuristically determined to belong to a loss event detected in an earlier
3099 * call, namely those for which m_sent_when < m_snd_last_loss_event_when.
3100 * 2. (If retransmission is enabled) Queue those Dropped packets for retransmission in retransmission queue.
3101 * 3. Erase the Dropped packets from snd_flying_packets*.
3102 *
3103 * For (non-asymptotic) performance, ideally we want to traverse snd_flying_pkts_by_when just once,
3104 * computing what's needed for these. Drilling down a bit:
3105 *
3106 * (2) and (3) are simple and involve walking over the Dropped range that has been computed (pre-condition above)
3107 * and adding-elsewhere or erasing those elements, respectively, though (2) must be done in chronological order
3108 * (increasing Sent_when).
3109 *
3110 * (1) is a matter of walking in anti-chronological (decreasing Sent_when) order over that same range, until
3111 * a certain Sent_when threshold is found, and stopping there.
3112 *
3113 * Thus, the kitchen-sink algorithm emerges: walk through Dropped range in decreasing Sent_when order, so
3114 * from last_dropped_pkt_it along snd_flying_pkts_by_when. Accumulate bytes/packets for (1), but stop
3115 * accumulating once m_snd_last_loss_event_when is reached w/r/t m_sent_when. Erase from snd_flying_pkts*
3116 * (carefully, since we are walking along one of them), for (3). And add to the retransmission queue, but in
3117 * reverse order versus the walking order, for (2). */
3118
3119 *dropped_pkts = snd_flying_pkts_by_when.size(); // We will just compute the final value by subtracting "after."
3120 *dropped_bytes = sock->m_snd_flying_bytes; // Ditto.
3121
3122 *cong_ctl_dropped_bytes = 0;
3123 *cong_ctl_dropped_pkts = 0;
3124 bool loss_event_finished = false;
3125
3126 /* We want to add to retransmission queue (if retransmission is on). We also want to traverse
3127 * snd_flying_pkts_by_when in forward newest->oldest order (for convenience and also to efficiently compute
3128 * cong_ctl_dropped_*). However we want to retransmit in reverse order (oldest->newest). So we
3129 * put the packets to retransmit in the latter order into snd_rexmit_q, at the end of the latter.
3130 * So, if it was [ABC], and we dropped [DEF], then we want to insert to yield [ABCFED] (ABC->ABCFED).
3131 * list<>::insert(it, v) will insert `v` before *it and return iterator to just-inserted element.
3132 * So we can memorize the latter and pass it in as `it` in the next insert(), rinse, repeat.
3133 * In the above example: ABC->ABC(D)->ABC(E)D->ABC(F)ED. // () is inserted element.
3134 * ^ ^ ^ // ^ is "fulcrum": insertion point for insertion following next ->.
3135 *
3136 * snd_rexmit_q_fulcrum_it, the insertion point, is so named due to being the "fulcrum" between the old and
3137 * new parts of snd_rexmit_q. History: Used to use a local new list<> here which would be spliced onto
3138 * the real queue at the end; but IMO this is more elegant (and probably a bit speedier). */
3139 auto& snd_rexmit_q = sock->m_snd_rexmit_q;
3140 decltype(sock->m_snd_rexmit_q)::iterator snd_rexmit_q_fulcrum_it = snd_rexmit_q.end();
3141
3142 // We are to fill this up, so it should not have anything yet.
3143 assert(pkts_marked_to_drop->empty());
3144
3145 auto pkt_it = last_dropped_pkt_it;
3146 while (pkt_it != snd_flying_pkts_by_when.past_oldest())
3147 {
3148 // We can't just ++pkt_it later on, because we are going to erase() at pkt_it soon, invalidating it.
3149 auto next_pkt_it = boost::next(pkt_it);
3150 // Now see end of loop body.
3151
3152 // Accumulate stuff for passing into congestion control at the end.
3153
3154 const Peer_socket::Sent_packet::Ptr sent_pkt = pkt_it->second;
3155 const Peer_socket::Sent_packet::Sent_when& sent_when = sent_pkt->m_sent_when.back();
3156
3157 if (!loss_event_finished)
3158 {
3159 if (// This is part of a new loss event if: There has been no loss event before this...
3160 (sock->m_snd_last_loss_event_when != Fine_time_pt{})
3161 // ...OR there has, but this packet was sent after that event was detected.
3162 && (sent_when.m_sent_time < sock->m_snd_last_loss_event_when))
3163 {
3164 /* This is the first packet encountered to be part of a previous loss event. If
3165 * retransmission is off, this will also cause the loop to exit. */
3166 loss_event_finished = true;
3167 }
3168 else
3169 {
3170 // Only got here if this packet and all Dropped packets after it are part of a new loss event.
3171 *cong_ctl_dropped_bytes += sent_pkt->m_size;
3172 ++(*cong_ctl_dropped_pkts);
3173 }
3174 }
3175 // else { Already found end of new loss event, if any, so no need to keep looking for it. }
3176
3177 // Add to retransmission queue if applicable.
3178
3179 if (rexmit_on)
3180 {
3181 if (!ok_to_rexmit_or_close(sock, pkt_it, true)) // Ensure not too many retransmissions already.
3182 /* ^-- defer_delta_check == true: because the only way to get to this method is from
3183 * async_low_lvl_recv(), which will perform event_set_all_check_delta(false) at the end of itself,
3184 * before the boost.asio handler exits. See Node::m_sock_events doc header for details. */
3185 {
3186 return false; // Already closed/logged/etc.
3187 }
3188 // else
3189
3190 /* Save a ref-counted pointer (to what includes packet data) in retransmission queue. We'll soon remove such
3191 * a pointer from snd_flying_pkts*, lowering the ref-count again. In other words, we are moving the sent-packet
3192 * object from snd_flying_pkts* to snd_rexmit_q (Dropped -> In-flight).
3193 *
3194 * Insert at the same position each time to ultimately arrange them in the reversed order that we want. */
3195 snd_rexmit_q_fulcrum_it = snd_rexmit_q.insert(snd_rexmit_q_fulcrum_it, sent_pkt);
3196 ++sock->m_snd_rexmit_q_size;
3197 }
3198
3199 /* Finally, we can erase it from snd_flying_pkts* and adjust snd_flying_bytes.
3200 * Will NOT invalidate other iterators into snd_flying_pkts_by_when.
3201 *
3202 * Also, save in pkts->pkts_marked_to_drop as advertised. */
3203
3204 static_assert
3206 "Scoreboard must not get otherwise changed when a packet is erased.");
3207 pkts_marked_to_drop->push_back(sent_when.m_order_num);
3208 snd_flying_pkts_erase_one(sock, pkt_it);
3209
3210 pkt_it = next_pkt_it;
3211 } // while (pkt_it != snd_flying_pkts_by_when.past_oldest())
3212
3213 // Includes ALL Dropped packets (not just ones from new loss event, if any), so != cong_ctl_dropped_pkts.
3214 *dropped_pkts -= snd_flying_pkts_by_when.size(); // Subtract "after" from "before" to get dropped count.
3215 *dropped_bytes -= sock->m_snd_flying_bytes; // Similar.
3216
3217 if (*cong_ctl_dropped_pkts != 0)
3218 {
3219 // Register that we've detected a NEW loss event (not the same as dropped_data() -- see that elsewhere).
3220 snd_stats.loss_event();
3221 }
3222
3223 return true;
3224} // Node::drop_pkts_on_acks()
3225
3227{
3228 using boost::algorithm::join;
3229 using boost::chrono::symbol_format;
3230 using std::string;
3231 using std::vector;
3232 using std::transform;
3233 using std::ostream;
3234
3235 // We are in thread W.
3236
3237 // This helper of handle_accumulated_acks() just logs the individual acks about to be processed.
3238
3239 // For brevity and a little speed:
3240 using Ack = Ack_packet::Individual_ack;
3241 using Acks = vector<Ack::Ptr>;
3242 const Acks& acked_packets = sock->m_rcv_acked_packets;
3243
3244 auto const logger_ptr = get_logger();
3245 if (logger_ptr && logger_ptr->should_log(log::Sev::S_DATA, get_log_component())) // Very verbose and slow!
3246 {
3247 // Prepare serialization of m_rcv_acked_packets for TRACE logging; quite verbose and slow!
3248 vector<string> ack_strs(acked_packets.size());
3249 transform(acked_packets.begin(), acked_packets.end(), ack_strs.begin(),
3250 [](Ack::Const_ptr ack) -> string
3251 {
3252 return util::ostream_op_string('[', ack->m_seq_num, ", ", int(ack->m_rexmit_id), ", ",
3253 symbol_format,
3254 ack->m_delay, ']'); // "ns," not "nanoseconds."
3255 });
3256 const string ack_str = join(ack_strs, " ");
3257
3258 FLOW_LOG_DATA_WITHOUT_CHECKING("NetFlow worker thread working on [" << sock << "]. "
3259 "Accumulated [ACK] packets with "
3260 "acknowledgments [seq_num, rexmit_id, delay]: "
3261 "[" << ack_str << "].");
3262 } // if (DATA)
3263 else
3264 {
3265 FLOW_LOG_TRACE("NetFlow worker thread working on [" << sock << "]. "
3266 "Accumulated [ACK] packets with "
3267 "[" << acked_packets.size() << "] individual acknowledgments.");
3268 }
3269
3270 if (sock->m_int_state == Peer_socket::Int_state::S_ESTABLISHED)
3271 {
3272 log_snd_window(sock);
3273 }
3274 // else { Why is this possible? Answer: See handle_accumulated_acks() for explanation near similar check. }
3275} // Node::log_accumulated_acks()
3276
3277void Node::drop_timer_action(Peer_socket::Ptr sock, bool drop_all_packets)
3278{
3279 using std::list;
3280 using boost::prior;
3281
3282 // We are in thread W.
3283
3284 // Since we call m_snd_drop_timer->done() when exiting ESTABLISHED, this should hold.
3285 assert(sock->m_int_state == Peer_socket::Int_state::S_ESTABLISHED);
3286
3287 // For brevity and a bit of speed:
3288 auto& snd_flying_pkts_by_when = sock->m_snd_flying_pkts_by_sent_when;
3289 auto& snd_flying_pkts_by_seq = sock->m_snd_flying_pkts_by_seq_num;
3290
3291 // Timer must not be running if there are no In-flight packets. Thus it should not have fired.
3292 assert(!snd_flying_pkts_by_when.empty());
3293
3294 /* Drop Timer fired and is telling us to consider Dropped some packets. If drop_all_packets, then
3295 * it's all of them. Otherwise it's just the earliest unacknowledged packet
3296 * (m_snd_flying_pkts_by_sent_when.begin()). */
3297
3298 // Log details of the In-flight packets before we change things.
3299 log_snd_window(sock);
3300
3301 const bool rexmit_on = sock->rexmit_on();
3302 // To check, at the end, whether we've changed can_send() false => true.
3303 const bool could_send_before_drops = can_send(sock);
3304 // To check, at the end, whether we've changed snd_deqable() false => true.
3305 const bool had_rexmit_data_before_drops = !sock->m_snd_rexmit_q.empty();
3306 // Will store ID of the one packet to drop; reserved value 0 will mean ALL packets are dropped.
3307 Peer_socket::order_num_t packet_marked_to_drop_or_drop_all;
3308
3309 // Used below for congestion control.
3310 size_t cong_ctl_dropped_bytes = 0;
3311 size_t cong_ctl_dropped_pkts = 0;
3312
3313 if (drop_all_packets)
3314 {
3315 cong_ctl_dropped_bytes = sock->m_snd_flying_bytes;
3316 cong_ctl_dropped_pkts = snd_flying_pkts_by_when.size();
3317
3318 // Queue them for retransmission, to be sent as soon as CWND provides enough space (could even be immediately).
3319 if (rexmit_on)
3320 {
3321 // Order is from earliest-sent to latest-sent (retransmission in the same order as transmission).
3322 for (Peer_socket::Sent_pkt_by_sent_when_map::Reverse_iterator pkt_it = snd_flying_pkts_by_when.oldest();
3323 pkt_it != snd_flying_pkts_by_when.past_newest();
3324 ++pkt_it)
3325 {
3326 // The forward iterator F pointing to same list element as reverse iterator R is prior(R.base()). Google it.
3327 if (!ok_to_rexmit_or_close(sock, prior(pkt_it.base()), false)) // Ensure not too many retransmissions already.
3328 /* ^-- defer_delta_check == false: because we were invoked from a timer event. Therefore, we will NOT perform
3329 * event_set_all_check_delta(false) before the boost.asio handler exits. Therefore boost.asio
3330 * may sleep (block) before event_set_all_check_delta(false). Therefore that would delay
3331 * delivery of the event to the user. Therefore force the delta check immediately. See
3332 * Node::m_sock_events doc header for details. */
3333 {
3334 return; // Already closed/logged/etc.
3335 }
3336 // else
3337
3338 sock->m_snd_rexmit_q.push_back(pkt_it->second); // Only a ref-counted pointer copy (constant time).
3339 }
3340 sock->m_snd_rexmit_q_size += cong_ctl_dropped_pkts;
3341 }
3342 // else { Just drop it. }
3343
3344 // Update our image of the pipe. For efficiency we use clear() instead of doing it one-by-one above.
3345
3346 // Update byte count.
3347 snd_flying_pkts_updated(sock, snd_flying_pkts_by_when.newest(), snd_flying_pkts_by_when.past_oldest(), false);
3348 snd_flying_pkts_by_when.clear();
3349 snd_flying_pkts_by_seq.clear();
3350
3351 packet_marked_to_drop_or_drop_all = 0; // Means drop all.
3352 }
3353 else
3354 {
3355 // Get the packet that was sent before all the others.
3356 const Peer_socket::Sent_pkt_ordered_by_when_iter& oldest_pkt_it = prior(snd_flying_pkts_by_when.past_oldest());
3357 Peer_socket::Sent_packet::Ptr oldest_pkt = oldest_pkt_it->second;
3358
3359 cong_ctl_dropped_bytes = oldest_pkt->m_size;
3360 cong_ctl_dropped_pkts = 1;
3361
3362 // Queue it for retransmission, to be sent as soon as CWND provides enough space (could even be immediately).
3363 if (rexmit_on)
3364 {
3365 if (!ok_to_rexmit_or_close(sock, oldest_pkt_it, false)) // Ensure not too many retransmissions already.
3366 // ^-- false <= Same as comment above.
3367 {
3368 return; // Already closed/logged/etc.
3369 }
3370 // else
3371
3372 sock->m_snd_rexmit_q.push_back(oldest_pkt); // Only a ref-counted pointer copy (constant time).
3373 ++sock->m_snd_rexmit_q_size;
3374 }
3375 // else { Just drop it. }
3376
3377 // Remember it short-term for the Drop_timer consolidated book-keeping below...
3378 packet_marked_to_drop_or_drop_all = oldest_pkt->m_sent_when.back().m_order_num;
3379
3380 // ...and in fact mark that packet Dropped (update our image of the pipe).
3381 snd_flying_pkts_erase_one(sock, oldest_pkt_it);
3382 }
3383
3384 /* Deal with congestion control. For introduction to the general topic see the large comment
3385 * near the top of handle_accumulated_acks().
3386 *
3387 * Since a Drop Timeout implies a large loss event, the congestion control module must be
3388 * informed. It may adjust the congestion window (used in can_send() and controlling how many
3389 * packets we are allowed to have In-flight at a time), probably downward.
3390 *
3391 * Also, this is a new loss event. Why? (For detailed explanation of what a loss event is, and
3392 * how we keep track of them, see that large comment in handle_accumulated_acks(). It
3393 * may be required to understand the rest of this paragraph.) Certainly this Drop is part of some
3394 * loss event by definition, but is it a new loss event, or merely the previous one (if such
3395 * exists)? Well, a Drop Timeout is, in practice, at least 1 second (which is likely 4 times a
3396 * pretty large RTT of 250 msec) and can also be estimated to be 3 * SRTT. In other words it is
3397 * probably much larger than SRTT, and certainly is at least a little larger than SRTT. Therefore
3398 * most likely any packet(s) Dropped by this DTO were sent after the last loss event (if any) was
3399 * detected. Hence this DTO event is a new loss event. We could explicitly check for this, but
3400 * it seems unnecessarily complex and intuitively unnecessary.
3401 *
3402 * Per handle_accumulated_acks(), when a new loss event is seen, m_snd_last_loss_event_when
3403 * is set to NOW. */
3404
3405 // @todo Arguable if it should be INFO or TRACE. We'll see.
3406 FLOW_LOG_INFO("cong_ctl [" << sock << "] update: Drop Timeout event: "
3407 "Dropped [" << cong_ctl_dropped_bytes << "] bytes = [" << cong_ctl_dropped_pkts << "] packets.");
3408
3409 // MUST call this after, not before, updating m_snd_flying_{packets|bytes} per method doc.
3410 sock->m_snd_cong_ctl->on_drop_timeout(cong_ctl_dropped_bytes, cong_ctl_dropped_pkts);
3411 sock->m_snd_last_loss_event_when = Fine_clock::now();
3412
3413 // Register that there was a timeout, and that bytes were converted from In-flight to Dropped.
3414 sock->m_snd_stats.drop_timeout();
3415 sock->m_snd_stats.dropped_data(cong_ctl_dropped_bytes, cong_ctl_dropped_pkts);
3416
3417 // Now log the "after."
3418 log_snd_window(sock);
3419
3420 // Since we've changed snd_flying_pkts*, Drop_timer events have occurred. Cleanly handle them all in one go.
3421
3422 const Drop_timer::Ptr drop_timer = sock->m_snd_drop_timer;
3423 drop_timer->start_contemporaneous_events();
3424
3425 /* Handle possible effect of above activities on the Drop Timer. (It may get disabled or restarted anew.)
3426 * Why not just do this right when we erase the associated packets from snd_flying_pkts*? We don't want to
3427 * trigger disruptive behavior like possibly retransmitting everything in the middle of all that accounting
3428 * which is not yet complete. Now it's complete, so it's the right time to handle this.
3429 *
3430 * Recall that snd_flying_pkts* have been updated and no longer contain the associated packet(s)'s info. */
3431 if (packet_marked_to_drop_or_drop_all == 0)
3432 {
3433 // Note that this is equivalent to calling ...packet_no_longer_in_flight(P) for all P -- just faster.
3434 drop_timer->on_no_packets_in_flight_any_longer();
3435 }
3436 else // if (packet_marked_to_drop_or_drop_all refers to, in fact, a specific packet)
3437 {
3438 drop_timer->on_packet_no_longer_in_flight(packet_marked_to_drop_or_drop_all);
3439 /* Could also call on_no_packets_in_flight_any_longer() if now none is In-flight, but performance-wise that'd
3440 * be the same; ...packet_no_longer_in_flight() will check the same condition anyway. So don't bother. */
3441 }
3442
3443 drop_timer->end_contemporaneous_events();
3444
3445 /* We've definitely reduced the number of packets we consider In-flight. We may also have added
3446 * packets to retransmission queue (if retransmission is on). Therefore can_send() may now return
3447 * true while at the beginning of the method it returned false; snd_deqable() may now return true
3448 * similarly. So have send_worker() check and send more if possible. See Node::send() for
3449 * discussion of overall strategy on this topic. */
3450 if ((!could_send_before_drops) || (rexmit_on && (!had_rexmit_data_before_drops)))
3451 {
3452 send_worker(sock, false);
3453 // ^-- defer_delta_check == false: for similar reason as in send_worker_check_state() calling send_worker().
3454 }
3455} // Node::drop_timer_action()
3456
3458{
3459 using std::min;
3460 using std::max;
3461 using boost::ratio;
3462 using boost::ratio_subtract;
3463 using boost::ratio_string;
3464 using boost::chrono::round;
3465 using boost::chrono::milliseconds;
3466 using boost::chrono::microseconds;
3467 using boost::chrono::seconds;
3468
3469 // We are in thread W.
3470
3471 // For brevity and a bit of speed:
3472 Fine_duration& srtt = sock->m_snd_smoothed_round_trip_time;
3473 Fine_duration& rtt_var = sock->m_round_trip_time_variance;
3474 Fine_duration& dto = sock->m_snd_drop_timeout;
3475 const Fine_duration& rtt = round_trip_time;
3476
3477 /* An ACK has supplied the given round_trip_time for a specific packet. We are to update the
3478 * smoothed RTT for the socket which is an estimate for the smooth "current" RTT for the socket.
3479 * Use RFC 6298 algorithm for SRTT calculation.
3480 *
3481 * RFC 6298 specifies the formula in "seconds." Of course it need not be seconds; it can be any
3482 * unit. We leave the unit we use unspecified, except to say that we will use the unit of
3483 * Fine_duration, which is the duration type of Fine_clock, which is the highest-resolution clock
3484 * available in the OS/hardware. Since, where possible, we keep using Fine_duration without
3485 * truncation to compute round_trip_time, assuming we don't introduce any unit conversions
3486 * (truncations, roundings) in the below code, the SRTT will maintain those units as well.
3487 * boost::chrono::duration will specifically cause compile failures if we don't explicitly specify
3488 * every truncation-inducing operation (duration_cast<>, round<>, etc.).
3489 *
3490 * BTW, this "unspecified" unit is probably nanoseconds.
3491 *
3492 * Note that the units used do NOT guarantee any particular clock granularity. E.g., I can give
3493 * you the time in milliseconds, but if I always say it in multiples of 1000 milliseconds, then I
3494 * may be working with milliseconds, but the resolution is 1 sec. */
3495
3496 if (srtt == Fine_duration::zero())
3497 {
3498 // First RTT measurement; initialize according to algorithm.
3499 srtt = rtt;
3500 rtt_var = rtt / 2;
3501
3502 // Truncate results to millisecond representation for readability.
3503 FLOW_LOG_TRACE("First SRTT calculation for [" << sock << "]: "
3504 "srtt = [" << round<milliseconds>(srtt) << " = " << srtt << "]; "
3505 "rtt_var = [" << round<milliseconds>(rtt_var) << " = " << rtt_var << "]; "
3506 "rtt = [" << rtt << "].");
3507 }
3508 else // if (SRTT was defined before this sample.)
3509 {
3510 // Subsequent RTT measurements.
3511
3512 // @todo Per last paragraph of RFC 6298-5, we MAY want to clear srtt/rtt_var afer multiple RTOs or maybe idleness.
3513 // (RTO = Retransmission Timeout, though we call it a Drop Timeout more accurately [we don't necessarily
3514 // retransmit on loss in NetFlow, unlike TCP].)
3515
3516 const Fine_duration prev_srtt = srtt;
3517 const Fine_duration prev_rtt_var = rtt_var;
3518
3519 /* Reason I used ratio<> instead of floating point constants: I don't want to use floating
3520 * points in production code that much. I don't necessarily trust it for consistent behavior across platforms...
3521 * and in general I just find integers more predictable/easier to reason about in most contexts of net_flow.
3522 * Reason I used ratio<> instead of just having separate integer constants for numerators and
3523 * denominators: I'd rather have ratio<> do the arithmetic for me (at compile time to boot!). */
3524 using Alpha = ratio<1, 8>; // 1/8, per RFC.
3525 using One_minus_alpha = ratio_subtract<ratio<1>, Alpha>;
3526 using Beta = ratio<1, 4>; // 1/4, per RFC.
3527 using One_minus_beta = ratio_subtract<ratio<1>, Beta>;
3528 // Now I can use X::num and X::den, such that X is the ratio X::num/X::den.
3529
3530 // Compute |srtt - rtt|.
3531 Fine_duration abs_srtt_minus_rtt = srtt - rtt;
3532 if (abs_srtt_minus_rtt.count() < 0)
3533 {
3534 abs_srtt_minus_rtt = -abs_srtt_minus_rtt;
3535 }
3536
3537 // Update the results per RFC.
3538 rtt_var
3539 = rtt_var * One_minus_beta::num / One_minus_beta::den
3540 + abs_srtt_minus_rtt * Beta::num / Beta::den;
3541 srtt
3542 = srtt * One_minus_alpha::num / One_minus_alpha::den
3543 + rtt * Alpha::num / Alpha::den;
3544
3545 // Truncate results to millisecond representation for readability.
3546 FLOW_LOG_TRACE("Next SRTT calculation for [" << sock << "]: "
3547 "srtt = [" << round<milliseconds>(srtt) << " = " << srtt << "]; "
3548 "rtt_var = [" << round<milliseconds>(rtt_var) << " = " << rtt_var << "]; "
3549 "rtt = [" << rtt << "]; "
3550 "prev_srtt = [" << prev_srtt << "]; "
3551 "prev_rtt_var = [" << prev_rtt_var << "]; "
3552 "alpha = " << (ratio_string<Alpha, char>::prefix()) << "; "
3553 "(1 - alpha) = " << (ratio_string<One_minus_alpha, char>::prefix()) << "; "
3554 "beta = " << (ratio_string<Beta, char>::prefix()) << "; "
3555 "(1 - beta) = " << (ratio_string<One_minus_beta, char>::prefix()) << "; "
3556 "|srtt - rtt| = [" << abs_srtt_minus_rtt << "].");
3557 } // else if (SRTT was defined before this sample)
3558
3559 /* Now compute Drop Timeout (DTO), similar to TCP's RTO (Retransmission Timeout): the minimum
3560 * amount of time we give an In-flight packet to get Acknowledged before considering it Dropped.
3561 * Again we use RFC 6298 for DTO computation.
3562 *
3563 * The formula is DTO = srtt + max(G, K * rtt_var), where K = 4 and G is the "clock
3564 * granularity." Additionally, we are to put a floor of 1 second on DTO. Finally, we are allowed
3565 * to put a ceiling on DTO, as long as that ceiling is at least 60 seconds.
3566 *
3567 * G plays an important part in the RTO caclulation algorithm, so we must know it. So what is it?
3568 * We don't know. We do however have a reasonably conservative upper bound; boost.timer
3569 * documentation lists some popular OS+CPU combinations and notes that for none of them does
3570 * high_resolution_timer exceed 5 microseconds. Therefore, let us pick the exceedingly
3571 * conservative G = 500 microseconds = 1/2 milliseconds. */
3572
3573 const Fine_duration clock_resolution_at_least = microseconds{500};
3574 const Fine_duration floor = seconds{1};
3575 const Fine_duration ceiling = sock->opt(sock->m_opts.m_dyn_drop_timeout_ceiling);
3576 const unsigned int k = 4;
3577
3578 const Fine_duration prev_dto = dto;
3579 const Fine_duration rtt_var_k = rtt_var * k;
3580 const Fine_duration srtt_plus_var_term = srtt + max(clock_resolution_at_least, rtt_var_k);
3581 dto = max(srtt_plus_var_term, floor);
3582 dto = min(dto, ceiling);
3583
3584 // Truncate results to millisecond representation for readability.
3585 FLOW_LOG_TRACE("Drop Timeout (DTO) calculation: "
3586 "dto = [" << round<milliseconds>(dto) << " = " << dto << "]; "
3587 "rtt_var * k = [" << rtt_var_k << "]; "
3588 "srtt + max(G, rtt_var * k) = [" << srtt_plus_var_term << "]; "
3589 "k = [" << k << "]; "
3590 "floor = [" << floor << "]; ceiling = [" << ceiling << "]; "
3591 "clock_resolution = [" << clock_resolution_at_least << "]; "
3592 "prev_dto = [" << prev_dto << "].");
3593} // void Node::new_round_trip_time_sample()
3594
3595void Node::log_snd_window(Peer_socket::Const_ptr sock, bool force_verbose_info_logging) const
3596{
3597 using std::vector;
3598 using std::list;
3599 using std::string;
3600 using boost::algorithm::join;
3601 using boost::prior;
3603 using std::flush;
3604
3605 // We're in thread W.
3606
3607 // For brevity and a little speed:
3608 const auto& snd_flying_pkts_by_seq = sock->m_snd_flying_pkts_by_seq_num;
3609 const auto& snd_flying_pkts_by_when = sock->m_snd_flying_pkts_by_sent_when;
3610 const size_t num_flying_pkts = snd_flying_pkts_by_seq.size();
3611
3612 // force_verbose_info_logging => log the most detail, as INFO (if INFO logging enabled).
3613
3614 if (snd_flying_pkts_by_seq.empty())
3615 {
3616 // No In-flight packets, so this is brief enough for TRACE as opposed to DATA.
3617 FLOW_LOG_WITH_CHECKING(force_verbose_info_logging ? log::Sev::S_INFO : log::Sev::S_TRACE,
3618 "Send window state for [" << sock << "]: cong_wnd "
3619 "[" << sock->bytes_blocks_str(sock->m_snd_cong_ctl->congestion_window_bytes()) << "]; "
3620 "sent+acked/dropped "
3621 "[" << sock->m_snd_init_seq_num << ", " << sock->m_snd_next_seq_num << ") "
3622 "unsent [" << sock->m_snd_next_seq_num << ", ...).");
3623 return;
3624 }
3625 // else
3626
3627 auto const logger_ptr = get_logger();
3628 if (((!logger_ptr) || (!logger_ptr->should_log(log::Sev::S_DATA, get_log_component()))) &&
3629 (!(force_verbose_info_logging && logger_ptr->should_log(log::Sev::S_INFO, get_log_component()))))
3630 {
3631 // Can't print entire In-flight data structure, but can print a summary, if TRACE enabled.
3633 ("Send window state for [" << sock << "]: cong_wnd "
3634 "[" << sock->bytes_blocks_str(sock->m_snd_cong_ctl->congestion_window_bytes()) << "]; "
3635 "sent+acked/dropped [" << sock->m_snd_init_seq_num << ", " << snd_flying_pkts_by_seq.begin()->first << ") "
3636 "in-flight [" << sock->m_snd_flying_bytes << "] bytes: " << num_flying_pkts << ":{...} "
3637 "unsent [" << sock->m_snd_next_seq_num << ", ...).");
3638 return;
3639 }
3640 // else
3641
3642 // Very verbose and slow!
3643
3644 const bool rexmit_on = sock->rexmit_on();
3645
3646 vector<string> pkt_strs;
3647 pkt_strs.reserve(num_flying_pkts);
3648 for (Peer_socket::Sent_pkt_ordered_by_seq_const_iter pkt_it_it = snd_flying_pkts_by_seq.begin();
3649 pkt_it_it != snd_flying_pkts_by_seq.end();
3650 ++pkt_it_it)
3651 {
3652 Sequence_number start, end;
3653 get_seq_num_range(pkt_it_it->second, &start, &end);
3654
3655 Peer_socket::Sent_packet::Const_ptr sent_pkt = pkt_it_it->second->second;
3656
3657 String_ostream pkt_str_os;
3658 pkt_str_os.os() << '[' << start;
3659 if (rexmit_on)
3660 {
3661 pkt_str_os.os() << '[' << int(sent_pkt->m_packet->m_rexmit_id) << '/' << sent_pkt->m_sent_when.back().m_order_num
3662 << "], ";
3663 }
3664 else
3665 {
3666 pkt_str_os.os() << ", ";
3667 }
3668 pkt_str_os.os() << end << ")<" << sent_pkt->m_acks_after_me << "acks" << flush;
3669
3670 pkt_strs.push_back(pkt_str_os.str());
3671 }
3672
3674 (force_verbose_info_logging ? log::Sev::S_INFO : log::Sev::S_DATA,
3675 "Send window state for [" << sock << "]: cong_wnd "
3676 "[" << sock->bytes_blocks_str(sock->m_snd_cong_ctl->congestion_window_bytes()) << "]; "
3677 "sent+acked/dropped [" << sock->m_snd_init_seq_num << ", " << snd_flying_pkts_by_seq.begin()->first << ") "
3678 "in-flight "
3679 "[" << sock->m_snd_flying_bytes << "] bytes: " << num_flying_pkts << ":{" << join(pkt_strs, " ") <<
3680 "} unsent [" << sock->m_snd_next_seq_num << ", ...).");
3681
3682 if (!rexmit_on)
3683 {
3684 return;
3685 }
3686 // else
3687
3688 // Since retransmission is on, also useful to show the packets sorted by when they were sent.
3689
3690 vector<string> pkt_strs_time;
3691 pkt_strs_time.reserve(num_flying_pkts);
3692 // Note I don't use `auto` only for clarity (to express it is a reverse iterator, hence why didn't use for(:)).
3693 for (Peer_socket::Sent_pkt_by_sent_when_map::Const_reverse_iterator pkt_it = snd_flying_pkts_by_when.const_oldest();
3694 pkt_it != snd_flying_pkts_by_when.const_past_newest();
3695 ++pkt_it)
3696 {
3697 Sequence_number start, end;
3698 // The forward iterator F pointing to same list element as reverse iterator R is prior(R.base()) [sic]. Google it.
3699 get_seq_num_range(prior(pkt_it.base()), &start, &end);
3700
3701 Peer_socket::Sent_packet::Const_ptr sent_pkt = pkt_it->second;
3702
3703 string pkt_str;
3705 start, '[', int(sent_pkt->m_packet->m_rexmit_id), '/',
3706 sent_pkt->m_sent_when.back().m_order_num, "], ", end, ")<",
3707 sent_pkt->m_acks_after_me, "acks");
3708 pkt_strs_time.push_back(pkt_str);
3709 }
3710
3711 // Log it only if it is different (only possible if some retransmitted packets are actually involved).
3712 if (pkt_strs_time != pkt_strs)
3713 {
3715 (force_verbose_info_logging ? log::Sev::S_INFO : log::Sev::S_DATA,
3716 "Sorted by time sent: {" << join(pkt_strs_time, " ") << "}.");
3717 }
3718} // Node::log_snd_window()
3719
3721{
3722 using boost::prior;
3723
3724 const Peer_socket::Sent_pkt_by_seq_num_map& flying_packets = sock->m_snd_flying_pkts_by_seq_num;
3725 if (flying_packets.empty())
3726 {
3727 return {}; // Default value. Less than all others.
3728 }
3729 // else
3730
3731 // Get the sequence number of the first datum in the last unhandled packet.
3732 const Peer_socket::Sent_pkt_by_seq_num_map::value_type& highest_val = *(prior(flying_packets.end()));
3733 Sequence_number seq_num = highest_val.first;
3734
3735 // Advance just past the data in that packet to get what we want.
3736 advance_seq_num(&seq_num, highest_val.second->second->m_size);
3737
3738 return seq_num;
3739}
3740
3742{
3743 // using boost::next; // Still ambiguous for some reason (in clang at least).
3744
3745 auto const logger_ptr = get_logger();
3746 if (logger_ptr && logger_ptr->should_log(log::Sev::S_TRACE, get_log_component()))
3747 {
3748 const Peer_socket::Sent_packet& sent_pkt = *pkt_it->second;
3749 const Peer_socket::order_num_t order_num = sent_pkt.m_sent_when.back().m_order_num;
3750 Sequence_number seq_num, seq_num_end;
3751 get_seq_num_range(pkt_it, &seq_num, &seq_num_end);
3752
3753 if (sock->rexmit_on())
3754 {
3756 ("On [" << sock << "] erasing packet [" << seq_num << ", " << seq_num_end << ") "
3757 "order_num [" << order_num << "] rexmit_id [" << int(sent_pkt.m_packet->m_rexmit_id) << "] from "
3758 "snd_flying_pkts* and friends.");
3759 }
3760 else
3761 {
3763 ("On [" << sock << "] erasing packet [" << seq_num << ", " << seq_num_end << ") "
3764 "order_num [" << order_num << "] from snd_flying_pkts* and friends.");
3765 }
3766 }
3767
3768 // Update byte count.
3769 snd_flying_pkts_updated(sock, pkt_it, boost::next(pkt_it), false);
3770
3771 // Finally erase from main structures.
3772 sock->m_snd_flying_pkts_by_seq_num.erase(pkt_it->first);
3773 sock->m_snd_flying_pkts_by_sent_when.erase(pkt_it);
3774
3775 // Note: As advertsied, we do NOT inform sock->m_snd_drop_timer. It is up to the caller to do the right thing there.
3776}
3777
3779 const Sequence_number& seq_num,
3781{
3782 using std::pair;
3783 using std::make_pair;
3784 // using boost::next; // Still ambiguous for some reason (in clang at least).
3785
3786 // For brevity and a bit of speed:
3787 auto& snd_flying_pkts_by_when = sock->m_snd_flying_pkts_by_sent_when;
3788
3789#ifndef NDEBUG
3790 const auto insert_result =
3791#endif
3792 snd_flying_pkts_by_when.insert(make_pair(seq_num, sent_pkt));
3793
3794 // In this map, last added (a/k/a last sent) packet = first in the ordering!
3795 const Peer_socket::Sent_pkt_ordered_by_when_iter& pkt_it = snd_flying_pkts_by_when.begin();
3796 assert(insert_result.second); // Sequence numbers must not repeat ever.
3797 assert(insert_result.first == pkt_it); // Check that just-inserted element is ordered at the start.
3798
3799 snd_flying_pkts_updated(sock, pkt_it, boost::next(pkt_it), true); // Update byte count.
3800
3801 // Accordingly, insert packet (in the form of iterator into the above map) into sequence-number-ordered "scoreboard."
3802#ifndef NDEBUG
3803 const auto insert_result_by_seq =
3804#endif
3805 sock->m_snd_flying_pkts_by_seq_num.insert(make_pair(seq_num, pkt_it));
3806
3807 // Check invariant: Key X is in ..._by_sent_when <=> key X is in ..._by_seq_num.
3808 assert(insert_result_by_seq.second);
3809
3810 /* Caution: As noted in the doc header for this method, note that while we've already inserted sent_pkt into
3811 * snd_flying_pkts_by_when, the actual value of sent_pkt->m_sent_when.back() -- the absolute "when" -- isn't ready.
3812 * It will only be finalized once we actually send off the packet (after pacing, if any), in mark_data_packet_sent().
3813 * Nevertheless, we know the packet will be sent sometime fairly soon; and in fact AFTER all the packets
3814 * following it it in snd_flying_pkts_by_when's iterator ordering and in fact BEFORE any packets that
3815 * would be subsequently ahead of it in snd_flying_pkts_by_when's iterator ordering. That is, we can
3816 * place it there now, despite not knowing the _absolute_ time when it be sent, because we are confident about
3817 * its _relative_ order of when it will be sent vs. all the other packets in that structure, past or future. */
3818
3819 // Everything following this point is logging only.
3820
3821 auto const logger_ptr = get_logger();
3822 if ((!logger_ptr) || (!logger_ptr->should_log(log::Sev::S_TRACE, get_log_component())))
3823 {
3824 return;
3825 }
3826 // else
3827
3828 Sequence_number seq_num_end;
3829 get_seq_num_range(pkt_it, nullptr, &seq_num_end);
3830 if (sock->rexmit_on())
3831 {
3833 ("On [" << sock << "] pushing packet [" << seq_num << ", " << seq_num_end << ") "
3834 "rexmit_id [" << int(sent_pkt->m_packet->m_rexmit_id) << "] onto snd_flying_pkts and friends.");
3835 }
3836 else
3837 {
3839 ("On [" << sock << "] pushing packet [" << seq_num << ", " << seq_num_end << ") "
3840 "onto snd_flying_pkts and friends.");
3841 }
3842}
3843
3847 bool added)
3848{
3849 // We are in thread W.
3850
3851 if (pkt_begin == pkt_end)
3852 {
3853 return; // Wouldn't do anything anyway, but return here to avoid logging.
3854 }
3855
3856 // For brevity and a bit of speed:
3857 const auto& snd_flying_pkts_by_when = sock->m_snd_flying_pkts_by_sent_when;
3858 size_t& snd_flying_bytes = sock->m_snd_flying_bytes;
3859
3860 // Optimization for when they effectively clear() snd_flying_pkts* (e.g., possibly on Drop Timeout):
3861 if ((!added)
3862 && (pkt_begin == snd_flying_pkts_by_when.const_newest())
3863 && (pkt_end == snd_flying_pkts_by_when.const_past_oldest()))
3864 {
3865 snd_flying_bytes = 0;
3866 }
3867 else
3868 {
3869 size_t delta_bytes = 0;
3870 for ( ; pkt_begin != pkt_end; ++pkt_begin)
3871 {
3872 delta_bytes += pkt_begin->second->m_size;
3873 }
3874 added ? (snd_flying_bytes += delta_bytes) : (snd_flying_bytes -= delta_bytes);
3875 }
3876
3877 FLOW_LOG_TRACE("cong_ctl [" << sock << "] update: "
3878 "In-flight [" << sock->bytes_blocks_str(snd_flying_bytes) << "].");
3879}
3880
3883 bool defer_delta_check)
3884{
3885 const Peer_socket::Sent_packet& pkt = *pkt_it->second;
3886
3887 Sequence_number seq_num, seq_num_end;
3888 get_seq_num_range(pkt_it, &seq_num, &seq_num_end);
3889
3890 const unsigned int rexmit_id = pkt.m_packet->m_rexmit_id;
3891 FLOW_LOG_TRACE("On [" << sock << "] attempting to queue for retransmission "
3892 "[" << seq_num << ", " << seq_num_end << "] which has been "
3893 "retransmitted [" << rexmit_id << "] times so far.");
3894 if (rexmit_id == sock->opt(sock->m_opts.m_st_max_rexmissions_per_packet))
3895 {
3896 rst_and_close_connection_immediately(socket_id(sock), sock,
3898 return false;
3899 }
3900 // else
3901 return true;
3902}
3903
3905 const Peer_socket_options* opts)
3906{
3907 return connect_with_metadata(to, boost::asio::buffer(&S_DEFAULT_CONN_METADATA, sizeof(S_DEFAULT_CONN_METADATA)),
3908 err_code, opts);
3909}
3910
3912 const boost::asio::const_buffer& serialized_metadata,
3913 Error_code* err_code,
3914 const Peer_socket_options* sock_opts)
3915{
3916 FLOW_ERROR_EXEC_AND_THROW_ON_ERROR(Peer_socket::Ptr, connect_with_metadata, to, serialized_metadata, _1, sock_opts);
3917 // ^-- Call ourselves and return if err_code is null. If got to present line, err_code is not null.
3918
3921
3922 // We are in thread U != W.
3923
3924 if (!running())
3925 {
3927 return Peer_socket::Ptr{};
3928 }
3929 // else
3930
3931 // If it's good enough for DATA packets, it's good enough for metadata in SYN.
3932 if (serialized_metadata.size() > max_block_size())
3933 {
3935 return Peer_socket::Ptr{};
3936 }
3937
3938 /* Put the rest of the work into thread W. For justification, see big comment in listen().
3939 * Addendum regarding performance: connect() is probably called more frequently than listen(), but
3940 * I doubt the performance impact is serious even so. send() and receive() might be a different
3941 * story. */
3942
3943 Peer_socket::Ptr sock;
3944 /* Load this->connect_worker(...) onto thread W boost.asio work queue.
3945 * We don't return until it finishes; therefore it is fine to do total & capture. */
3946 asio_exec_ctx_post(get_logger(), &m_task_engine, Synchronicity::S_ASYNC_AND_AWAIT_CONCURRENT_COMPLETION,
3947 [&]() { connect_worker(to, serialized_metadata, sock_opts, &sock); });
3948 // If got here, the task has completed in thread W and signaled us to that effect.
3949
3950 // connect_worker() indicates success or failure through this data member.
3951 if (sock->m_disconnect_cause)
3952 {
3953 *err_code = sock->m_disconnect_cause;
3954 return Peer_socket::Ptr{}; // sock will go out of scope and thus will be destroyed.
3955 }
3956 // else
3957 err_code->clear();
3958 return sock;
3959} // Node::connect_with_metadata()
3960
3961void Node::connect_worker(const Remote_endpoint& to, const boost::asio::const_buffer& serialized_metadata,
3962 const Peer_socket_options* sock_opts,
3963 Peer_socket::Ptr* sock_ptr)
3964{
3965 using boost::asio::buffer;
3966 using boost::asio::ip::address;
3967
3968 assert(sock_ptr);
3969
3970 // We are in thread W. connect() is waiting for us to set *sock_ptr and return.
3971
3972 // Create new socket and set all members that may be immediately accessed by user in thread U after we're done.
3973
3974 auto& sock = *sock_ptr;
3975 if (sock_opts)
3976 {
3977 /* They provided custom per-socket options. Before we give those to the new socket, let's
3978 * validate them (for proper values and internal consistency, etc.). */
3979
3980 Error_code err_code;
3981 const bool opts_ok = sock_validate_options(*sock_opts, nullptr, &err_code);
3982
3983 // Due to the advertised interface of the current method, we must create a socket even on error.
3984 sock.reset(sock_create(*sock_opts));
3985
3986 // Now report error if indeed options were invalid. err_code is already set and logged in that case.
3987 if (!opts_ok)
3988 {
3989 sock->m_disconnect_cause = err_code;
3990 return;
3991 }
3992 // else
3993 }
3994 else
3995 {
3996 /* More typically, they did not provide per-socket options. So we just pass our global
3997 * template for the per-socket options to the Peer_socket constructor. The only caveat is
3998 * that template may be concurrently changed, so we must lock it. Could do it with opt(), but
3999 * that introduces an extra copy of the entire struct, so just do it explicitly.
4000 *
4001 * Note: no need to validate; global options (including per-socket ones) are validated
4002 * elsewhere when set. */
4003 Peer_socket* sock_non_ptr;
4004 {
4006 sock_non_ptr = sock_create(m_opts.m_dyn_sock_opts);
4007 }
4008 sock.reset(sock_non_ptr);
4009 }
4010
4011 // Socket created; set members.
4012
4013 sock->m_active_connect = true;
4014 sock->m_node = this;
4016 sock->m_remote_endpoint = to;
4017 // Will be sent in SYN to be deserialized by user on the other side. Save here if we must retransmit SYN.
4018 sock->m_serialized_metadata.assign_copy(serialized_metadata);
4019
4020 /* Initialize the connection's send bandwidth estimator (object that estimates available
4021 * outgoing bandwidth based on incoming acknowledgments). It may be used by m_snd_cong_ctl,
4022 * depending on the strategy chosen, but may be useful in its own right. Hence it's a separate
4023 * object, not inside *m_snd_cong_ctl. */
4024 sock->m_snd_bandwidth_estimator.reset(new Send_bandwidth_estimator{get_logger(), sock});
4025
4026 // Initialize the connection's congestion control strategy based on the configured strategy.
4027 sock->m_snd_cong_ctl.reset
4028 (Congestion_control_selector::create_strategy(sock->m_opts.m_st_cong_ctl_strategy, get_logger(), sock));
4029 // ^-- No need to use opt() yet: user doesn't have socket and cannot set_options() on it yet.
4030
4031 /* Tweak: If they specify the "any" IP address as the destination (which means any interface on
4032 * this machine), response traffic will look as though it's coming from the loopback IP address,
4033 * or another specific IP address -- not "any." Thus it will not be able to be properly
4034 * demultiplexed to this socket, since that will be saved at the "any" address in our data
4035 * structures. So that's an error. */
4036 bool ip_addr_any_error = false;
4037 const address& addr = to.m_udp_endpoint.address(); // Short-hand.
4038 if (addr.is_v4())
4039 {
4040 if (addr.to_v4() == util::Ip_address_v4::any())
4041 {
4042 ip_addr_any_error = true;
4043 }
4044 }
4045 else if (addr.is_v6())
4046 {
4047 if (addr.to_v6() == util::Ip_address_v6::any())
4048 {
4049 ip_addr_any_error = true;
4050 }
4051 }
4052 // else a new version of IP! Yay!
4053 if (ip_addr_any_error)
4054 {
4055 // Mark/log error.
4056 Error_code* err_code = &sock->m_disconnect_cause;
4058 return;
4059 }
4060 // else
4061
4062 // Allocate ephemeral local port.
4063
4064 sock->m_local_port = m_ports.reserve_ephemeral_port(&sock->m_disconnect_cause);
4065 if (sock->m_local_port == S_PORT_ANY)
4066 {
4067 // Error already logged and is in sock->m_disconnect_cause.
4068 return;
4069 }
4070 // else
4071
4072 const Socket_id socket_id = Node::socket_id(sock);
4073 FLOW_LOG_INFO("NetFlow worker thread starting active-connect of [" << sock << "].");
4074
4075 if (util::key_exists(m_socks, socket_id))
4076 {
4077 /* This is an active connect (we're intiating the connection). Therefore in particular it
4078 * should be impossible that our local_port() equals an already existing connection's
4079 * local_port(); Port_space is supposed to prevent the same ephemeral port from being handed out
4080 * to more than one connection. Therefore this must be a programming error. */
4081
4082 FLOW_LOG_WARNING("Cannot add [" << sock << "], because such a connection already exists. "
4083 "This is an ephemeral port collision and "
4084 "constitutes either a bug or an extremely unlikely condition.");
4085
4086 // Mark/log error.
4087 Error_code* err_code = &sock->m_disconnect_cause;
4089
4090 // Return port.
4091 Error_code return_err_code;
4092 m_ports.return_port(sock->m_local_port, &return_err_code);
4093 assert(!return_err_code);
4094
4095 return;
4096 } // if (that socket pair already exists)
4097 // else
4098
4099 /* Try the packet send just below again if SYN not acknowledged within a certain amount of time.
4100 * Give up if that happens too many times. Why do this BEFORE sending packet? Because
4101 * this can fail, in which case we don't want a weird situation where we've sent
4102 * the packet but failed to start the retransmit/timeout timers.
4103 * Update: It can no longer fail, so that reasoning is N/A. Not moving, though, because it's still fine here. */
4104 setup_connection_timers(socket_id, sock, true);
4105
4106 /* Initial Sequence Number (ISN) (save before create_syn() uses it).
4107 * Remember it in case we must retransmit the SYN. (m_snd_next_seq_num may have been further increased by then.) */
4108 Sequence_number& init_seq_num = sock->m_snd_init_seq_num;
4109 init_seq_num = m_seq_num_generator.generate_init_seq_num();
4110 /* Setting this now ensures ALL subsequent copies (essentially, every single Sequence_number on this socket's
4111 * local data number line!) will have the same nice metadata (hence nice logging) too.
4112 * The `+ 1` nuance is explained in class Sequence_number doc header, *Metadata* section. */
4113 init_seq_num.set_metadata('L', init_seq_num + 1, sock->max_block_size());
4114 // Sequence number of first bit of actual data.
4115 sock->m_snd_next_seq_num = init_seq_num + 1;
4116
4117 // Make a SYN packet to send.
4118 auto syn = create_syn(sock);
4119
4120 // Fill out common fields and asynchronously send packet.
4121 async_sock_low_lvl_packet_send_paced(sock, Low_lvl_packet::ptr_cast(syn));
4122
4123 /* send will happen asynchronously, and the registered completion handler will execute in this
4124 * thread when done (NO SOONER than this method finishes executing). */
4125
4126 // No more erros: Map socket pair to the socket data structure (kind of analogous to a TCP net-stack's TCB structure).
4127 m_socks[socket_id] = sock;
4128
4129 // CLOSED -> SYN_SENT.
4130 sock_set_int_state(sock, Peer_socket::Int_state::S_SYN_SENT);
4131} // Node::connect_worker()
4132
4134 const Peer_socket_options* sock_opts)
4135{
4136 return sync_connect_with_metadata(to, Fine_duration::max(),
4137 boost::asio::buffer(&S_DEFAULT_CONN_METADATA, sizeof(S_DEFAULT_CONN_METADATA)),
4138 err_code, sock_opts);
4139}
4140
4142 const boost::asio::const_buffer& serialized_metadata,
4143 Error_code* err_code, const Peer_socket_options* opts)
4144{
4145 return sync_connect_with_metadata(to, Fine_duration::max(), serialized_metadata, err_code, opts);
4146}
4147
4149 const boost::asio::const_buffer& serialized_metadata,
4150 Error_code* err_code, const Peer_socket_options* sock_opts)
4151{
4153 to, max_wait, serialized_metadata, _1, sock_opts);
4154 // ^-- Call ourselves and return if err_code is null. If got to present line, err_code is not null.
4155
4156 // We are in thread U != W.
4157
4158 /* This is actually pretty simple. All we want to do is connect(), which is non-blocking, and
4159 * then block until the connection is ready (at least according to our side). Ready means that
4160 * the socket is Writable (since user has no access to the socket yet, nothing can be loading
4161 * data onto the Send buffer, and obviously the congestion window is clear, so it must be
4162 * Writable). Note that, like BSD sockets, we specifically don't consider a socket Writable
4163 * until in ESTABLISHED internal state. */
4164
4165 /* For the "block until Writable" part, create and load the Event_set. Do this before connect(),
4166 * so that if it fails we don't have to then clean up the socket before returning error to user. */
4167
4168 const Event_set::Ptr event_set = event_set_create(err_code);
4169 if (!event_set)
4170 {
4171 assert(*err_code == error::Code::S_NODE_NOT_RUNNING);
4172 return Peer_socket::Ptr{}; // *err_code is set.
4173 }
4174 // Now we know Node is running(); and we have event_set.
4175
4176 // We must clean up event_set at any return point below.
4177 Error_code dummy_prevents_throw;
4178 const auto event_set_cleanup = util::setup_auto_cleanup([&]()
4179 {
4180 // Eat any error when closing Event_set, as it's unlikely and not interesting to user.
4181 event_set->close(&dummy_prevents_throw);
4182 });
4183
4184 const auto sock = connect_with_metadata(to, serialized_metadata, err_code, sock_opts);
4185 if (!sock)
4186 {
4187 return sock; // *err_code is set. It's probably some user error like an invalid destination.
4188 }
4189 // else we have a socket that has started connecting.
4190
4191 /* We must clean up sock (call sock->close_abruptly(&dummy_prevents_throw)) at any return point (including
4192 * exception throw) below, EXCEPT the success case. Because of the latter, we can't use the
4193 * auto_cleanup trick we used on event_set. So, we'll just have to handle sock cleanup
4194 * manually. */
4195
4196 // Add the one event about which we care.
4197 bool result = event_set->add_wanted_socket<Peer_socket>(sock, Event_set::Event_type::S_PEER_SOCKET_WRITABLE,
4198 &dummy_prevents_throw);
4199 assert(result); // Node is running, so there's no way that should have failed.
4200
4201 // Wait for Writable.
4202 result = event_set->sync_wait(max_wait, err_code);
4203 if (!result)
4204 {
4205 if (*err_code == error::Code::S_EVENT_SET_CLOSED)
4206 {
4207 // It's unlikely, but I guess someone could have destroyed Node during the wait (we do allow that during sleep).
4209 }
4210 else
4211 {
4212 // This is quite common and is analogous to POSIX's EINTR semantics (signal interrupted the blocking call).
4213 assert(*err_code == error::Code::S_WAIT_INTERRUPTED);
4214 }
4215
4216 // Clean up (as discussed above).
4217 sock->close_abruptly(&dummy_prevents_throw); // Eat any error; user doesn't care.
4218 return Peer_socket::Ptr{}; // *err_code is set.
4219 } // if (sync_wait() failed)
4220 // else we know event_set is still open, and sync_wait() succeeded.
4221
4222 // OK; either that returned 1 event, or 0 events (timeout).
4223 const bool ready = event_set->events_detected(err_code);
4224 /* Node had not been destroyed by the time sync_wait() finished, and we don't allow simultaneous
4225 * ~Node() outside a blocking sleep (see notes in class Node doc header). The only way this
4226 * failed is if Event_set was closed, and that could only happen if Node was destroyed. */
4227 assert(!*err_code);
4228
4229 if (ready)
4230 {
4231 /* Didn't time out; socket is Writable. However, that does not mean it's Writable for "good"
4232 * reasons. If an error was encountered since the original non-blocking connect (e.g., RST
4233 * received; or handshake timeout expired), then it is now Writable, but any operation like
4234 * send() or receive() will immediately yield an error. If that is the case,
4235 * close_connection_immediately() has set user-visible state to S_CLOSED. So let's check for
4236 * it and return an error in that case.
4237 *
4238 * We could also not; pretend socket is ready and let user discover error when trying to
4239 * transmit. However it seems like a good property to help him out. */
4240
4241 if (sock->state() == Peer_socket::State::S_CLOSED)
4242 {
4243 // No need to cleanup socket; it is already closed.
4244
4245 // Return error as above.
4246 *err_code = sock->m_disconnect_cause; // No need to lock; m_disconnect_cause set and can't change later.
4247 return Peer_socket::Ptr{};
4248 }
4249 // else it's probably really ready for action.
4250
4251 return sock; // *err_code is success.
4252 }
4253 // else
4254
4255 // Timed out! Clean up socket, as above, and return null with a specific error (as advertised).
4256 sock->close_abruptly(&dummy_prevents_throw);
4258 return Peer_socket::Ptr{};
4259} // Node::sync_connect_impl()
4260
4261void Node::setup_connection_timers(const Socket_id& socket_id, Peer_socket::Ptr sock, bool initial)
4262{
4265 using boost::chrono::microseconds;
4266 using boost::chrono::duration_cast;
4267 using boost::weak_ptr;
4268
4269 // We are in thread W.
4270
4271 Fine_duration rexmit_from_now = sock->opt(sock->m_opts.m_st_connect_retransmit_period);
4272
4273 // Finalize the retransmit scheduled task firing time; and update the # retries statistic.
4274 if (!initial)
4275 {
4276 assert(scheduled_task_fired(get_logger(), sock->m_init_rexmit_scheduled_task));
4277
4278 ++sock->m_init_rexmit_count;
4279 /* This is a bit more precise than leaving rexmit_from_now alone, as it counts from when firing was
4280 * actually scheduled, vs. when the timer was actually triggered by boost.asio. The 2nd addend should be a bit
4281 * negative and thus decrease rexmit_from_now a bit. */
4282 rexmit_from_now += scheduled_task_fires_from_now_or_canceled(get_logger(), sock->m_init_rexmit_scheduled_task);
4283 /* @todo RFC 6298 mandates that this must be doubled after each attempt instead of keeping
4284 * the same value. Doesn't mean we should follow it. */
4285 }
4286
4287 // Firing time is set; start timer. Call that body when task fires, unless it is first canceled.
4288 sock->m_init_rexmit_scheduled_task
4289 = schedule_task_from_now(get_logger(), rexmit_from_now, true, &m_task_engine,
4290 [this, socket_id,
4291 sock_observer = weak_ptr<Peer_socket>(sock)]
4292 (bool)
4293 {
4294 auto sock = sock_observer.lock();
4295 if (sock)
4296 {
4297 handle_connection_rexmit_timer_event(socket_id, sock);
4298 }
4299 // else { Possible or not, allow for this possibility for maintainability. }
4300 });
4301
4302 // Also set up the timeout that will stop these retries from happening.
4303 if (initial)
4304 {
4305 sock->m_connection_timeout_scheduled_task
4307 sock->opt(sock->m_opts.m_st_connect_retransmit_timeout),
4308 true, &m_task_engine,
4309 [this, socket_id,
4310 sock_observer = weak_ptr<Peer_socket>(sock)]
4311 (bool)
4312 {
4313 // We are in thread W.
4314
4315 auto sock = sock_observer.lock();
4316 if (!sock)
4317 {
4318 return; // Possible or not, allow for this possibility for maintainability.
4319 }
4320 // else
4321
4322 FLOW_LOG_INFO("Connection handshake timeout timer [" << sock << "] has been triggered; was on "
4323 "attempt [" << (sock->m_init_rexmit_count + 1) << "].");
4324
4325 assert((sock->m_int_state == Peer_socket::Int_state::S_SYN_SENT)
4326 || (sock->m_int_state != Peer_socket::Int_state::S_SYN_RCVD));
4327
4328 // Timeout. Give up. Send RST, in case they do come to their senses -- but it's too late for us.
4329
4330 /* Close connection in our structures and inform user. Pre-conditions
4331 * assumed by call: sock in m_socks and sock->state() == S_OPEN (yes, since m_int_state ==
4332 * S_SYN_SENT/RCVD); err_code contains the reason for the close (yes). */
4333 rst_and_close_connection_immediately(socket_id, sock, error::Code::S_CONN_TIMEOUT, false);
4334 /* ^-- defer_delta_check == false: for similar reason as when calling send_worker() from
4335 * send_worker_check_state(). */
4336 });
4337 } // if (initial)
4338} // Node::setup_connection_timers()
4339
4341{
4342 using util::Blob;
4343
4344 // We are in thread W.
4345
4346 assert((sock->m_int_state == Peer_socket::Int_state::S_SYN_SENT)
4347 || (sock->m_int_state == Peer_socket::Int_state::S_SYN_RCVD));
4348
4349 // Not an error (so not WARNING), but it's rare and interesting enough for INFO.
4350 FLOW_LOG_INFO("Connection handshake retransmit timer [" << sock << "] triggered; was on "
4351 "attempt [" << (sock->m_init_rexmit_count + 1) << "].");
4352
4353 // Try again. Reproduce the SYN or SYN_ACK... but first set up the next timer.
4354
4355 // Setup the next timer before sending packet for the same reason as in the original SYN/SYN_ACK-sending code.
4356 setup_connection_timers(socket_id, sock, false);
4357
4358 /* Send packet.
4359 * @todo More code reuse? Or save the serialized version inside socket and resend here verbatim? */
4360
4361 Low_lvl_packet::Ptr re_syn_base;
4362 if (sock->m_active_connect)
4363 {
4364 auto syn = create_syn(sock);
4365 re_syn_base = Low_lvl_packet::ptr_cast(syn);
4366 }
4367 else
4368 {
4369 // (Subtlety: As of this writing it wouldn't have changed since original SYN_ACK, but safe>sorry.)
4370 sock->m_rcv_last_sent_rcv_wnd = sock_rcv_wnd(sock);
4371
4372 auto syn_ack = create_syn_ack(sock);
4373 re_syn_base = Low_lvl_packet::ptr_cast(syn_ack);
4374 }
4375
4376 // Fill out common fields and asynchronously send packet.
4377 async_sock_low_lvl_packet_send_paced(sock, std::move(re_syn_base));
4378} // Node::handle_connection_rexmit_timer_event()
4379
4381{
4384
4385 // We are in thread W.
4386
4387 /* Cancel any timers. Note that this will NOT prevent a given timer's handler from running.
4388 * It will try to make it run ASAP with operation_aborted error code. However, it may not even
4389 * succeed in that. In particular, if by the time the current handler started the timer handler
4390 * event was already queued inside m_task_engine, then canceling the timer now will not load
4391 * operation_aborted into the handler call; it will instead fire as if the timer really expired
4392 * (which it did). Therefore the timer handler should be careful to check the state of the socket
4393 * and exit if the state is not suitable (in this case, S_CLOSED).
4394 *
4395 * Even so, try to cancel with operation_aborted just to cut down on entropy a bit (at least by
4396 * executing all handlers ASAP).
4397 *
4398 * Update: However, scheduled_task_cancel() will indeed cleanly cancel. `Timer`s are still in direct use
4399 * as well however, so the above still applies to some of the below. */
4400
4401 sock->m_rcv_delayed_ack_timer.cancel();
4402 sock->m_snd_pacing_data.m_slice_timer.cancel();
4403
4404 if (sock->m_init_rexmit_scheduled_task)
4405 {
4406 scheduled_task_cancel(get_logger(), sock->m_init_rexmit_scheduled_task);
4407 sock->m_init_rexmit_scheduled_task = Scheduled_task_handle{};
4408 }
4409 if (sock->m_connection_timeout_scheduled_task)
4410 {
4411 scheduled_task_cancel(get_logger(), sock->m_connection_timeout_scheduled_task);
4412 sock->m_connection_timeout_scheduled_task = Scheduled_task_handle{};
4413 }
4414 if (sock->m_rcv_in_rcv_wnd_recovery)
4415 {
4416 scheduled_task_cancel(get_logger(), sock->m_rcv_wnd_recovery_scheduled_task);
4417 sock->m_rcv_in_rcv_wnd_recovery = false;
4418 }
4419
4420 if (sock->m_snd_drop_timer)
4421 {
4422 // This Drop_timer guy actually will prevent any callbacks from firing.
4423 sock->m_snd_drop_timer->done();
4424
4425 /* The two `shared_ptr`s (sock and m_snd_drop_timer) point to each other. Nullify this to break the cycle
4426 * and thus avoid memory leak. */
4427 sock->m_snd_drop_timer.reset();
4428 }
4429}
4430
4432{
4433 sock->m_snd_drop_timeout = sock->opt(sock->m_opts.m_st_init_drop_timeout);
4434
4435 const auto on_fail = [this, socket_id, sock](const Error_code& err_code)
4436 {
4437 rst_and_close_connection_immediately(socket_id, sock, err_code, false);
4438 // ^-- defer_delta_check == false: for similar reason as when calling send_worker() from send_worker_check_state().
4439 };
4440 const auto on_timer = [this, socket_id, sock](bool drop_all_packets)
4441 {
4442 drop_timer_action(sock, drop_all_packets);
4443 };
4444
4445 /* Set up the Drop Timer. Basically give it some key fields of sock (DTO value, the In-flight
4446 * queue) and the callbacks to call when events occur, such as the Drop Timer expiring.
4447 * Additionally, when events m_snd_drop_timer wants to know about happen, we will call
4448 * m_snd_drop_timer->on_...(). */
4449 sock->m_snd_drop_timer = Drop_timer::create_drop_timer(get_logger(), &m_task_engine, &sock->m_snd_drop_timeout,
4450 Peer_socket::Ptr{sock}, on_fail, on_timer);
4451}
4452
4454 const Function<size_t (size_t max_data_size)>& snd_buf_feed_func,
4455 Error_code* err_code)
4456{
4457 using boost::asio::post;
4458
4459 /* We are in user thread U != W.
4460 * It's important to keep that in mind in this method. In particular, it is absolutely unsafe to
4461 * access m_int_state, which belongs solely to thread W and is never locked. */
4462
4463 // IMPORTANT: The logic here must be consistent with sock_is_writable().
4464
4465 if (!running())
4466 {
4468 return 0;
4469 }
4470 // else
4471
4472 // Pre-condition is that m_mutex is locked already. So EVERYTHING that can be locked, is, including the buffers.
4473
4474 // Pre-condition.
4475 assert(sock->m_state == Peer_socket::State::S_OPEN); // Locked.
4476
4477 if (sock->m_disconnect_cause) // Locked.
4478 {
4479 // Error has been recorded, and we're not CLOSED => we are DISCONNECTING.
4480 assert(sock->m_open_sub_state == Peer_socket::Open_sub_state::S_DISCONNECTING);
4481
4482 /* Disconnection is underway. Adding more data to the Send buffer is pointless; we
4483 * don't allow more data to be queued to be sent after an error (though existing buffered data
4484 * may yet be sent... but that's not relevant here). @todo No graceful close yet. */
4485
4486 // Mark in *err_code and log.
4487 FLOW_ERROR_EMIT_ERROR_LOG_INFO(sock->m_disconnect_cause);
4488 return 0;
4489 }
4490 // else
4491
4492 // No fatal error (socket not disconnecing or closed). However it may still be connecting.
4493
4494 if (sock->m_open_sub_state == Peer_socket::Open_sub_state::S_CONNECTING)
4495 {
4496 /* Here we draw a line in the sand and refuse to buffer any data. We could easily allow
4497 * buffering data even when still S_CONNECTING. However, I am copying BSD socket semantics
4498 * here, as they do seem to be useful. As a user I don't want to think I've "sent" gobs of data
4499 * while there's little to suggest that there's even anyone listening on the other side. */
4500 err_code->clear();
4501 return 0;
4502 }
4503 // else
4504 assert(sock->m_open_sub_state == Peer_socket::Open_sub_state::S_CONNECTED);
4505
4506 const bool was_deqable = snd_deqable(sock); // See below.
4507
4508 /* Write the user-provided data into m_snd_buf; provide the missing argument (max_data_size).
4509 * Round up to a multiple of max-block-size to ensure we never fragment a max-block-size-sized
4510 * chunk of data when they're using unreliable mode! */
4511 const size_t sent = snd_buf_feed_func(sock->max_block_size_multiple(sock->m_opts.m_st_snd_buf_max_size));
4512
4513 // Register that the Send buffer possibly grew.
4514 sock->m_snd_stats.buffer_fed(sock->m_snd_buf.data_size());
4515
4516 /* We've done the minimal thing send() does: added data to the send buffer. Now we may need to
4517 * kick off the actual asynchronous sending of some of these data by thread W. It's important to
4518 * discuss the overall strategy for how that works.
4519 *
4520 * Key question: how does W send low-level packets over UDP? Answer: if there's anything on the
4521 * Send buffer or retransmission queue (if retransmission is enabled), and there is no other
4522 * (congestion control, probably) reason NOT to send packets, then dequeue a packet from
4523 * retransmission queue or Send buffer and send it off to the UDP layer; repeat in a tight loop
4524 * until both Send queues are empty, or there's some reason NOT to send packets (again, congestion
4525 * control). Let's write this in pseudo-code:
4526 *
4527 * DEQ(sock): // Thread W only.
4528 * if (!sendable(sock)):
4529 * return // Slight optimization; perform this first check before locking.
4530 * lock sock // Must lock because sock->m_snd_buf accessible from other threads.
4531 * while (sendable(sock) && deqable(sock)):
4532 * dequeue sock->m_snd_buf -> block
4533 * serialize block into packet
4534 * send packet via UDP
4535 * unlock sock
4536 *
4537 * sendable(sock):
4538 * return <...probably some congestion control condition involving CWND or something>
4539 *
4540 * deqable(sock):
4541 * return !(sock->m_rexmit_q.empty() && sock->m_snd_buf.empty())
4542 *
4543 * When should DEQ(sock) execute? Answer: whenever sendable(sock) and deqable(sock) are true. If
4544 * they're true, but DEQ(sock) doesn't run for time period P, then it's practically like adding
4545 * sleep(P) from the user's point of view. So how do we get DEQ(sock) to execute as soon as those
4546 * conditions are true? Well, running it repeatedly in a thread W tight loop would do it, but
4547 * obviously that's unacceptable.
4548 *
4549 * So consider the initial state after sock enters ESTABLISHED state. sendable(sock) is true;
4550 * deqable(sock) is false. The moment deqable(sock) becomes true, we should execute DEQ(sock); in
4551 * other words in the first sock->send(), as that will add to m_snd_buf. After DEQ(sock) exits,
4552 * there's no need to call DEQ(sock) until again both conditions are true. Therefore, the
4553 * algorithm is: whenever sendable(sock) goes from false to true, and/or deqable(sock) from false
4554 * to true, call DEQ(sock). If inside DEQ(sock) one of the conditions is still false, it will
4555 * quickly return. (Call the latter a NOOP.)
4556 *
4557 * Now we must come up with a scheme that will ensure DEQ(sock) will run very quickly after either
4558 * condition (sendable(sock), deqable(sock)) becomes true; and that will not peg the CPU.
4559 *
4560 * Consider sendable(). Only thread W (transport layer) can determine this value: it depends on
4561 * wholly internal details like packets in-flight and CWND. Therefore sendable(sock) can go
4562 * false->true only in W. Hence W, whenever changing any component that might affect
4563 * sendable(sock) would do:
4564 *
4565 * // ... Something related to sendable(sock) has changed....
4566 * DEQ(sock) // So check and send if possible.
4567 *
4568 * Clearly this calls DEQ(sock) as soon as humanly possible after sendable(sock) becomes true.
4569 * Clearly it wastes no CPU cycles either. OK.
4570 *
4571 * Now consider deqable(). sock->m_snd_buf can only change from empty to non-empty in the
4572 * previous statement (snd_buf_feed_func()). That is in thread U != W. Suppose we write:
4573 *
4574 * SEND(sock, blocks): // Non-W threads only.
4575 * lock sock // Must lock because sock->m_snd_buf accessible from other threads.
4576 * add blocks -> sock->m_snd_buf
4577 * if (sock->m_snd_buf was empty before previous statement)
4578 * // Queue DEQ(sock) for asynchronous execution on thread W as soon as it's free:
4579 * post(W, DEQ(sock))
4580 * unlock sock
4581 *
4582 * Does this call DEQ(sock) as soon as deqable(sock) becomes true? Well, DEQ(sock) can only run
4583 * on thread W, and the enqueuing of blocks can only happen on thread U, and post() will cause
4584 * DEQ(sock) to run as soon as possible. Therefore that's as good as it can be. Is it correct,
4585 * however? The mainstream case is that once "unlock sock" finished in SEND(), thread W will get
4586 * some free time, execute the just-queued DEQ(), and thus everything works out. OK so far.
4587 *
4588 * Since, however, post() is (obviously) asynchronous and done from thread non-W, there is
4589 * potential for other tomfoolery. First consider competing SEND() calls from other threads.
4590 * Because of locking, they will be entirely sequential even from different threads and thus can
4591 * be considered as all in one thread U != W. Now suppose SEND() placed DEQ() onto W, and another
4592 * SEND() executes before DEQ() executes on W. No problem: since only DEQ() can dequeue the Send
4593 * buffer, and the 1st SEND() made the buffer non-empty, the 2nd SEND() will not affect the DEQ()
4594 * situation, since it cannot make m_snd_buf become non-empty after being empty (was already
4595 * non-empty).
4596 *
4597 * Second consider SEND(sock, blocks) executing while a W handler is executing. Now suppose this
4598 * W handler discovers that sendable() may be affected and thus calls DEQ(sock) as shown above;
4599 * meanwhile SEND() posts DEQ(sock) onto W as well. W will wait until SEND(sock, blocks) exits
4600 * (due to the lock) before executing most of DEQ(sock), but when it does it will be ITS DEQ(sock)
4601 * that executes first (regardless of whether the post from thread U happened first). This
4602 * DEQ(sock) will not be a NOOP, which is great. Now, thread W should exit that handler and
4603 * finally execute SEND()'s posted DEQ() -- which will be a NOOP, because the synchronous
4604 * DEQ(sock) from thread W preempted it.
4605 *
4606 * Is this OK? Most likely. It'll spend some extra CPU cycles on the check in the NOOP, but
4607 * that's it. Now, there is some conceivable way that, maybe, such NOOPs could happen a lot in a
4608 * very busy system and perhaps even "bunch" up to peg the CPU. However, after doing many thought
4609 * experiments, I unable to come up with anything actually worrying.
4610 *
4611 * The other way deqable(sock) can become true is if m_rexmit_q was empty but becomes non-empty.
4612 * In other words, if we detect packet as Dropped, we will have added it (if retransmission is on)
4613 * to m_rexmit_q. This can only happen on thread W and thus is handled similarly to
4614 * sendable(sock):
4615 *
4616 * // ... Something related to deqable(sock) has changed....
4617 * DEQ(sock) // So check and send if possible.
4618 *
4619 * So this system should be OK. Now let's map the above pseudocode to actual code.
4620 *
4621 * SEND(sock, blocks) is the very method you're reading now (Peer_socket::send() and
4622 * Node::send(), runs in thread U != W). DEQ(sock) is Node::send_worker(sock) (runs in thread
4623 * W). sendable(sock) is Node::can_send(sock). deqable(sock) is Node::snd_deqable(sock).
4624 * post(W, f) is post(Node::m_task_engine, f).
4625 *
4626 * OK, there is one more small caveat. If DEQ(sock) is placed onto W by SEND(sock, blocks),
4627 * then before this DEQ() is executed, thread W may change the state of sock (for example, close
4628 * it). Therefore, DEQ() must also ensure it's operating in a state where it can send data
4629 * (ESTABLISHED at least), and if not, NOOP. Of course if DEQ() is executed synchronously by W,
4630 * then this is unnecessary (since W code wouldn't execute DEQ() directly unless already in a
4631 * proper state for this). So, send_worker_check_state() is actually a little bit more than just
4632 * DEQ(), while send_worker() is just DEQ(). send() posts send_worker_check_state(), while
4633 * thread W executes send_worker() directly. */
4634
4635 if ((!was_deqable) && (sent != 0))
4636 {
4637 // Possibly send_worker() can send packets now (send buffer went from empty to not).
4638 post(m_task_engine, [this, sock]() { send_worker_check_state(sock); });
4639 }
4640
4641 err_code->clear();
4642 return sent;
4643 // Note that sock->m_mutex is unlocked here (and send_worker() will lock it again when it [probably soon] executes).
4644} // Node::send()
4645
4646bool Node::sock_is_writable(const boost::any& sock_as_any) const
4647{
4648 using boost::any_cast;
4649
4650 const Peer_socket::Const_ptr sock = any_cast<Peer_socket::Ptr>(sock_as_any);
4651
4652 Peer_socket::Lock_guard lock{sock->m_mutex}; // Many threads can access/write below state.
4653
4654 /* Our task here is to return true if and only if at this very moment calling sock->send() would
4655 * yield either a return value of > 0 OR a non-success *err_code. In other words, send() would
4656 * return "something." This is used for Event_set machinery.
4657 *
4658 * This should mirror send()'s algorithm. @todo Should send() call this, for code reuse?
4659 * Maybe/maybe not. Consider performance when deciding.
4660 *
4661 * - If state is CLOSED, then some sort of error/terminating condition occurred, so send()
4662 * would return 0 and non-success Error_code == sock->m_disconnect_cause. (Writable.)
4663 * - Otherwise, if state is OPEN+DISCONNECTING, then graceful close (@todo implement it) is
4664 * underway; we do not allow more data to be sent (except what's already in Sent buffer), so
4665 * send() would return 0 and non-success Error_code == sock->m_disconnect_cause.
4666 * (Writable.)
4667 * - Otherwise, if state is OPEN+CONNECTED, and there is Send buffer space, send() would return >
4668 * 0 and no error. (Writable.)
4669 * - The other remaining possibilities:
4670 * - OPEN+CONNECTED but no Send buffer space (returns 0, no error). (Not Writable.)
4671 * - OPEN+CONNECTING -- we don't allow accumulating data in Send buffer (returns 0, no error).
4672 * (Not Writable.) */
4673
4674 return (sock->m_state == Peer_socket::State::S_CLOSED)
4675 || (sock->m_open_sub_state == Peer_socket::Open_sub_state::S_DISCONNECTING)
4676 || ((sock->m_open_sub_state == Peer_socket::Open_sub_state::S_CONNECTED)
4677 && snd_buf_enqable(sock));
4678} // Node::sock_is_writable()
4679
4681{
4682 // See big comment block in Node::send() first.
4683
4684 // We are in thread W.
4685
4686 /* This method can be thought of as the chunk of the finite state machine that defines what
4687 * happens when the "user called send, adding at least 1 block to the send buffer" (@todo: or any data
4688 * at all, if in reliable mode?) event is defined. Therefore, we will have a switch() that will handle every
4689 * state and decide what should happen when that event fires in that state.
4690 *
4691 * send() placed us onto thread W. When send() did so, m_int_state (which it was not allowed to
4692 * check, as only thread W can access it) was at least ESTABLISHED (since state was
4693 * S_OPEN+S_CONNECTED, ensured via assert()). Therefore, we can eliminate several states with
4694 * assert()s: SYN_SENT, SYN_RCVD. */
4695
4696 switch (sock->m_int_state)
4697 {
4699 // Mainstream case.
4700 send_worker(sock, false);
4701 /* ^-- defer_delta_check == false: because we were invoked from thread U != W, we are NOT
4702 * invoked from async_low_lvl_recv(). Therefore, we will NOT perform
4703 * event_set_all_check_delta(false) before the boost.asio handler exits. Therefore boost.asio
4704 * may sleep (block) before event_set_all_check_delta(false). Therefore that would delay
4705 * delivery of the Writable event to the user. Therefore force the delta check immediately.
4706 * See Node::m_sock_events doc header for details. */
4707 break;
4709 // Unlikely but legitimate.
4710 FLOW_LOG_INFO('[' << sock << "] "
4711 "in state [" << sock->m_int_state << "] "
4712 "closed before asynchronous send_worker() could proceed.");
4713 break;
4716 // Crash. See above reasoning.
4717 FLOW_LOG_WARNING('[' << sock << "] "
4718 "in state [" << sock->m_int_state << "] "
4719 "somehow had send() called on it.");
4720 assert(false);
4721 break;
4722 } // switch (sock->m_int_state)
4723} // Node::send_worker_check_state()
4724
4725void Node::send_worker(Peer_socket::Ptr sock, bool defer_delta_check)
4726{
4727 using boost::asio::buffer;
4728 using boost::next;
4729 using boost::ratio;
4730 using boost::ratio_string;
4731 using boost::chrono::milliseconds;
4732 using boost::chrono::round;
4733 using boost::shared_ptr;
4734 using std::list;
4735
4736 // We are in thread W.
4737
4738 // See big comment block in Node::send() first.
4739
4740 // Pre-condition.
4741 assert(sock->m_int_state == Peer_socket::Int_state::S_ESTABLISHED);
4742
4743 /* We are about to potentially send a bunch of DATA packets. Before sending a given packet, we
4744 * will call can_send() which will ask the congestion control module whether there is space in
4745 * what it thinks is the available pipe and return true if so (as well as check rcv_wnd, ensuring
4746 * the receiver's Receive buffer can handle the data once they arrive). However, how it answers
4747 * that question depends on the size of the pipe (m_snd_cong_ctl->congestion_window_bytes(),
4748 * a/k/a CWND). Many (most?) congestion control modules will want to reduce CWND when a
4749 * connection has been idle -- not sending anything, due to no data to be sent in Send buffer --
4750 * for a while. Thus we must call m_snd_cong_ctl->on_idle_timeout() if we've hit Idle Timeout.
4751 *
4752 * The definition of Idle Timeout we use is from TCP RFC 5681-4.1 (and DCCP CCID 2 RFC 4341-5.1).
4753 * It's simple: Idle Timeout is DTO (Drop Timeout) time units since a DATA packet has been last
4754 * sent. While I basically grasp the intuition behind it (if a DTO since even the last-sent
4755 * packet has expired, and no retransmission/further transmission has occurred, then there must
4756 * have been no more data for a while), I can't quite prove to myself that it's exactly right,
4757 * mostly due to the fact that DTO may change over time. It's probably right though, as RFC 4341
4758 * recommends it, even though that protocol is closer to NetFlow than TCP (full selective ACKs).
4759 * Anyway, if we see too many false Idle timeouts, revisit this.
4760 *
4761 * Why check this now? Why not start a proper timer, each time packet is sent, instead and just
4762 * inform m_snd_cong_ctl when it fires? Answer: timer management is somewhat of a pain in the ass
4763 * (as you can see in our other various timers, such as m_snd_drop_timer). Here we have an opportunity
4764 * to simply check the condition and affect CWND right before CWND would be used anyway
4765 * (can_send()). It's simpler, and the performance impact is negligible (it's just a
4766 * Fine_clock::now() call and a comparison). You ask, why not do the same for other timers
4767 * then, in particular the Drop Timer? Answer: for Drop Timer, we really need to know exactly
4768 * when it fires, so that we can Drop In-flight packets right then and possibly send more
4769 * packets (among other things). In this case there is no such requirement; we only care about
4770 * whether the Idle Timeout has tripped when we're about to send something. */
4771
4772 /* To avoid a very close race between DTO and idle timeout, apply a slight factor of > 1 to DTO.
4773 * Using boost::ratio<> instead of a double or something for same reason as in
4774 * new_round_trip_time_sample(). */
4775 using Idle_timeout_dto_factor = ratio<110, 100>;
4776 const Fine_duration idle_timeout
4777 = sock->m_snd_drop_timeout * Idle_timeout_dto_factor::num / Idle_timeout_dto_factor::den;
4778 const Fine_duration since_last_send = Fine_clock::now() - sock->m_snd_last_data_sent_when;
4779
4780 if ((sock->m_snd_last_data_sent_when != Fine_time_pt{}) && (since_last_send > idle_timeout))
4781 {
4782 // Arguable if this should be INFO or TRACE. We'll see.
4783 FLOW_LOG_INFO("Idle timeout triggered for [" << sock << "]; "
4784 "last activity [" << round<milliseconds>(since_last_send) << "] ago "
4785 "exceeds idle timeout [" << round<milliseconds>(idle_timeout) << "] "
4786 "= " << (ratio_string<Idle_timeout_dto_factor, char>::prefix()) << " x "
4787 "[" << round<milliseconds>(sock->m_snd_drop_timeout) << "].");
4788 sock->m_snd_cong_ctl->on_idle_timeout();
4789 sock->m_snd_stats.idle_timeout();
4790 }
4791
4792 /* Check networking conditions (presumably congestion control) and flow control (rcv_wnd).
4793 * Ideally this would always be true, but then we'd overwhelm the link when send() is invoked on
4794 * large amounts of data and/or repeatedly. */
4795 if (!can_send(sock))
4796 {
4797 FLOW_LOG_TRACE('[' << sock << "]: "
4798 "Initial check: can_send() is false.");
4799 return;
4800 }
4801 // else can send if there are data to send.
4802
4803 /* Didn't lock sock above, as can_send() depends only on internal state, which is accessed from
4804 * thread W only. This is an optimization to avoid thread contention (with non-W send()s) for the
4805 * lock in the case when congestion control is preventing sends.
4806 *
4807 * Have to lock now, for sock->m_snd_buf access (at least). */
4808
4809 const bool rexmit_on = sock->rexmit_on();
4810 bool writable; // See below.
4811 {
4812 Peer_socket::Lock_guard lock{sock->m_mutex};
4813
4814 // Check whether enough data in retransmission queue or snd_buf to send a packet.
4815 if (!snd_deqable(sock))
4816 {
4817 FLOW_LOG_TRACE('[' << sock << "]: "
4818 "Initial check: can_send() is true, but no data to send.");
4819 return;
4820 }
4821 // else can send >= 1 packet.
4822
4823 // For brevity and a bit of speed:
4824 Socket_buffer& snd_buf = sock->m_snd_buf;
4825 list<Peer_socket::Sent_packet::Ptr>& rexmit_q = sock->m_snd_rexmit_q;
4826 size_t& rexmit_q_size = sock->m_snd_rexmit_q_size;
4827 Sequence_number& snd_next_seq_num = sock->m_snd_next_seq_num;
4828
4829 // @todo Implement graceful close.
4830 assert(sock->m_open_sub_state != Peer_socket::Open_sub_state::S_DISCONNECTING);
4831
4832 FLOW_LOG_TRACE('[' << sock << "]: "
4833 "Initial check: Will send from rexmit queue of size [" << rexmit_q_size << "] and/or "
4834 "Send buffer with total size [" << snd_buf.data_size() << "].");
4835 // Very verbose and CPU-intensive!
4836 FLOW_LOG_DATA("Send buffer data = [\n" << snd_buf << "].");
4837
4838 // Send packets until one or both of can_send() and snd_deqable() become false.
4839 do
4840 {
4841 shared_ptr<Data_packet> data;
4843 bool rexmit = false;
4844
4845 /* Record send time. It's only a temporary value for logging, until we
4846 * actually send packet. However, do generate the permanent m_order_num, which is unique. */
4847 Peer_socket::Sent_packet::Sent_when sent_when{ sock_get_new_snd_order_num(sock), Fine_clock::now(), 0 };
4848
4849 /* To provide the best experience on the receiving side, retransmit before sending new data,
4850 * so that Receive buffer on other side receives data as soon as possible. */
4851 if (rexmit_q.empty())
4852 {
4853 // Nothing in retransmission queue, so something is in Send buffer.
4854
4855 // Create low-level DATA packet.
4856 data = Low_lvl_packet::create_uninit_packet<Data_packet>(get_logger());
4857 data->m_rexmit_id = 0; // First (if retransmission is off, only) send attempt.
4858
4859 // Dequeue one block into the packet's data field.
4860
4861 /* Try to dequeue the head block directly into data.m_data. Because we are operating snd_buf
4862 * with block_size_hint == sock->max_block_size(); and because we don't send unless CWND
4863 * allows for at least max_block_size() bytes to be sent, the following should be a
4864 * constant-time operation (a swap of internal buffers) as opposed to a copy. */
4865 snd_buf.consume_buf_move(&data->m_data, sock->max_block_size());
4866
4867 // snd_deqable() returned true, so there must be at least one byte available.
4868 assert(!data->m_data.empty());
4869
4870 // Set sequence number; then advance the next sequence number variable for the next time we do this.
4871 data->m_seq_num = snd_next_seq_num;
4872 advance_seq_num(&snd_next_seq_num, data);
4873
4874 /* We are just about to send the packet. Assume it has been sent. It is not yet Acknowledged
4875 * and not yet Dropped. Therefore it is now In-flight. We should place its info at the back of
4876 * m_snd_flying_pkts_by_sent_when. We must maintain the invariant w/r/t that structure (see comment
4877 * for m_snd_flying_pkts_by_sent_when).
4878 *
4879 * Purpose of keeping these data: at least for comparison against Congestion Window,
4880 * for congestion control. */
4881
4882 // Guarantee that the new sequence number is > all the currently In-flight ones.
4883 assert(data->m_seq_num >= snd_past_last_flying_datum_seq_num(sock));
4884 /* Therefore we will add the following to the end of the map's ordering. Note we've
4885 * incremented m_snd_next_seq_num already, maintaining that member's invariant relationship
4886 * with m_snd_flying_pkts_by_sent_when. */
4887
4888 // New packet: create new metadata object. Record send time. (The latter will be rewritten later.)
4889 sent_pkt = Peer_socket::Sent_packet::Ptr{new Peer_socket::Sent_packet{rexmit_on, data, sent_when}};
4890 }
4891 else // if (!rexmit_q.empty())
4892 {
4893 // Get packet and metadata from front of retransmission queue.
4894 rexmit = true;
4895 sent_pkt = rexmit_q.front();
4896
4897 --rexmit_q_size;
4898 rexmit_q.pop_front();
4899
4900 // We'd saved the packet we sent last time -- just need to update some things before resending.
4901 data = sent_pkt->m_packet;
4902
4903 // Retransmitting -- update retransmit count ID (used to match acks to the acked transmit attempt).
4904 ++data->m_rexmit_id;
4905
4906 // Record the send time of this newest attempt. (If pacing enabled this will be rewritten later.)
4907 sent_pkt->m_sent_when.push_back(sent_when);
4908
4909 // Chronologically, no packets sent after this one have been acked yet, as this packet is new.
4910 sent_pkt->m_acks_after_me = 0;
4911 }
4912
4913 /* Note: We have saved Fine_clock::now() as the send time of the packet. However, especially
4914 * if pacing is enabled, we want to record it at the time it is actually sent (pacing may
4915 * delay it). Even if pacing is disabled, CPU pegging may cause a delay in sending (although
4916 * whether that should "count" is a more philosophical question). With pacing, though, since
4917 * pacing spreads out packets over SRTT, and SRTT is measured based on
4918 * Sent_packet::m_sent_when, RTTs artifically become longer and longer if we record the send
4919 * time now. Anyway, this means m_sent_when.back() should be overwritten when the packet is
4920 * actually sent (which should be very soon, unless pacing is enabled).
4921 * See async_sock_low_lvl_packet_send_paced(). */
4922
4923 // data and sent_pkt are ready.
4924
4925 // Add to snd_flying_pkts* and friends; update byte counts.
4926 snd_flying_pkts_push_one(sock, data->m_seq_num, sent_pkt);
4927
4928 /* By adding to m_snd_flying_pkts_by_sent_when (i.e., increasing In-flight byte count), we may have
4929 * affected the result of can_send(). We do check it at the end of the while () body, so OK. */
4930
4931 // Fill out common fields and asynchronously send packet (packet pacing potentially performed inside).
4933
4934 sock->m_snd_stats.data_sent(data->m_data.size(), rexmit);
4935 }
4936 while (can_send(sock) && snd_deqable(sock)); // (there is CWND/rcv_wnd space; and either rexmittable or new data)
4937
4938 FLOW_LOG_TRACE('[' << sock << "]; connection [" << sock << "]: "
4939 "Final check: "
4940 "can_send() == [" << can_send(sock) << "]; "
4941 "snd_deqable() == [" << snd_deqable(sock) << "].");
4942
4943 writable = snd_buf_enqable(sock); // Must do before releasing lock.
4944 } // lock
4945
4946 /* Finally, check if the above has dequeued enough of m_snd_buf for it to accept more data from
4947 * user. If so, sock is certainly now Writable. Therefore we should soon inform anyone waiting
4948 * on any Event_sets for sock to become Writable.
4949 *
4950 * Caveat: Similar to that in Node::handle_syn_ack_ack_to_syn_rcvd() at similar point in the
4951 * code.
4952 *
4953 * Also: why do this outside the above locked block? Same reason as similar code in
4954 * handle_data_to_established(). */
4955 if (writable &&
4957 {
4958 // Possibly inform the user for any applicable Event_sets right now.
4959 event_set_all_check_delta(defer_delta_check);
4960 }
4961
4962 /* @todo After we implement graceful close, if we'd emptied m_snd_buf above, then here we should
4963 * advance the graceful close towards the final situation (m_int_state and m_state both
4964 * S_CLOSED). */
4965} // Node::send_worker()
4966
4968{
4969 using std::min;
4970
4971 /* m_snd_cong_ctl is the congestion control module, and its CWND value determines how many bytes can
4972 * be In-flight at any given time. If there are enough free bytes (CWND - In-flight) to send
4973 * data, then we can send. Otherwise we cannot. Easy, except what's "data"? There are two
4974 * reasonable answers. One: a byte or more. Two: min(max-block-size, Send buffer size). The former
4975 * answer is fine but somewhat annoying, because then we have to lock sock here***. The 2nd answer
4976 * clearly works but is potentially a little greedier than necessary (i.e., if the 1st block to
4977 * send is small enough to fit into CWND, but CWND doesn't have max-block-size space).
4978 * However, actually, we pretty much have to choose the 2nd answer regardless, as we don't want to
4979 * fragment max-block-size-sized chunks, if we can help it (in the spirit of the reliability
4980 * guarantee [when running in unreliable mode] made in send() method doc header).
4981 *
4982 * I choose the 2nd answer, because (1) it's easier (no locking of sock); (2) it is used by real
4983 * TCP implementations which keep CWND in multiples of MSS (equivalent of max-block-size); (3)
4984 * it's still safe; and (4) see previous paragraph's end. Regarding safety: it's safe, since
4985 * there can be no deadlock, because even if there's < MBS bytes free, eventually In-flight
4986 * packets will become Acknowledged or Dropped and no longer be In-flight, freeing up CWND space;
4987 * and CWND is guaranteed to always be at least 1 * MBS. Thus eventually can_send() will return
4988 * true.
4989 *
4990 * *** - I am now not sure why I wrote this. Why would we have to lock sock here in that case? */
4991
4992 // We have rcv_wnd also; so pretend previous paragraph has: s/CWND/min(CWND, rcv_wnd)/.
4993
4994 const size_t pipe_taken = sock->m_snd_flying_bytes;
4995 const size_t cong_wnd = sock->m_snd_cong_ctl->congestion_window_bytes();
4996 const size_t& rcv_wnd = sock->m_snd_remote_rcv_wnd; // @todo Any particular reason this has & but not pipe_taken?
4997 // Send no more than the network NOR the other side's Receive buffer can take.
4998 const size_t pipe_total = min(cong_wnd, rcv_wnd);
4999
5000 const bool can
5001 = (pipe_taken < pipe_total) && ((pipe_total - pipe_taken) >= sock->max_block_size());
5002
5003 FLOW_LOG_TRACE("cong_ctl [" << sock << "] info: can_send = [" << can << "]; "
5004 "pipe_taken = [" << sock->bytes_blocks_str(pipe_taken) << "]; "
5005 "cong_wnd = [" << sock->bytes_blocks_str(cong_wnd) << "]; "
5006 "rcv_wnd = [" << sock->bytes_blocks_str(rcv_wnd) << "].");
5007
5008 return can;
5009} // Node::can_send()
5010
5012 const Function<size_t ()>& rcv_buf_consume_func,
5013 Error_code* err_code)
5014{
5015 using boost::asio::post;
5016
5017 /* We are in user thread U != W.
5018 * It's important to keep that in mind in this method. In particular, it is absolutely unsafe to
5019 * access m_int_state, which belongs solely to thread W and is never locked. */
5020
5021 // IMPORTANT: The logic here must be consistent with sock_is_readable().
5022
5023 if (!running())
5024 {
5026 return 0;
5027 }
5028 // else
5029
5030 // Pre-condition is that m_mutex is locked already. So EVERYTHING that can be locked, is, including the buffers.
5031
5032 // Pre-condition.
5033 assert(sock->m_state == Peer_socket::State::S_OPEN); // Locked.
5034 assert((sock->m_open_sub_state == Peer_socket::Open_sub_state::S_CONNECTED) ||
5035 (sock->m_open_sub_state == Peer_socket::Open_sub_state::S_CONNECTING) ||
5036 (sock->m_open_sub_state == Peer_socket::Open_sub_state::S_DISCONNECTING));
5037
5038 /* In the rest of the method we must ensure we handle all the cases (-1a/b/c-, -2-) documented in
5039 * the Peer_socket::receive() documentation header. -3- was already handled by
5040 * Peer_socket::receive() before calling us. */
5041
5042 // Try to dequeue stuff into their buffer.
5043 const bool no_bytes_available = sock->m_rcv_buf.empty();
5044 const size_t bytes_consumed = rcv_buf_consume_func();
5045
5046 if (bytes_consumed != 0)
5047 {
5048 /* Unequivocal: if there was stuff in the Receive buffer and was able to place it into their
5049 * buffer then there is no error. (Even if m_disconnect_cause is not success, we are only
5050 * supposed to report that after the Receive buffer has been emptied.)
5051 *
5052 * This handles case -2-. */
5053 FLOW_LOG_TRACE("User thread receive() for [" << sock << "] "
5054 "has successfully returned [" << bytes_consumed << "] bytes.");
5055 err_code->clear();
5056
5057 /* We have changed (increased) the amount of free space in m_rcv_buf. This has rcv_wnd
5058 * implications. We have to at least check whether we should send a window update to the
5059 * other side. However all such book-keeping must be done in thread W due to the data
5060 * involved; call this->receive_wnd_updated(sock). */
5061 post(m_task_engine, [this, sock]() { receive_wnd_updated(sock); });
5062
5063 if (sock->m_rcv_buf.empty()
5064 && (sock->m_open_sub_state == Peer_socket::Open_sub_state::S_DISCONNECTING))
5065 {
5066 /* We've emptied the Receive buffer; and we're in the middle of a graceful close. (@todo
5067 * Graceful close not yet implemented.) There are two possibilities. One, m_int_state ==
5068 * S_CLOSED. In this case the graceful close, at the transport layer, is over, and the only
5069 * thing stopping us from entering m_state == S_CLOSED (via close_connection_immediately())
5070 * was that the user hadn't read all of m_rcv_buf. In this case thread W should
5071 * close_connection_immediately(). Two, m_int_state may be after ESTABLISHED but before
5072 * CLOSED, in which case thread W still has to finish up graceful closing anyway.
5073 *
5074 * We are in thread W and cannot work with m_int_state, so checking it here is not possible.
5075 * Therefore we put this task onto thread W. */
5076 post(m_task_engine,
5077 [this, sock]() { receive_emptied_rcv_buf_while_disconnecting(sock); });
5078 }
5079 return bytes_consumed;
5080 }
5081 // else if (bytes_consumed == 0)
5082
5083 if (sock->m_open_sub_state == Peer_socket::Open_sub_state::S_CONNECTING)
5084 {
5085 /* This is case -1b-. Since we are CONNECTING, no data could have been received yet (simply
5086 * not at that stage of connection opening), so Receive buffer is empty. */
5087 FLOW_LOG_TRACE("User thread receive() for [" << sock << "] "
5088 "has successfully returned no bytes because still not fully connected.");
5089 err_code->clear();
5090 return 0;
5091 }
5092 // else if (state is CONNECTED or DISCONNECTING)
5093
5094 /* We're CONNECTED or DISCONNECTING but could get no bytes. Let's examine each state.
5095 *
5096 * - CONNECTED: Either they provided a zero-sized target buffer (in which case
5097 * !no_bytes_available), or the Receive buffer is simply empty. Thus this is either -1a- or
5098 * -1c- (no_bytes_available determines which).
5099 *
5100 * - DISCONNECTING: Either:
5101 * - the initial block was too large for the max_data_size they provided in their receive()
5102 * call (in which case !no_bytes_available); or
5103 * - they called close_final() (@todo not yet implemented) and thus the Receive buffer was
5104 * cleared at that time, and all incoming data were ignored after that; thus the Receive
5105 * buffer is empty, but a graceful close is still in progress; or
5106 * - they did not call close_final(), but there is a graceful close in progress, and the
5107 * Receive buffer is simply empty.
5108 * Thus this is either -1a- or -1c-. */
5109
5110 if (!no_bytes_available)
5111 {
5112 // This is case -1c-.
5113 FLOW_LOG_TRACE("User thread receive() for [" << sock << "] "
5114 "has data to return, but the provided buffer size is too small.");
5115 err_code->clear();
5116 return 0;
5117 }
5118 // else if (no_bytes_available)
5119
5120 // This is case -1a-.
5121 FLOW_LOG_TRACE("User thread receive() for [" << sock << "] "
5122 "returning no data because Receive buffer empty.");
5123
5124 err_code->clear();
5125
5126 /* @todo Sigh. There's more. Yes, in some situations we can return 0/success here. In other
5127 * situations, though, we should return 0/<Error_code for graceful close> here. The latter
5128 * case would be in the situations where we know no data is coming, or user has said he doesn't
5129 * care about any more data:
5130 *
5131 * -1- A graceful close was initiated by the OTHER side. (Therefore no data could be coming to
5132 * save into Receive buffer.)
5133 * -2- Only we initiated the graceful close, but it was via close_final(), i.e., user is not
5134 * interested in any incoming data anymore. (Therefore we'll always just ignore any
5135 * incoming DATA and not put it into Receive buffer.)
5136 * -3- Only we initiated the graceful close, and it was via close_start() (i.e., user cares
5137 * about further incoming data); however, the final handshake has reached a state in which
5138 * further data cannot be incoming. (Therefore no data could be coming to save into Receive
5139 * buffer.)
5140 *
5141 * I am not writing code for this logic at this time. The implementations depends on how
5142 * exactly our graceful close works. This entire method, right now, is dead code, since there is
5143 * no graceful close, but I wrote it anyway to provide a skeleton for the future, since I
5144 * already thought about it. However it would be unreasonable to implement the above logic in the
5145 * absence of graceful close in the first place, skeleton or not. Therefore, dead code or not, I
5146 * do the "conservative" thing: return 0/success even in the above situations. Eventually the
5147 * graceful close will complete, at which point we'll return an error anyway, so the user won't be
5148 * left uninformed forever (worst case: the close will time out).
5149 *
5150 * For when we do implement the above logic, some thoughts: Detecting the situation in thread U
5151 * != W may be difficult and may introduce complex synchronization issues. One way
5152 * to do it might be to introduce synchronized bool Peer_socket::m_no_more_rcv_data, which
5153 * starts at false and can become true (but not false again). This member would be set to true,
5154 * by thread W, if and only if one of the above situations is detected by thread W. Then here
5155 * we'd check it, and if it's true, return error; otherwise return success.
5156 *
5157 * IMPORTANT: The logic here must be consistent with sock_is_readable(). */
5158 return 0;
5159} // Node::receive()
5160
5161bool Node::sock_is_readable(const boost::any& sock_as_any) const
5162{
5163 using boost::any_cast;
5164
5165 const Peer_socket::Const_ptr sock = any_cast<Peer_socket::Ptr>(sock_as_any);
5166
5167 Peer_socket::Lock_guard lock{sock->m_mutex}; // Many threads can access/write below state.
5168
5169 /* Our task here is to return true if and only if at this very moment calling sock->receive(),
5170 * assuming sufficient user buffer space, would yield either a return value of > 0 OR a
5171 * non-success *err_code. In other words, receive() would return "something." This is used for
5172 * Event_set machinery.
5173 *
5174 * This should mirror receive()'s algorithm. @todo Should receive() call this, for code reuse?
5175 * Maybe/maybe not. Consider performance when deciding.
5176 *
5177 * - If state is CLOSED, then some sort of error/terminating condition occurred, so receive()
5178 * would return 0 and non-success Error_code == sock->m_disconnect_cause. (Readable.)
5179 * - Otherwise, if Receive buffer can be dequeued, receive() would return > 0.
5180 * - Otherwise, if Receive buffer cannot be dequeued, receive() would return 0 and no error. (Not
5181 * Readable.) Note that Receive buffer is guaranteed to be clear when entering non-Readable
5182 * non-error states (OPEN+CONNECTING, OPEN+DISCONNECTING). (Readable.)
5183 *
5184 * @todo Once we implement graceful close, there will be situations where Receive buffer is empty, state is
5185 * OPEN+DISCONNECTING, m_disconnect_cause = <cause of disconnect>, and we should return true (Readable)
5186 * here (only when we also know that no future Receive traffic possible). See receive(). */
5187
5188 return (sock->m_state == Peer_socket::State::S_CLOSED) || rcv_buf_deqable(sock);
5189} // Node::sock_is_readable()
5190
5192{
5193 // We are in thread W.
5194
5195 /* rcv_wnd (free Receive buffer space) is sent to other side opportunistically in ACKs. While
5196 * sender is sending data, they will have a good idea of our rcv_wnd as well. Is that (in a
5197 * one-way-traffic situation) sufficient however? If the sender is not sending data, because the
5198 * application on the sender doesn't provide more data to send, then the discussion is moot.
5199 * What if the sender is not sending data, because we have told it rcv_wnd is 0 (meaning our
5200 * Receive buffer is full)? This can and will happen. For example suppose our application layer
5201 * simply stops reading from Receive buffer for a while, resulting in rcv_wnd 0 sent in one of the
5202 * ACKs. Now sender knows rcv_wnd is 0. Now suppose our application reads off the entire Receive
5203 * buffer. rcv_wnd is now 100%, but since sender is not sending (because it thinks rcv_wnd is
5204 * still 0), there will be no ACKs onto which to add rcv_wnd. Thus the traffic completely stops.
5205 *
5206 * Original RFC 793 (as well as RFC 1122) suggests TCP sender should deal with this by "probing"
5207 * with 1-byte (I think) data segments sent regularly (every RTO; our DTO) in order to trigger
5208 * ACKs, which would eventually expose the non-zero rcv_wnd. To me this seems to have the
5209 * disadvantage of complexity and implications on how we packetize data (especially since in
5210 * unreliable mode we're not supposed to break up contiguous blocks of max-block-size bytes).
5211 * Also it is not as responsive as it could be. Consider that the most common scenario in
5212 * high-speed downloads is that the Receive buffer is exceeded only momentarily (due to thread
5213 * contention on receiver or something) but is then quickly emptied (once the thread contention is
5214 * resolved). In case that happens in a fraction of a second, having the probe occur a DTO later
5215 * wastes a long time. Instead the RECEIVER could take initiative and send an empty ACK with a
5216 * rcv_wnd update. When should it do this? A naive answer would be to do it simply EVERY time
5217 * free Receive buffer space increases. However that'd be terrible, as in a typical scenario
5218 * (where lots of bytes arrive, while user reads off lots of bytes due to them becoming available
5219 * to read) it would explode the number of ACKs. Even in the "sender has stopped due to
5220 * rcv_wnd=0" situation, this would result in a ton of ACKs. Plus it would cause sender to start
5221 * recovering with quite small windows which is inefficient. So the less naive way is to send the
5222 * ACK of our volition if free buffer space has increased by some % of its max capacity (like
5223 * 50%).
5224 *
5225 * This would certainly solve aforementioned situation where Receive buffer fills up momentarily
5226 * but then is quickly cleared. A fraction of a second later, the free space will have increased
5227 * by over 50%, an ACK would go to sender, and sender would work with a nice large rcv_wnd.
5228 * However, if the Receiver only reads off 49% of the data and then stops, traffic would remain
5229 * stuck (even though 49% of the buffer is available). This is where the sender-side probing
5230 * would solve it (slowly); though sender-side unsolicited ACKing on a timer would also do. I
5231 * leave that as a @todo; probably important in a widely-used net_flow; but without it it should be
5232 * sufficient for the initial intended purpose of net_flow. In that use scenario, we count on the
5233 * receiver code to be well behaved and read from Receive buffer as soon as the computer lets it.
5234 *
5235 * With that settled, there is one more concern. This is intuitively clear but is also mentioned
5236 * in RFC 1122-4.2.2.17. Suppose the receiver-initiated ACK after 50% of buffer is cleared is
5237 * dropped by the network. ACKs are not reliable (there are no ACKs of ACKs), so then we're back
5238 * in no-more-traffic-forever land. To solve this, I implement this scheme: Having sent that ACK,
5239 * start a timer and then send it again periodically, until some long time period (something like
5240 * a minute) expires (just in case) OR we get a new DATA packet from the sender. In the latter
5241 * case we're back in business, as it implies sender got our window update. Note that this
5242 * mechanism is not necessary any longer, once we implement sender-side probing as explained
5243 * above. */
5244
5245 // As always, no need to lock m_state, etc., unless we plan to alter them, since no other thread can alter them.
5246
5247 if (sock->m_int_state != Peer_socket::Int_state::S_ESTABLISHED)
5248 {
5249 /* Yes, they emptied Receive buffer. However, we haven't finished the graceful close.
5250 * Therefore -- even though one more barrier to reaching m_state == S_CLOSED has been removed --
5251 * there's nothing further to do at this time. In fact, in certain situations we might even
5252 * get more data onto the Receive buffer! @todo No graceful close yet. */
5253 FLOW_LOG_INFO('[' << sock << "] Receive buffer space freed, "
5254 "but state is now [" << sock->m_int_state << "]; ignoring.");
5255 return;
5256 }
5257 // else if (m_int_state == S_ESTABLISHED)
5258
5259 if (sock->m_rcv_in_rcv_wnd_recovery)
5260 {
5261 /* We have already sent the unsolicited ACK and are currently in the phase where we're
5262 * periodically sending more, until we get some DATA from sender or a long period of time
5263 * passes. Even if we've freed yet another large chunk of the buffer since the last ACK, do
5264 * not start again... just let it continue. */
5265 FLOW_LOG_TRACE('[' << sock << "] Receive buffer space freed, but "
5266 "we are already in rcv_wnd recovery mode. Nothing to do.");
5267 return;
5268 }
5269 // else
5270
5271 // Grab available Receive buffer space.
5272 const size_t rcv_wnd = sock_rcv_wnd(sock);
5273 // @todo That was a ~copy/paste of Node::async_low_lvl_ack_send(). Add code reuse.
5274
5275 const size_t& last_rcv_wnd = sock->m_rcv_last_sent_rcv_wnd;
5276
5277 if (rcv_wnd <= last_rcv_wnd)
5278 {
5279 /* This seems odd, but one can imagine more data arriving between when we were placed onto W's
5280 * task queue and when we executed. So it's not that odd and not worth INFO or WARNING. */
5281 FLOW_LOG_TRACE('[' << sock << "] Receive buffer space freed, but "
5282 "free space [" << sock->bytes_blocks_str(rcv_wnd) << "] <= prev "
5283 "free space [" << sock->bytes_blocks_str(last_rcv_wnd) << "]. Nothing to do.");
5284 return;
5285 }
5286 // else
5287
5288 const size_t diff = rcv_wnd - last_rcv_wnd;
5289 const unsigned int pct = sock->opt(sock->m_opts.m_st_rcv_buf_max_size_to_advertise_percent);
5290 const size_t max_rcv_buf_size = sock->max_block_size_multiple(sock->m_opts.m_st_rcv_buf_max_size);
5291 const size_t min_inc = max_rcv_buf_size * pct / 100;
5292
5293 if (diff < min_inc)
5294 {
5295 // Not big enough increase; wait until more space is freed before informing other side.
5296 FLOW_LOG_TRACE('[' << sock << "] Receive buffer space "
5297 "freed is [" << sock->bytes_blocks_str(diff) << "] since last advertisement; "
5298 "< threshold [" << pct << "%] x "
5299 "[" << sock->bytes_blocks_str(max_rcv_buf_size) << "] = "
5300 "[" << sock->bytes_blocks_str(min_inc) << "]. Not advertising rcv_wnd yet.");
5301 return;
5302 }
5303 // else cool. Let's advertise it.
5304
5305 // This is ~equally as rare as Receive buffer overflows, so this is worth an INFO message.
5306 FLOW_LOG_INFO('[' << sock << "] Receive buffer space "
5307 "freed is [" << sock->bytes_blocks_str(diff) << "] since last advertisement; "
5308 "rcv_wnd = [" << sock->bytes_blocks_str(rcv_wnd) << "]; "
5309 ">= threshold [" << pct << "%] x "
5310 "[" << sock->bytes_blocks_str(max_rcv_buf_size) << "] = "
5311 "[" << sock->bytes_blocks_str(min_inc) << "]. Sending unsolicited rcv_wnd-advertising ACK "
5312 "and entering rcv_wnd recovery.");
5313
5314 // Prevent any further shenanigans (see above), until we exit this mode.
5315 sock->m_rcv_in_rcv_wnd_recovery = true;
5316 // Mark this down, so that we exit this mode eventually.
5317 sock->m_rcv_wnd_recovery_start_time = Fine_clock::now();
5318
5319 // Record we started the mode.
5320 sock->m_rcv_stats.rcv_wnd_recovery_event_start();
5321
5322 async_rcv_wnd_recovery(sock, rcv_wnd);
5323} // Node::receive_wnd_updated()
5324
5326{
5327 using boost::chrono::milliseconds;
5328 using boost::chrono::round;
5329 using boost::weak_ptr;
5330
5331 // We are in thread W.
5332
5333 // As discussed in Node::receive_wnd_updated(), send the ACK and then periodically re-send it until canceled.
5334
5335 // Create an ACK with no packets acknowledged (so just a window update) and send it off.
5336 auto ack = Low_lvl_packet::create_uninit_packet<Ack_packet>(get_logger());
5337 ack->m_rcv_wnd = rcv_wnd;
5338 // Record that it was advertised!
5339 sock->m_rcv_last_sent_rcv_wnd = rcv_wnd;
5340
5342
5343 // Register one ACK packet we will send ASAP (and that it acknowledged no individual packets).
5344 sock->m_rcv_stats.sent_low_lvl_ack_packet(true);
5345
5346 // ACK queued to send soon. Now, as discussed, protect against it being lost by scheduling a timer.
5347
5348 const Fine_duration fire_when_from_now = sock->opt(sock->m_opts.m_dyn_rcv_wnd_recovery_timer_period);
5349
5350 FLOW_LOG_INFO("Setting timer to fire "
5351 "[" << round<milliseconds>(fire_when_from_now) << "] from now.");
5352
5353 /* As usual, when scheduling a thing we can use the much simpler util::schedule_task_*() API; or the
5354 * full-featured boost.asio Timer. We don't need the advanced features; so the only possible reason
5355 * to go with Timer would be the perf considerations (see schedule_task_from_now() doc header for discussion).
5356 * It is emphatically NOT the case that lots of these tasks are scheduled/fired/canceled per unit time;
5357 * e.g., we see it as rare enough to be OK with an INFO log message. Hence no need to reuse a Timer repeatedly,
5358 * so use the simple API. */
5359
5360 sock->m_rcv_wnd_recovery_scheduled_task
5361 = schedule_task_from_now(get_logger(), fire_when_from_now, true, &m_task_engine,
5362 [this, sock_observer = weak_ptr<Peer_socket>(sock)](bool)
5363 {
5364 // We are in thread W.
5365
5366 auto sock = sock_observer.lock();
5367 if (!sock)
5368 {
5369 return; // Possible or not, allow for this possibility for maintainability.
5370 }
5371 // else
5372
5373 const Fine_duration since_recovery_started = Fine_clock::now() - sock->m_rcv_wnd_recovery_start_time;
5374 if (since_recovery_started > sock->opt(sock->m_opts.m_dyn_rcv_wnd_recovery_max_period))
5375 {
5376 // We've kept ACKing for a long time, and still no data. Give up: it's all up to the sender now.
5377
5378 // This is ~equally as rare as Receive buffer overflows, so this is worth an INFO message.
5379 FLOW_LOG_INFO('[' << sock << "]: still no new DATA arrived since last rcv_wnd advertisement; "
5380 "Time since entering recovery [" << round<milliseconds>(since_recovery_started) << "] expired. "
5381 "Ending rcv_wnd recovery.");
5382 sock->m_rcv_in_rcv_wnd_recovery = false;
5383
5384 // Record we ended in timeout.
5385 sock->m_rcv_stats.rcv_wnd_recovery_event_finish(false);
5386
5387 return;
5388 }
5389 // else
5390
5391 // Still in rcv_wnd recovery. Send another unsolicited ACK (as in receive_wnd_updated()).
5392
5393 // Re-grab available Receive buffer space.
5394 const size_t rcv_wnd = sock_rcv_wnd(sock);
5395
5396 // This is ~equally as rare as Receive buffer overflows, so this is worth an INFO message.
5397 FLOW_LOG_INFO('[' << sock << "]: still no new DATA arrived since last rcv_wnd advertisement; "
5398 "rcv_wnd = [" << sock->bytes_blocks_str(rcv_wnd) << "]; "
5399 "time since entering recovery [" << round<milliseconds>(since_recovery_started) << "]. "
5400 "Sending unsolicited rcv_wnd-advertising ACK and continuing rcv_wnd recovery.");
5401
5402 async_rcv_wnd_recovery(sock, rcv_wnd);
5403 }); // on-scheduled-task-fired
5404} // Node::async_rcv_wnd_recovery()
5405
5407{
5408 using boost::chrono::milliseconds;
5409 using boost::chrono::round;
5411
5412 // We are in thread W.
5413
5414 // We got some good DATA. If we were sending unsolicited window update ACKs, we can now stop.
5415
5416 if (!sock->m_rcv_in_rcv_wnd_recovery)
5417 {
5418 // We weren't.
5419 return;
5420 }
5421 // else
5422
5423 // This is ~equally as rare as Receive buffer overflows, so this is worth an INFO message.
5424 FLOW_LOG_INFO('[' << sock << "]: Canceling rcv_wnd recovery; "
5425 "Time since entering recovery "
5426 "[" << round<milliseconds>(Fine_clock::now() - sock->m_rcv_wnd_recovery_start_time) << "].");
5427
5428 sock->m_rcv_in_rcv_wnd_recovery = false;
5429#ifndef NDEBUG
5430 const bool canceled =
5431#endif
5432 scheduled_task_cancel(get_logger(), sock->m_rcv_wnd_recovery_scheduled_task);
5433 assert(canceled);
5434
5435 // Record we ended in success.
5436 sock->m_rcv_stats.rcv_wnd_recovery_event_finish(true);
5437}
5438
5440{
5441 using std::numeric_limits;
5442
5443 // We are in thread W.
5444
5445 if (!sock->opt(sock->m_opts.m_st_rcv_flow_control_on))
5446 {
5447 /* Flow control disabled, so if we always advertise the same huge value, the other side will
5448 * never stop sending due to rcv_wnd. On this side, we won't activate rcv_wnd recovery, because
5449 * the "last advertised" window will always equal the current window. */
5450 return numeric_limits<size_t>::max();
5451 }
5452 // else
5453
5454 // Grab available Receive buffer space. We have to momentarily lock sock due to access to sock->m_rcv_buf.
5455 size_t rcv_buf_size;
5456 {
5458 rcv_buf_size = sock->m_rcv_buf.data_size();
5459 }
5460
5461 // Add the reassembly queue cumulative stored data size. Why? See sock_data_to_reassembly_q_unless_overflow().
5462 if (sock->rexmit_on())
5463 {
5464 rcv_buf_size += sock->m_rcv_reassembly_q_data_size; // (At least one reason we must be in thread W.)
5465 }
5466
5467 const size_t max_rcv_buf_size = sock->max_block_size_multiple(sock->m_opts.m_st_rcv_buf_max_size);
5468
5469 return (max_rcv_buf_size > rcv_buf_size) ? (max_rcv_buf_size - rcv_buf_size) : 0;
5470}
5471
5473{
5474 // We are in thread W.
5475
5476 /* As always, no need to lock m_state, etc., unless we plan to alter them, since no other thread can alter them.
5477 * ...On the other hand, we are going to be checking m_rcv_buf for emptiness below, and if it's not empty,
5478 * a user thread U != W may be altering it right now by consuming it. So, lock.
5479 *
5480 * Could think about locking later in this function, but this is called so rarely I'd rather not have to
5481 * worry about whether it's OK to do that and just not. */
5482 Peer_socket::Lock_guard lock{sock->m_mutex};
5483
5484 if (sock->m_state == Peer_socket::State::S_CLOSED)
5485 {
5486 /* When were placed onto thread W, state was S_OPEN+S_DISCONNECTING, but before boost.asio
5487 * could execute us, it executed another handler which already moved us to S_CLOSED for
5488 * whatever reason (there are many valid ones). So just don't do anything, as we no longer
5489 * apply. It's kind of interesting, so log INFO message. */
5490 FLOW_LOG_INFO('[' << sock << "] "
5491 "was completely closed before asynchronous "
5492 "receive_emptied_rcv_buf_while_disconnecting() could proceed.");
5493 return;
5494 }
5495 // else
5496
5497 // Sanity-check (we cannot be called until there's a graceful close underway).
5498 assert((sock->m_state == Peer_socket::State::S_OPEN) &&
5499 (sock->m_open_sub_state == Peer_socket::Open_sub_state::S_DISCONNECTING));
5500
5501 const Socket_id socket_id = Node::socket_id(sock);
5502
5503 if (sock->m_int_state != Peer_socket::Int_state::S_CLOSED)
5504 {
5505 /* Yes, they emptied Receive buffer. However, we haven't finished the graceful close.
5506 * Therefore -- even though one more barrier to reaching m_state == S_CLOSED has been removed --
5507 * there's nothing further to do at this time. In fact, in certain situations we might even
5508 * get more data onto the Receive buffer! @todo No graceful close yet. */
5509 FLOW_LOG_TRACE('[' << sock << "] "
5510 "is gracefully closing, and Receive buffer is empty, but graceful close itself not yet finished.");
5511 return;
5512 }
5513 // else if (m_int_state == S_CLOSED)
5514
5515 // Ensure Receive buffer is indeed still empty. (Can still get data while gracefully closing.)
5516 if (!sock->m_rcv_buf.empty())
5517 {
5518 /* Some data arrived between the time we were placed on thread W and boost.asio executing us.
5519 * So we can't do anything; user has to receive() the stuff first, which should call us again. */
5520 FLOW_LOG_TRACE('[' << sock << "] "
5521 "is gracefully closing, but Receive buffer has data again.");
5522 return;
5523 }
5524 // else if (m_int_state == S_CLOSED, and m_rcv_buf is empty)
5525
5526 // Yes, the transport layer final handshake is finished. Since Receive buffer now empty, no more barriers remain.
5527 FLOW_LOG_TRACE('[' << sock << "] "
5528 "is gracefully closing, and Receive buffer is now empty. Ready to permanently close.");
5530 Error_code{}, /* err_code == success indicates clean close here. */
5531 false);
5532 /* ^-- defer_delta_check == false: for similar reason as when calling send_worker() from
5533 * send_worker_check_state(). */
5534} // Node::receive_emptied_rcv_buf_while_disconnecting()
5535
5537{
5538 using boost::adopt_lock;
5541
5542 /* We are in user thread U != W.
5543 * It's important to keep that in mind in this method. In particular, it is absolutely unsafe to
5544 * access m_int_state, which belongs solely to thread W and is never locked. */
5545
5546 {
5547 /* WARNING!!! sock->m_mutex is locked, but WE must unlock it before returning! Can't leave that
5548 * to the caller, because we must unlock at a specific point below, right before post()ing
5549 * close_abruptly_worker() onto thread W. Use a Lock_guard that adopts an
5550 * already-locked mutex. */
5551 Peer_socket::Lock_guard lock{sock->m_mutex, adopt_lock};
5552
5553 if (!running())
5554 {
5556 return;
5557 }
5558 // else
5559
5560 // Pre-condition.
5561 assert(sock->m_state == Peer_socket::State::S_OPEN);
5562
5563 /* Put the rest of the work into thread W. For justification, see big comment in listen().
5564 * Addendum regarding performance: close_abruptly() is probably called more frequently than
5565 * listen(), but I doubt the performance impact is serious even so. send() and receive() might be
5566 * a different story. */
5567
5568 // We're done -- must unlock so that thread W can do what it wants to with sock.
5569 } // lock
5570
5571 // Load this onto thread W boost.asio work queue. We don't return until it runs, so [&].
5572 asio_exec_ctx_post(get_logger(), &m_task_engine, Synchronicity::S_ASYNC_AND_AWAIT_CONCURRENT_COMPLETION, [&]()
5573 {
5574 // We are in thread W. Thread U is waiting for us to do our stuff and return.
5575
5576 /* Since we were placed onto thread W, another handler may have been executed before boost.asio
5577 * got to us. Therefore we may already be S_CLOSED. Detect this. */
5578
5579 if (sock->m_state == Peer_socket::State::S_CLOSED) // No need to lock: only W can write to this.
5580 {
5581 // Yep, already closed. sock->m_disconnect_cause is already set to closure reason. Done.
5582 *err_code = sock->m_disconnect_cause;
5583 return;
5584 }
5585 // else
5586
5587 /* Cool, we're not quite closed yet. We could be connecting... or connected... or even in the
5588 * middle of graceful close (@todo that's not yet implemented). Any of those situations allow
5589 * close_abruptly(), just as (indeed because of the fact that) any of those situations allow
5590 * close_connection_immediately() (..., error::...).
5591 *
5592 * Therefore simply do the following. Pre-conditions hold: sock is in m_socks and is S_OPEN
5593 * (because not S_CLOSED); 3rd arg contains failure reason. */
5595 /* ^-- defer_delta_check == false: for similar reason as when calling send_worker() from
5596 * send_worker_check_state(). */
5597
5598 // That set sock->m_disconnect_cause. Closure successful. Done.
5599 err_code->clear(); // Success.
5600 }); // asio_exec_ctx_post()
5601 // If got here, the task has completed in thread W and signaled us to that effect.
5602} // Node::close_abruptly()
5603
5605 const Error_code& err_code, bool defer_delta_check)
5606{
5607 using boost::lexical_cast;
5608 using std::string;
5609
5610 // We are in thread W.
5611
5612 // @todo OK if a graceful close (S_OPEN+S_DISCONNECTING) is already in progress? Below provides for it, but ensure.
5613 assert(sock->m_state == Peer_socket::State::S_OPEN);
5614
5615 if (err_code)
5616 {
5617 FLOW_ERROR_LOG_ERROR(err_code);
5618 FLOW_LOG_INFO("Closing and destroying [" << sock << "] abruptly.");
5619 }
5620 else
5621 {
5622 // m_disconnect_cause has already been set and logged.
5623 FLOW_LOG_INFO("Closing and destroying [" << sock << "] after graceful close.");
5624 }
5625 // Log final state report.
5626 sock_log_detail(sock);
5627
5628 /* Thread safety: we're in thread W, so no need to lock things by default (as most resources can
5629 * also only be accessed from thread W). Exceptions are certain data members in Peer_socket
5630 * sock and Server_socket serv that may have originated it (if it was a passive open). I will
5631 * comment on the locking situation for those data members as they come up in the code. */
5632
5633 // First, set various state in *sock (including emptying Send and Receive buffers and setting m_node = nullptr).
5634
5635 /* Save the final set of stats for Peer_socket::info(), as the source data will probably get
5636 * purged just below in sock_disconnect_*(). */
5637 sock_load_info_struct(sock, &sock->m_info_on_close);
5638 // We may have to massage it a little more, because some info is set below, by when it's too late.
5639
5640 if (err_code)
5641 {
5642 // sock->m_disconnect_cause has not yet been set; so sock_load_info_struct() did not copy it properly yet. Do so.
5643 sock->m_info_on_close.m_disconnect_cause = err_code;
5644 // Similarly:
5645 sock->m_info_on_close.m_int_state_str = lexical_cast<string>(Peer_socket::Int_state::S_CLOSED);
5646
5647 /* This is an abrupt close. This can be called in any situation once sock is in m_socks. It's
5648 * our responsibility to move directly to transport layer state S_CLOSED and user state
5649 * S_CLOSED. */
5650 sock_set_int_state(sock, Peer_socket::Int_state::S_CLOSED); // Thread W access only; no need to lock.
5651 // Sets S_CLOSED public state (and related data, including m_disconnect_cause). Locked inside.
5652 sock_disconnect_detected(sock, err_code, true);
5653 }
5654 else
5655 {
5656 /* We are in a graceful close and have reached the final stage of it (connection entirely
5657 * closed without having to abruptly close; buffers emptied gracefully by user and/or Node).
5658 * Therefore m_int_state is already S_CLOSED (method pre-condition), so
5659 * we just complete the user-visible state change. */
5660
5661 assert(sock->m_int_state == Peer_socket::Int_state::S_CLOSED); // Thread W access only; no need to lock.
5662 sock_disconnect_completed(sock); // Sets S_CLOSED public state (and related data). Locked inside.
5663 }
5664
5665 // Next, remove sock from our main socket list.
5666
5667#ifndef NDEBUG
5668 const auto erased = 1 ==
5669#endif
5670 m_socks.erase(socket_id);
5671 assert(erased); // S_OPEN => it's in m_socks. Otherwise there's a serious bug somewhere.
5672
5673 // Next, if this potentially is an unaccepted connection, delete it from the corresponding server socket.
5674 if (!sock->m_active_connect)
5675 {
5676 /* What is that Server_socket though? Well, it's in sock->m_originating_serv... but that data
5677 * member can be accessed from a non-W thread, so we'd have to lock it. But the mutex that
5678 * protects it in in *m_originating_serv itself! So it's a chicked/egg problem. However, we
5679 * can find that Server_socket (if it applies to sock) another way: through the port. Its port
5680 * must be the same as local_port. If such a Server_socket exists, cool; and if sock is
5681 * tracked inside it, cool. Otherwise we needn't do anything. */
5682 Port_to_server_map::const_iterator port_to_server_it = m_servs.find(sock->m_local_port);
5683 if (port_to_server_it != m_servs.end()) // Server at same port number exists. Not necessarily our guy though.
5684 {
5685 // If it is our guy, delete us from him.
5686 Server_socket::Ptr serv = port_to_server_it->second;
5687 serv_peer_socket_closed(serv, sock); // Thread-safe (in particular with respect to simultaneous serv->accept()).
5688 }
5689 }
5690
5691 // sock now should not be (directly or indirectly) referenced in any Node data structures.
5692
5693 // Cancel any timers.
5694 cancel_timers(sock);
5695
5696 /* Return the port -- but only if it is an active open. If it's a passive open the port is
5697 * still reserved for the server socket. */
5698 if (sock->m_active_connect)
5699 {
5700 Error_code return_err_code;
5701 m_ports.return_port(sock->m_local_port, &return_err_code);
5702 assert(!return_err_code);
5703 }
5704
5705 /* sock has changed to CLOSED state. Performing sock->receive() or sock->write() would therefore
5706 * certainly return an error. Returning an error from those methods (as opposed to 0 but no
5707 * error) is considered Readable and Writable, respectively (as we want to alert the user to the
5708 * error, so her wait [if any] wakes up and notices the error). Therefore we should soon inform
5709 * anyone waiting on any Event_sets for sock to become Readable or Writable.
5710 *
5711 * Caveat: Similar to that in Node::handle_syn_ack_ack_to_syn_rcvd() at similar point in the
5712 * code. */
5713
5714 // Accumulate the event into the Node store (note: not any Event_set yet).
5715 const bool inserted_rd = m_sock_events[Event_set::Event_type::S_PEER_SOCKET_READABLE].insert(sock).second;
5716 const bool inserted_wr = m_sock_events[Event_set::Event_type::S_PEER_SOCKET_WRITABLE].insert(sock).second;
5717 if (inserted_rd || inserted_wr) // Must always perform both insert()s, hence the use of the 2 variables.
5718 {
5719 // Possibly inform the user for any applicable Event_sets right now.
5720 event_set_all_check_delta(defer_delta_check);
5721 }
5722} // Node::close_connection_immediately()
5723
5725 const Error_code& err_code, bool defer_delta_check)
5726{
5727 // We are in thread W.
5729 close_connection_immediately(socket_id, sock, err_code, defer_delta_check);
5730}
5731
5733{
5734 using util::Blob;
5735
5736 auto syn = Low_lvl_packet::create_uninit_packet<Syn_packet>(get_logger());
5737 // Initial Sequence Number.
5738 syn->m_init_seq_num = sock->m_snd_init_seq_num;
5739 /* Send serialized version of arbitrary user data, which user can deserialize on the other side
5740 * after accepting connection.
5741 * Add const to express we require a copy, not move. */
5742 syn->m_serialized_metadata = static_cast<const Blob&>(sock->m_serialized_metadata);
5743
5744 return syn;
5745}
5746
5748{
5749 auto syn_ack = Low_lvl_packet::create_uninit_packet<Syn_ack_packet>(get_logger());
5750 // Initial Sequence Number (the start of our own series).
5751 syn_ack->m_init_seq_num = sock->m_snd_init_seq_num;
5752 // Random security token.
5753 syn_ack->m_packed.m_security_token = sock->m_security_token;
5754 // Advertise initial rcv_wnd.
5755 syn_ack->m_packed.m_rcv_wnd = sock->m_rcv_last_sent_rcv_wnd;
5756
5757 return syn_ack;
5758}
5759
5761 boost::shared_ptr<const Syn_ack_packet>& syn_ack)
5762{
5763 // Make a packet.
5764 auto syn_ack_ack = Low_lvl_packet::create_uninit_packet<Syn_ack_ack_packet>(get_logger());
5765 // No sequence number (not the initial SYN; not data).
5766 // Security token: give it back to them (they will verify).
5767 syn_ack_ack->m_packed.m_security_token = syn_ack->m_packed.m_security_token;
5768 // Initial receive window is probably the entire, ~empty Receive buffer. Save the advertised rcv_wnd as promised.
5769 syn_ack_ack->m_packed.m_rcv_wnd = sock->m_rcv_last_sent_rcv_wnd = sock_rcv_wnd(sock);
5770
5771 // Fill out common fields and asynchronously send packet.
5773}
5774
5776{
5777 using boost::chrono::milliseconds;
5778 using boost::chrono::duration_cast;
5779 using std::make_pair;
5780 using std::vector;
5781 using std::numeric_limits;
5782
5783 // We are in thread W.
5784
5785 // Handle the timer-related corner cases (if we were invoked by m_rcv_delayed_ack_timer triggering).
5786
5787 // For brevity and speed:
5788 vector<Peer_socket::Individual_ack::Ptr>& pending_acks = sock->m_rcv_pending_acks;
5789
5790 if (sys_err_code == boost::asio::error::operation_aborted)
5791 {
5792 FLOW_LOG_TRACE("Delayed [ACK] timer [" << sock << "] canceled; "
5793 "pending acknowledgment count [" << pending_acks.size() << "].");
5794 return;
5795 }
5796 // else
5797
5798 FLOW_LOG_TRACE("Delayed [ACK] timer [" << sock << "] triggered, or ACK forced; "
5799 "pending acknowledgment count [" << pending_acks.size() << "].");
5800
5801 if (sys_err_code)
5802 {
5803 FLOW_ERROR_SYS_ERROR_LOG_WARNING(); // Log non-portable error.
5804 // Nothing else to do here. We don't know what this means. So just treat it as if timer was triggered.
5805 }
5806
5807 if (sock->m_int_state != Peer_socket::Int_state::S_ESTABLISHED)
5808 {
5809 /* This is unlikely but legitimate. (Can happen if, by the time the handler that advanced state
5810 * from ESTABLISHED to another state started, this timer also was triggered and thus queued the
5811 * current handler inside m_task_engine.) */
5812 FLOW_LOG_TRACE("Delayed [ACK] timer [" << sock << "] triggered, "
5813 "but socket already in inapplicable state [" << sock->m_int_state << "]. Ignoring.");
5814 return;
5815 }
5816 // else
5817
5818 if (pending_acks.empty())
5819 {
5820 /* This is probably a bug if we're here. However, assert() or connection closure seems a bit
5821 * drastic... carry on. */
5822 FLOW_LOG_WARNING("Delayed [ACK] timer [" << sock << "] triggered, "
5823 "but socket has no pending acknowledgments. This is likely an internal bug. Ignoring.");
5824 return;
5825 }
5826 // else
5827
5828 /* OK, let's do it. Basically just shove all the acknowledgments into an ACK packet. Namely, for
5829 * each one, shove the starting sequence number and the amount of time since we first received it
5830 * (so the other side can subtract that to compute RTT, if it wants).
5831 *
5832 * However we may run out of space and need more ACKs. To keep track of how much space we've
5833 * used, compute an estimate for serializing those two pieces of data and keep adding that for
5834 * each acknowledgment handled. The budget is given by max-block-size; a DATA packet is allowed
5835 * that much payload on top of the normal header stuff, so that should be good enough for us too.
5836 * There's probably some constant overhead on top of that, but it's close enough.
5837 *
5838 * ACK is also used as an opportunistic way to send rcv_wnd to the other side, which informs
5839 * them of how much more data we can take at this time. Naively we should just have rcv_wnd =
5840 * the max buffer size minus the buffer space currently taken, and that is the most accurate
5841 * thing. However RFC 793 ("Window Management Suggestions") and probably other literature
5842 * suggest to (when the available space is increasing) advertise the window in larger steps (so
5843 * withhold the higher rcv_wnd value until it increases even further up to some threshold). For
5844 * now I forego such fanciness. See also the rcv_wnd-related comment in
5845 * Node::receive_wnd_increased() for further reasoning on rcv_wnd (namely surrounding the fact
5846 * that sometimes we must send ACKs with no packets acknowledged to ensure a connection does not
5847 * stall due to a zero rcv_wnd). */
5848
5849 // Grab available Receive buffer space. Save it for later comparison.
5850 const size_t& rcv_wnd = sock->m_rcv_last_sent_rcv_wnd = sock_rcv_wnd(sock);
5851
5852 auto ack = Low_lvl_packet::create_uninit_packet<Ack_packet>(get_logger());
5853 ack->m_rcv_wnd = rcv_wnd; // Advertise receive window. @todo Code reuse?
5854
5855 const size_t max_block_size = sock->max_block_size();
5856 size_t size_est_inc
5858 if (sock->rexmit_on())
5859 {
5860 size_est_inc += sizeof(Low_lvl_packet::rexmit_id_t);
5861 }
5862 assert(size_est_inc <= max_block_size); // At least one has to fit.
5863
5864 const Fine_time_pt time_now = Fine_clock::now();
5865 size_t size_est_so_far = sizeof(Low_lvl_packet::rcv_wnd_t); // How many raw bytes we have, approximately, used.
5866 for (Peer_socket::Individual_ack::Const_ptr ind_ack : pending_acks)
5867 {
5868 if (size_est_so_far + size_est_inc > max_block_size)
5869 {
5870 // Too big. Send off what we have.
5872
5873 // Register one ACK packet we will send ASAP.
5874 sock->m_rcv_stats.sent_low_lvl_ack_packet(false);
5875
5876 // As async_sock_low_lvl_packet_send_paced() says, we cannot reuse ack's pointed-to-object. Make new one.
5877 ack = Low_lvl_packet::create_uninit_packet<Ack_packet>(get_logger());
5878 ack->m_rcv_wnd = rcv_wnd; // Advertise receive window. @todo Code reuse?
5879
5880 size_est_so_far = sizeof(Low_lvl_packet::rcv_wnd_t);
5881 }
5882
5883 // Add the acknowledgment to the current ACK.
5884
5885 // First sequence number in packet.
5886 const Sequence_number& seq_num = ind_ack->m_seq_num;
5887
5888 // ACK delay for this individual acknowledgment. Compute it; then validate it.
5889
5890 /* @todo In low_lvl_io, we perform packet pacing but currently choose to assign a value of
5891 * 0 bytes to an ACK. That is, while we do preserve the order of DATA and ACK packets -- if
5892 * both happen to be in the outgoing stream -- we do not delay the sending of the ACK once it is
5893 * the next packet to be sent out. However, even so, an ACK's sending may be delayed by the
5894 * pacing applied to DATA packets intermixed with it. Therefore the ACK delay measurement we
5895 * take here may be incorrect (too low) in that case. This can cause overestimated RTTs on the
5896 * sender's side. The to-do is to correct the ACK delay value in a given ACK by adding the
5897 * pacing delay (if any) of the ACK to the individual ACK delays within it. Conceptually this
5898 * is similar to the sent_when value being set when choosing to send a DATA packet and then
5899 * corrected in the pacing module later.
5900 *
5901 * This to-do is not important until we in practice start mixing sending and receiving at the
5902 * application layer... but still -- it's worth knowing that there is a design bug here. */
5903
5904 // Shouldn't be negative.
5905 Fine_duration delay = time_now - ind_ack->m_received_when;
5906 if (delay.count() < 0)
5907 {
5908 /* This is pretty crazy and should not happen according to the documented properties of
5909 * Fine_clock. No need to crash or disconnect though, so do our best.... */
5910 FLOW_LOG_WARNING("Delayed [ACK] timer [" << sock << "] triggered; "
5911 "delay for packet [" << seq_num << ", ...) is "
5912 "negative: [" << delay << "]; using zero.");
5913 delay = Fine_duration::zero();
5914 }
5915
5916 /* Convert whatever resolution Fine_clock uses to milliseconds because we want to keep that
5917 * field of the ACK sized according to how the low-level packet handling code prefers it for
5918 * efficiency. Overflow is possible. Use duration_cast (truncation) instead of rounding,
5919 * because in very low-latency situations the extra microseconds rounding up can cause a
5920 * negative RTT calculation on the other side (when this ACK is received). The ACK handling
5921 * code will just clamp the value at zero on the other side, but let's try to avoid it anyway
5922 * on this side.
5923 *
5924 * @todo This comment appears to be outdated, as Ack_delay_time_unit is just Fine_duration.
5925 * Look into this. */
5926 Ack_packet::Ack_delay_time_unit pkt_delay = duration_cast<Ack_packet::Ack_delay_time_unit>(delay);
5927 const Ack_packet::ack_delay_t MAX_DELAY_VALUE = numeric_limits<Ack_packet::ack_delay_t>::max();
5928 if (uint64_t(pkt_delay.count()) > uint64_t(MAX_DELAY_VALUE))
5929 {
5930 /* This is pretty crazy though not 100% impossible if the CPU is really loaded, or some other
5931 * shenanigans. So do our best.... */
5932 FLOW_LOG_WARNING("Delayed [ACK] timer [" << sock << "] triggered; "
5933 "delay for packet [" << seq_num << ", ...) is [" << pkt_delay << "]; overflow; "
5934 "using max value [" << MAX_DELAY_VALUE << "] units.");
5935 // @todo Maybe there's a more sane ceiling value than the absolute maximum?
5936 pkt_delay = Ack_packet::Ack_delay_time_unit{MAX_DELAY_VALUE};
5937 }
5938
5939 // Finally write the individual acknowledgment.
5940 if (sock->rexmit_on())
5941 {
5942 ack->m_rcv_acked_packets_rexmit_on_out.push_back
5944 ind_ack->m_rexmit_id,
5945 Ack_packet::ack_delay_t(pkt_delay.count())});
5946 }
5947 else
5948 {
5949 ack->m_rcv_acked_packets_rexmit_off_out.push_back
5951 Ack_packet::ack_delay_t(pkt_delay.count())});
5952 }
5953 size_est_so_far += size_est_inc;
5954
5955 // Register one packet of unknown size that we've packaged into an ACK and will send ASAP.
5956 sock->m_rcv_stats.sent_individual_ack();
5957 } // for (ind_ack : pending_acks)
5958
5959 // Don't forget the last non-full ACK, if any.
5960 if (size_est_so_far != 0)
5961 {
5963 }
5964
5965 // Register one ACK packet we will send ASAP.
5966 sock->m_rcv_stats.sent_low_lvl_ack_packet(false);
5967
5968 // All serialized to be sent; the timer can start again when a packet must be acknowledged.
5969 pending_acks.clear();
5970
5971 // Register that now there are 0 pending individual acks.
5972 sock->m_rcv_stats.current_pending_to_ack_packets(0);
5973
5974 // Note that all the ACKs are sent off outside this handler and only once UDP is ready.
5975} // Node::async_low_lvl_ack_send()
5976
5978{
5979 // We are in thread W.
5980 return Socket_id{ sock->remote_endpoint(), sock->local_port() };
5981}
5982
5984{
5985 // There is stuff to send if there is anything to retransmit or at least new user data.
5986 return !(sock->m_snd_rexmit_q.empty() && sock->m_snd_buf.empty());
5987}
5988
5990{
5991 // See doc comment for rationale for keeping this in a function.
5992
5993 /* Since 1 block can be at most max-block-size, if that much space is free, then definitely one
5994 * can enqueue onto m_snd_buf. Note that if less than max-block-size space is free, it would
5995 * still be possible to enqueue a smaller block; yet we still return false. We are intentionally
5996 * conservative, because we are guaranteeing ANY one enqueueing will work. More importantly, this
5997 * guarantees our Socket_buffer scheme (see class doc header) to guarantee constant-time
5998 * dequeueing will work.
5999 *
6000 * We're not overly conservative, either; i.e., no one is likely to complain this policy is too
6001 * stingy. */
6002 return sock->m_snd_buf.data_size() + sock->max_block_size()
6003 <= sock->opt(sock->m_opts.m_st_snd_buf_max_size);
6004}
6005
6007{
6008 // See doc comment for rationale for keeping this in a function.
6009 return !sock->m_rcv_buf.empty();
6010}
6011
6013{
6014 // We are in thread W.
6015
6016 FLOW_LOG_TRACE('[' << sock << "] changing state from [" <<
6017 sock->m_int_state << "] to [" << new_state << "].");
6018 sock->m_int_state = new_state;
6019}
6020
6022{
6023 Peer_socket::Lock_guard lock{sock->m_mutex};
6024
6025 // @todo Add TRACE logging.
6026
6027 sock->m_state = state;
6028 if (state == Peer_socket::State::S_OPEN)
6029 {
6030 sock->m_open_sub_state = open_sub_state;
6031 }
6032 else // (state == Peer_socket::State::S_CLOSED)
6033 {
6034 /* Important convention: S_CLOSED means socket is permanently incapable of sending or
6035 * receiving more data. At this point the originating Node removes the socket from its internal
6036 * structures. Therefore, the Node itself may even go away -- while this Peer_socket still
6037 * exists. Since we use shared_ptr when giving our socket objects, that's fine -- but we want to
6038 * avoid returning an invalid Node* in node(). So, when S_CLOSED, sock->m_node = nullptr. */
6039 sock->m_node = nullptr;
6040 }
6041}
6042
6043void Node::sock_disconnect_detected(Peer_socket::Ptr sock, const Error_code& disconnect_cause, bool close)
6044{
6045 Peer_socket::Lock_guard lock{sock->m_mutex};
6046
6047 sock->m_disconnect_cause = disconnect_cause;
6048
6049 if (close)
6050 {
6051 // DONE.
6052 sock_set_state(sock, Peer_socket::State::S_CLOSED); // Reentrant mutex => OK.
6053 sock_free_memory(sock);
6054 }
6055 else
6056 {
6057 // This socket is screwed, but let user get any remaining buffer data out.
6058
6059 // Reentrant mutex => OK:
6061 }
6062}
6063
6065{
6066 Peer_socket::Lock_guard lock{sock->m_mutex};
6067
6068 // Sanity-check pre-conditions. (Basically ensure disconnect_detected(err_code, false) was previously called.)
6069 assert(sock->m_disconnect_cause);
6070 assert((sock->m_state == Peer_socket::State::S_OPEN)
6071 && (sock->m_open_sub_state == Peer_socket::Open_sub_state::S_DISCONNECTING));
6072
6073 sock_set_state(sock, Peer_socket::State::S_CLOSED); // Reentrant mutex => OK.
6074 sock_free_memory(sock);
6075}
6076
6078{
6079 sock->m_rcv_buf.clear();
6080 sock->m_snd_buf.clear();
6081 sock->m_rcv_packets_with_gaps.clear();
6082 sock->m_rcv_reassembly_q_data_size = 0;
6083 sock->m_snd_flying_pkts_by_sent_when.clear();
6084 sock->m_snd_flying_pkts_by_seq_num.clear();
6085 sock->m_snd_rexmit_q.clear();
6086 sock->m_serialized_metadata.make_zero(); // clear() does not deallocate, but this does.
6087 sock->m_rcv_syn_rcvd_data_q.clear();
6088 sock->m_rcv_pending_acks.clear();
6089 sock->m_rcv_acked_packets.clear();
6090 sock->m_snd_pacing_data.m_packet_q.clear();
6091
6092 /* Destroy memory stored in m_snd_cong_ctl which may be non-O(1). This is a little questionable;
6093 * maybe should leave it to destructor? However since we store it as a pointer and are to free
6094 * any "significant" memory, and this may be significant, we may as well just delete it. */
6095 sock->m_snd_cong_ctl.reset();
6096 // Same deal.
6097 sock->m_snd_bandwidth_estimator.reset();
6098}
6099
6101{
6102 // We are in thread U != W.
6103
6104 if (!running())
6105 {
6107 return false;
6108 }
6109 // else
6110
6111 /* We just want to replace m_opts with a copy of opts. First validate opts (including with
6112 * respect to m_opts, and also check for invalid values and such), then copy it over. */
6113
6114 // Log new options values. A bit computationally expensive so just use TRACE for now. @todo Reconsider?
6115 FLOW_LOG_TRACE("For [" << sock << "]:\n\n" << opts);
6116
6117 // Will be writing sock->m_opts if all goes well, so must acquire exclusive ownership of m_opts.
6118 Peer_socket::Options_lock lock{sock->m_opts_mutex};
6119
6120 /* Validate the new option set (including ensuring they're not changing static options' values).
6121 * Note that an explicit pre-condition of this method is that m_opts_mutex is locked if needed,
6122 * hence the above locking statement is not below this call. */
6123 if (!sock_validate_options(opts, &sock->m_opts, err_code))
6124 {
6125 return false;
6126 }
6127 // else
6128
6129 // Boo-ya.
6130 sock->m_opts = opts;
6131 return true;
6132} // Node::sock_set_options()
6133
6134/// @cond
6135/* -^- Doxygen, please ignore the following. (Don't want docs generated for temp macro; this is more maintainable
6136 * than specifying the macro name to omit it, in Doxygen-config EXCLUDE_SYMBOLS.) */
6137
6138/* Normaly I try to avoid macro cleverness, but in this case to get a nice printout we need the
6139 * # technique, and also this eliminates quite a bit of repetition. So let's.... */
6140#define VALIDATE_STATIC_OPTION(ARG_opt) \
6141 validate_static_option(opts.ARG_opt, prev_opts->ARG_opt, #ARG_opt, err_code)
6142#define VALIDATE_CHECK(ARG_check) \
6143 validate_option_check(ARG_check, #ARG_check, err_code)
6144
6145// -v- Doxygen, please stop ignoring.
6146/// @endcond
6147
6149 const Peer_socket_options* prev_opts,
6150 Error_code* err_code) const
6151{
6152 /* We are to validate the given set of per-socket option values. If prev_opts, then the context
6153 * is that an already-existing socket (with already-set options) is being called with
6154 * set_options(), i.e. user is modifying options for an existing socket. In that case we must
6155 * ensure that no static (unchangeable) option's value would be changed by this.
6156 *
6157 * If not prev_opts, then the per-socket options within the global per-Node Node_options object
6158 * are being changed. Per-socket options in that context are always dynamic, since if they were
6159 * static, there'd be no point in making the per-socket in the first place. So in that case that
6160 * static option check is to be skipped.
6161 *
6162 * Finally, we must check for individual integrity of the specified values (including consistency
6163 * with other option values). */
6164
6165 using boost::chrono::seconds;
6166 using std::numeric_limits;
6167
6168 // We are in thread U != W or in thread W.
6169
6170 // Pre-clear (maintenance-proof); each failed check below shall set *err_code as needed.
6171 err_code->clear();
6172
6173 if (prev_opts)
6174 {
6175 /* As explained above, they're trying to change an existing socket's option values. Ensure
6176 * all the static options' values are the same in opts and prev_opts. */
6177
6178 // Explicitly documented pre-condition is that *prev_opts is already locked if necessary. So don't lock.
6179
6180 const bool static_ok
6181 = VALIDATE_STATIC_OPTION(m_st_max_block_size)
6182 && VALIDATE_STATIC_OPTION(m_st_connect_retransmit_period)
6183 && VALIDATE_STATIC_OPTION(m_st_connect_retransmit_timeout)
6184 && VALIDATE_STATIC_OPTION(m_st_snd_buf_max_size)
6185 && VALIDATE_STATIC_OPTION(m_st_rcv_buf_max_size)
6186 && VALIDATE_STATIC_OPTION(m_st_rcv_syn_rcvd_data_q_cumulative_max_size)
6187 && VALIDATE_STATIC_OPTION(m_st_rcv_flow_control_on)
6188 && VALIDATE_STATIC_OPTION(m_st_rcv_buf_max_size_slack_percent)
6189 && VALIDATE_STATIC_OPTION(m_st_rcv_buf_max_size_to_advertise_percent)
6190 && VALIDATE_STATIC_OPTION(m_st_rcv_max_packets_after_unrecvd_packet_ratio_percent)
6191 && VALIDATE_STATIC_OPTION(m_st_delayed_ack_timer_period)
6192 && VALIDATE_STATIC_OPTION(m_st_max_full_blocks_before_ack_send)
6193 && VALIDATE_STATIC_OPTION(m_st_rexmit_on)
6194 && VALIDATE_STATIC_OPTION(m_st_max_rexmissions_per_packet)
6195 && VALIDATE_STATIC_OPTION(m_st_init_drop_timeout)
6196 && VALIDATE_STATIC_OPTION(m_st_snd_pacing_enabled)
6197 && VALIDATE_STATIC_OPTION(m_st_snd_bandwidth_est_sample_period_floor)
6198 && VALIDATE_STATIC_OPTION(m_st_cong_ctl_strategy)
6199 && VALIDATE_STATIC_OPTION(m_st_cong_ctl_init_cong_wnd_blocks)
6200 && VALIDATE_STATIC_OPTION(m_st_cong_ctl_max_cong_wnd_blocks)
6201 && VALIDATE_STATIC_OPTION(m_st_cong_ctl_cong_wnd_on_drop_timeout_blocks)
6202 && VALIDATE_STATIC_OPTION(m_st_cong_ctl_classic_wnd_decay_percent)
6203 && VALIDATE_STATIC_OPTION(m_st_drop_packet_exactly_after_drop_timeout)
6204 && VALIDATE_STATIC_OPTION(m_st_drop_all_on_drop_timeout)
6205 && VALIDATE_STATIC_OPTION(m_st_out_of_order_ack_restarts_drop_timer);
6206
6207 if (!static_ok)
6208 {
6209 // validate_static_option() has set *err_code.
6210 return false;
6211 }
6212 // else
6213 } // if (prev_opts)
6214
6215 // Now sanity-check the values themselves. @todo Comment and reconsider these?
6216 const bool checks_ok
6217 = VALIDATE_CHECK(opts.m_st_max_block_size >= 512)
6218 && VALIDATE_CHECK(opts.m_st_connect_retransmit_period.count() > 0)
6219 && VALIDATE_CHECK(opts.m_st_connect_retransmit_timeout.count() > 0)
6220 && VALIDATE_CHECK(opts.m_st_snd_buf_max_size >= 4 * opts.m_st_max_block_size)
6221 && VALIDATE_CHECK(opts.m_st_rcv_buf_max_size >= 4 * opts.m_st_max_block_size)
6223 && VALIDATE_CHECK(opts.m_st_rcv_max_packets_after_unrecvd_packet_ratio_percent >= 100)
6224 && VALIDATE_CHECK(opts.m_st_delayed_ack_timer_period <= seconds{1})
6225 && VALIDATE_CHECK(util::in_closed_range(Fine_duration::zero(),
6227 Fine_duration{seconds{1}}))
6228 && VALIDATE_CHECK(opts.m_st_max_full_blocks_before_ack_send >= 1)
6229 && VALIDATE_CHECK(opts.m_st_max_rexmissions_per_packet >= 1)
6230 && VALIDATE_CHECK(opts.m_st_max_rexmissions_per_packet <= numeric_limits<Low_lvl_packet::rexmit_id_t>::max())
6231 && VALIDATE_CHECK(opts.m_st_init_drop_timeout.count() > 0)
6232 && VALIDATE_CHECK(opts.m_st_snd_bandwidth_est_sample_period_floor.count() > 0)
6233 && VALIDATE_CHECK(opts.m_st_cong_ctl_init_cong_wnd_blocks <= opts.m_st_cong_ctl_max_cong_wnd_blocks)
6234 && VALIDATE_CHECK
6235 (4 * opts.m_st_cong_ctl_max_cong_wnd_blocks * opts.m_st_max_block_size <= opts.m_st_rcv_buf_max_size)
6236 && VALIDATE_CHECK(opts.m_st_cong_ctl_cong_avoidance_increment_blocks < 20)
6237 && VALIDATE_CHECK(opts.m_st_cong_ctl_classic_wnd_decay_percent <= 100)
6238 && VALIDATE_CHECK(util::in_closed_range<size_t>(1, opts.m_st_cong_ctl_cong_wnd_on_drop_timeout_blocks, 10))
6239 && VALIDATE_CHECK(opts.m_dyn_drop_timeout_ceiling > 4 * opts.m_st_init_drop_timeout)
6240 && VALIDATE_CHECK(opts.m_dyn_drop_timeout_backoff_factor >= 1)
6241 && VALIDATE_CHECK(opts.m_dyn_rcv_wnd_recovery_timer_period.count() > 0);
6242
6243 // On error, validate_option_check() has set *err_code.
6244
6245 return checks_ok;
6246
6247#undef VALIDATE_CHECK
6248#undef VALIDATE_STATIC_OPTION
6249} // Node::sock_validate_options()
6250
6252{
6255 using boost::adopt_lock;
6256
6257 // We are in thread U != W.
6258
6259 Peer_socket_info stats;
6260 {
6261 /* WARNING!!! sock->m_mutex is locked, but WE must unlock it before returning! Can't leave that
6262 * to the caller, because we must unlock at a specific point below, right before post()ing
6263 * sock_info_worker() onto thread W. Use a Lock_guard that adopts an already-locked mutex. */
6264 Peer_socket::Lock_guard lock{sock->m_mutex, adopt_lock};
6265
6266 if (!running())
6267 {
6268 /* This is kind of a weird case, in that sock's Node having stopped running is a problem, but
6269 * in this case they just want the socket stats. The only reason we're in this method --
6270 * calling sock->info() did not simply return the stats itself -- is that there was a danger
6271 * thread W might change the stats, while we'd be copying them. Well, if !running() there is no
6272 * danger of that. So we can just: */
6273 sock_load_info_struct(sock, &stats);
6274 return stats;
6275 }
6276 // else
6277
6278 /* Okay -- Node is running and may change stats's source info at any time. Therefore, since we
6279 * do not have a mutex for all that source info, we place a task on W and set up a future as a
6280 * way for it to inform us it's done. This has a certain performance penalty, but that's better
6281 * than having to lock each time we need to modify this source data throughout W's operations.
6282 * Moreover we warned about the performance penalty in the doc header for Peer_socket::info(). */
6283
6284 // We're done -- must unlock so that thread W can do what it wants to with sock.
6285 } // lock
6286
6287 // Load this onto thread W boost.asio work queue. We don't return until it's done, so [&] is OK.
6288 asio_exec_ctx_post(get_logger(), &m_task_engine, Synchronicity::S_ASYNC_AND_AWAIT_CONCURRENT_COMPLETION,
6289 [&]() { sock_load_info_struct(sock, &stats); });
6290 // If got here, the task has completed in thread W and signaled us to that effect.
6291
6292 return stats;
6293} // Node::sock_info()
6294
6296{
6297 using boost::lexical_cast;
6298 using std::string;
6299
6300 // We are in thread W.
6301
6302 stats->m_rcv = sock->m_rcv_stats.stats();
6303 stats->m_snd = sock->m_snd_stats.stats();
6304
6305 // @todo This is more suitable for the non-existent Node_info and Node::load_info_struct(). (It's not per-socket.)
6307
6308 stats->m_int_state_str = lexical_cast<string>(sock->m_int_state);
6309 stats->m_is_active_connect = sock->m_active_connect;
6310 // No need to lock: no thread but W can write to it.
6311 stats->m_disconnect_cause = sock->m_disconnect_cause;
6312
6313 {
6314 // Gotta lock, as Receive and Send buffers can be modified at any time by thread U at least.
6315 Peer_socket::Lock_guard lock{sock->m_mutex};
6316 stats->m_rcv_buf_size = sock->m_rcv_buf.data_size();
6317 stats->m_snd_buf_size = sock->m_snd_buf.data_size();
6318 }
6319
6320 stats->m_rcv_wnd = sock_rcv_wnd(sock);
6321 stats->m_rcv_wnd_last_advertised = sock->m_rcv_last_sent_rcv_wnd;
6322 stats->m_rcv_reassembly_q_data_size = sock->m_rcv_reassembly_q_data_size;
6323 stats->m_rcv_packets_with_gaps = sock->m_rcv_packets_with_gaps.size();
6325 = sock->m_rcv_syn_rcvd_data_q.empty() ? 0 : sock->m_rcv_syn_rcvd_data_cumulative_size;
6326 stats->m_rcv_syn_rcvd_data_q_size = sock->m_rcv_syn_rcvd_data_q.size();
6327
6328 stats->m_snd_rcv_wnd = sock->m_snd_remote_rcv_wnd;
6329 stats->m_snd_cong_ctl_in_flight_bytes = sock->m_snd_flying_bytes;
6330 stats->m_snd_cong_ctl_in_flight_count = sock->m_snd_flying_pkts_by_sent_when.size();
6331 stats->m_snd_cong_ctl_wnd_bytes = sock->m_snd_cong_ctl->congestion_window_bytes();
6332 stats->m_snd_cong_ctl_wnd_count_approx = stats->m_snd_cong_ctl_wnd_bytes / sock->max_block_size();
6333 stats->m_snd_smoothed_round_trip_time = sock->m_snd_smoothed_round_trip_time;
6334 stats->m_snd_round_trip_time_variance = sock->m_round_trip_time_variance;
6335 stats->m_snd_drop_timeout = sock->m_snd_drop_timeout;
6336 stats->m_snd_pacing_packet_q_size = sock->m_snd_pacing_data.m_packet_q.size();
6337 stats->m_snd_pacing_bytes_allowed_this_slice = sock->m_snd_pacing_data.m_bytes_allowed_this_slice;
6338 stats->m_snd_pacing_slice_start = sock->m_snd_pacing_data.m_slice_start;
6339 stats->m_snd_pacing_slice_period = sock->m_snd_pacing_data.m_slice_period;
6341 = util::to_mbit_per_sec<Send_bandwidth_estimator::Time_unit>
6342 (sock->m_snd_bandwidth_estimator->bandwidth_bytes_per_time());
6343
6344 stats->m_sock_opts = sock->opt(sock->m_opts); // Lock and copy... probably not the fastest thing ever....
6345 stats->m_node_opts = opt(m_opts); // Ditto.
6346}
6347
6349{
6350 // We are in thread W.
6351
6352 /* We are to log details about the given socket. Since the idea is that this would be called on
6353 * the order of at most once or twice a second, we can be as verbose as we think is useful without
6354 * (too much) concern for performance. */
6355
6356 Peer_socket_info stats;
6357 sock_load_info_struct(sock, &stats); // This involves some copying, but, again, we are not too concerned with speed.
6358
6359 FLOW_LOG_INFO("[=== Socket state for [" << sock << "]. ===\n" << stats);
6360
6361 // Log receive and send windows details. Force the logging of the most verbose possible amount of info.
6362 log_snd_window(sock, true);
6363 log_rcv_window(sock, true);
6364 // @todo Should this be inside Peer_socket_info also?
6365
6366 FLOW_LOG_INFO("=== Socket state for [" << sock << "]. ===]");
6367} // Node::sock_log_detail()
6368
6369void Node::advance_seq_num(Sequence_number* seq_num, boost::shared_ptr<const Data_packet> data) // Static.
6370{
6371 /* We just need to increment *seq_num, which points to the start of the data in `data`,
6372 * to a value that points to the data just past the end of the data in `data`. Why is this in a
6373 * separate method? Answer: We may want to change the mapping from sequence number to byte of data. In
6374 * particular the mapping can be one-to-one, as in TCP. Or it can be one sequence number to all bytes in a
6375 * particular packet, which I've seen in certain lesser known custom protocols. This allows us to
6376 * (hopefully) change the code in one place. */
6377
6378 advance_seq_num(seq_num, data->m_data.size());
6379} // Node::advance_seq_num()
6380
6381void Node::advance_seq_num(Sequence_number* seq_num, size_t data_size)
6382{
6383 /* For now go with TCP's convention (one byte to one sequence number, no gaps). While we deal
6384 * with blocks, instead of streams, this may complicate the math a bit and use more sequence
6385 * number space (faster wrapping). However, it would make it easier to adapt the algorithms
6386 * when we move to byte streams; and we currently use a sequence number so large that wrapping
6387 * is impossible. Update: we have moved to streams. */
6388 *seq_num += data_size;
6389}
6390
6391template<typename Packet_map_iter>
6392void Node::get_seq_num_range(const Packet_map_iter& packet_it,
6393 Sequence_number* seq_num_start, Sequence_number* seq_num_end) // Static.
6394{
6395 const Sequence_number& seq_num_start_cref = packet_it->first;
6396 if (seq_num_start)
6397 {
6398 *seq_num_start = seq_num_start_cref;
6399 }
6400 if (seq_num_end)
6401 {
6402 *seq_num_end = seq_num_start_cref;
6403 advance_seq_num(seq_num_end, packet_it->second->m_size);
6404 }
6405}
6406
6408{
6409 // Since m_snd_last_order_num starts at 0, this ensures 0 is reserved, as advertised.
6410 return ++sock->m_snd_last_order_num;
6411}
6412
6414{
6415 // Just make a regular net_flow::Peer_socket.
6416 return sock_create_forward_plus_ctor_args<Peer_socket>(opts);
6417}
6418
6419// Free implementations.
6420
6421std::ostream& operator<<(std::ostream& os, const Peer_socket* sock)
6422{
6423 return
6424 sock
6425 ? (os
6426 << "NetFlow_socket "
6427 << "[" << sock->remote_endpoint() << "]<=>[NetFlow [:" << sock->local_port() << "]] "
6428 "@" << static_cast<const void*>(sock))
6429 : (os << "NetFlow_socket@null");
6430}
6431
6432/// @cond
6433/* -^- Doxygen, please ignore the following. (Don't want docs generated for temp macro; this is more maintainable
6434 * than specifying the macro name to omit it, in Doxygen-config EXCLUDE_SYMBOLS.) */
6435
6436// That's right, I did this. Wanna fight about it?
6437#define STATE_TO_CASE_STATEMENT(ARG_state) \
6438 case Peer_socket::Int_state::S_##ARG_state: \
6439 return os << #ARG_state
6440
6441// -v- Doxygen, please stop ignoring.
6442/// @endcond
6443
6444std::ostream& operator<<(std::ostream& os, Peer_socket::Int_state state)
6445{
6446 switch (state)
6447 {
6448 STATE_TO_CASE_STATEMENT(CLOSED);
6449 STATE_TO_CASE_STATEMENT(SYN_SENT);
6450 STATE_TO_CASE_STATEMENT(SYN_RCVD);
6451 STATE_TO_CASE_STATEMENT(ESTABLISHED);
6452 }
6453 return os;
6454#undef STATE_TO_CASE_STATEMENT
6455}
6456
6457} // namespace flow::net_flow
const Component & get_log_component() const
Returns reference to the stored Component object, particularly as many FLOW_LOG_*() macros expect.
Definition: log.cpp:272
Logger * get_logger() const
Returns the stored Logger pointer, particularly as many FLOW_LOG_*() macros expect.
Definition: log.cpp:260
Interface that the user should implement, passing the implementing Logger into logging classes (Flow'...
Definition: log.hpp:1392
static Congestion_control_strategy * create_strategy(Strategy_choice strategy_choice, log::Logger *logger_ptr, Peer_socket::Const_ptr sock)
Factory method that, given an enum identifying the desired strategy, allocates the appropriate Conges...
Definition: cong_ctl.cpp:101
static Ptr create_drop_timer(log::Logger *logger_ptr, util::Task_engine *node_task_engine, Fine_duration *sock_drop_timeout, Peer_socket::Const_ptr &&sock, const Function< void(const Error_code &err_code)> &timer_failure, const Function< void(bool drop_all_packets)> &timer_fired)
Constructs Drop_timer and returns a ref-counted pointer wrapping it.
Definition: drop_timer.cpp:28
@ S_PEER_SOCKET_WRITABLE
Event type specifying the condition of interest wherein a target Peer_socket sock is such that callin...
@ S_PEER_SOCKET_READABLE
Event type specifying the condition of interest wherein a target Peer_socket sock is such that callin...
An object of this class is a single Flow-protocol networking node, in the sense that: (1) it has a di...
Definition: node.hpp:934
void snd_flying_pkts_updated(Peer_socket::Ptr sock, Peer_socket::Sent_pkt_ordered_by_when_const_iter pkt_begin, const Peer_socket::Sent_pkt_ordered_by_when_const_iter &pkt_end, bool added)
Updates Peer_socket::m_snd_flying_bytes according to an operation (add packets, remove packets) calle...
bool categorize_individual_ack(const Socket_id &socket_id, Peer_socket::Ptr sock, Ack_packet::Individual_ack::Const_ptr ack, bool *dupe_or_late, Peer_socket::Sent_pkt_ordered_by_when_iter *acked_pkt_it)
Helper of perform_accumulated_on_recv_tasks() that categorizes the given accumulated individual ackno...
void handle_data_to_established(const Socket_id &socket_id, Peer_socket::Ptr sock, boost::shared_ptr< Data_packet > packet, bool syn_rcvd_qd_packet)
Handles a just-deserialized, just-demultiplexed, low-level DATA packet delivered to the given peer so...
bool sock_is_writable(const boost::any &sock_as_any) const
Returns true if and only if calling sock->send() with at least some arguments would return either non...
Peer_socket_info sock_info(Peer_socket::Const_ptr sock)
Implementation of sock->info() for socket sock in all cases except when sock->state() == Peer_socket:...
void receive_wnd_updated(Peer_socket::Ptr sock)
Placed by receive() onto W if it has dequeued data from Receive buffer and given it to the user,...
void sock_track_new_data_after_gap_rexmit_off(Peer_socket::Ptr sock, boost::shared_ptr< const Data_packet > packet, size_t data_size, bool *slide, size_t *slide_size)
Helper for handle_data_to_established() that aims to register the given DATA packet as an out-of-orde...
Peer_socket::Ptr sync_connect_with_metadata(const Remote_endpoint &to, const boost::chrono::duration< Rep, Period > &max_wait, const boost::asio::const_buffer &serialized_metadata, Error_code *err_code=nullptr, const Peer_socket_options *opts=nullptr)
A combination of sync_connect() and connect_with_metadata() (blocking connect, with supplied metadata...
Definition: node.hpp:3914
bool sock_data_to_reassembly_q_unless_overflow(Peer_socket::Ptr sock, boost::shared_ptr< Data_packet > packet)
Helper for handle_data_to_established() that aims to register the given DATA packet as an out-of-orde...
static bool ensure_sock_open(Socket_ptr sock, Error_code *err_code)
Helper method that checks whether the given Peer_socket or Server_socket is CLOSED; if so,...
Definition: node.hpp:4099
void send_worker(Peer_socket::Ptr sock, bool defer_delta_check)
Thread W implemention of send(): synchronously or asynchronously send the contents of sock->m_snd_buf...
void handle_accumulated_acks(const Socket_id &socket_id, Peer_socket::Ptr sock)
Helper of perform_accumulated_on_recv_tasks() that handles any incoming acknowledgments and rcv_wnd u...
void async_rcv_wnd_recovery(Peer_socket::Ptr sock, size_t rcv_wnd)
receive_wnd_updated() helper that continues rcv_wnd recovery: that is, sends unsolicited ACK with a r...
void log_accumulated_acks(Peer_socket::Const_ptr sock) const
Helper of handle_accumulated_acks() that logs the about-to-be-handled accumulated individual acknowle...
void sock_free_memory(Peer_socket::Ptr sock)
Helper that clears all non-O(1)-space data structures stored inside sock.
void sock_load_info_struct(Peer_socket::Const_ptr sock, Peer_socket_info *stats) const
Given a Peer_socket, copies all stats info (as available via Peer_socket::info()) from various struct...
void log_snd_window(Peer_socket::Const_ptr sock, bool force_verbose_info_logging=false) const
Logs TRACE or DATA messages thats show the detailed state of the sending sequence number space.
void send_worker_check_state(Peer_socket::Ptr sock)
Helper placed by send() onto W to invoke send_worker() but ensures that the socket has not entered so...
size_t m_low_lvl_max_buf_size
OS-reported m_low_lvl_sock UDP receive buffer maximum size, obtained right after we OS-set that setti...
Definition: node.hpp:3735
Non_blocking_func_ret_type sync_op(typename Socket::Ptr sock, const Function< Non_blocking_func_ret_type()> &non_blocking_func, Non_blocking_func_ret_type would_block_ret_val, Event_set::Event_type ev_type, const Fine_time_pt &wait_until, Error_code *err_code)
Implementation of core blocking transfer methods, namely Peer_socket::sync_send(),...
Definition: node.hpp:3935
size_t sock_max_packets_after_unrecvd_packet(Peer_socket::Const_ptr sock) const
Computes and returns the max size for Peer_socket::m_rcv_packets_with_gaps for sock.
Peer_socket::Sent_pkt_ordered_by_when_iter categorize_pkts_as_dropped_on_acks(Peer_socket::Ptr sock, const boost::unordered_set< Peer_socket::order_num_t > &flying_now_acked_pkts)
Helper of perform_accumulated_on_recv_tasks() that determines the range of In-flight packets that sho...
void rcv_get_first_gap_info(Peer_socket::Const_ptr sock, bool *first_gap_exists, Sequence_number *seq_num_after_first_gap)
Helper for handle_data_to_established() that gets simple info about Peer_socket::m_rcv_packets_with_g...
bool snd_deqable(Peer_socket::Const_ptr sock) const
Return true if and only if there are enough data either in Peer_socket::m_snd_rexmit_q of sock (if re...
Peer_socket::Ptr sync_connect(const Remote_endpoint &to, const boost::chrono::duration< Rep, Period > &max_wait, Error_code *err_code=nullptr, const Peer_socket_options *opts=nullptr)
The blocking (synchronous) version of connect().
Definition: node.hpp:3925
void async_low_lvl_ack_send(Peer_socket::Ptr sock, const Error_code &sys_err_code={})
Sends a low-level ACK packet, with all accumulated in Peer_socket::m_rcv_pending_acks of sock individ...
void cancel_timers(Peer_socket::Ptr sock)
Cancel any timers and scheduled tasks active in the given socket.
void sock_rcv_buf_now_readable(Peer_socket::Ptr sock, bool syn_rcvd_qd_packet)
Helper for handle_data_to_established() that assumes the given's socket Receive buffer is currently r...
void snd_flying_pkts_erase_one(Peer_socket::Ptr sock, Peer_socket::Sent_pkt_ordered_by_when_iter pkt_it)
Erases (for example if considered Acknowledged or Dropped) a packet struct from the "scoreboard" (Pee...
Opt_type opt(const Opt_type &opt_val_ref) const
Obtain a copy of the value of a given option in a thread-safe manner.
Definition: node.hpp:4138
bool sock_validate_options(const Peer_socket_options &opts, const Peer_socket_options *prev_opts, Error_code *err_code) const
Analogous to validate_options() but checks per-socket options instead of per-Node options.
void handle_accumulated_pending_acks(const Socket_id &socket_id, Peer_socket::Ptr sock)
Helper of perform_accumulated_on_recv_tasks() that handles any additional individual outgoing acknowl...
void receive_wnd_recovery_data_received(Peer_socket::Ptr sock)
Pertaining to the async_rcv_wnd_recovery() mechanism, this handles the event that we have received an...
static Peer_socket::order_num_t sock_get_new_snd_order_num(Peer_socket::Ptr sock)
Returns the "order number" to use for Peer_socket::Sent_packet::Sent_when structure corresponding to ...
Peer_socket::Ptr sync_connect_impl(const Remote_endpoint &to, const Fine_duration &max_wait, const boost::asio::const_buffer &serialized_metadata, Error_code *err_code, const Peer_socket_options *opts)
Implementation core of sync_connect*() that gets rid of templated or missing arguments thereof.
size_t max_block_size() const
The maximum number of bytes of user data per received or sent block on connections generated from thi...
Definition: node.cpp:1115
void snd_flying_pkts_push_one(Peer_socket::Ptr sock, const Sequence_number &seq_num, Peer_socket::Sent_packet::Ptr sent_pkt)
Adds a new packet struct (presumably representing packet to be sent shortly) to the "scoreboard" (Pee...
Syn_packet::Ptr create_syn(Peer_socket::Const_ptr sock)
Helper that creates a new SYN packet object to the extent that is suitable for immediately passing to...
Peer_socket::Ptr connect_with_metadata(const Remote_endpoint &to, const boost::asio::const_buffer &serialized_metadata, Error_code *err_code=nullptr, const Peer_socket_options *opts=nullptr)
Same as connect() but sends, as part of the connection handshake, the user-supplied metadata,...
void close_abruptly(Peer_socket::Ptr sock, Error_code *err_code)
Implementation of non-blocking sock->close_abruptly() for socket sock in all cases except when sock->...
static void get_seq_num_range(const Packet_map_iter &packet_it, Sequence_number *seq_num_start, Sequence_number *seq_num_end)
Given an iterator into a Peer_socket::Sent_pkt_by_sent_when_map or Peer_socket::Recv_pkt_map,...
Syn_ack_packet::Ptr create_syn_ack(Peer_socket::Const_ptr sock)
Like create_syn() but for SYN_ACK.
virtual Peer_socket * sock_create(const Peer_socket_options &opts)
Internal factory used for ALL Peer_socket objects created by this Node (including subclasses).
bool snd_buf_enqable(Peer_socket::Const_ptr sock) const
Return true if and only if there is enough free space in Peer_socket::m_snd_buf of sock to enqueue an...
bool can_send(Peer_socket::Const_ptr sock) const
Answers the perennial question of congestion and flow control: assuming there is a DATA packet to sen...
void sock_slide_rcv_next_seq_num(Peer_socket::Ptr sock, size_t slide_size, bool reassembly_in_progress)
Helper for handle_data_to_established() that aims to register a set of received DATA packet data as i...
void sock_log_detail(Peer_socket::Const_ptr sock) const
Logs a verbose state report for the given socket.
Peer_socket::Ptr connect(const Remote_endpoint &to, Error_code *err_code=nullptr, const Peer_socket_options *opts=nullptr)
Initiates an active connect to the specified remote Flow server.
static void advance_seq_num(Sequence_number *seq_num, boost::shared_ptr< const Data_packet > data)
Assuming *seq_num points to the start of data.m_data, increments *seq_num to point to the datum just ...
static Sequence_number snd_past_last_flying_datum_seq_num(Peer_socket::Const_ptr sock)
Obtain the sequence number for the datum just past the last (latest) In-flight (i....
void event_set_all_check_delta(bool defer_delta_check)
For each WAITING Event_set within the Node: checks for any events that hold, and if any do hold,...
Definition: event_set.cpp:1127
void serv_peer_socket_closed(Server_socket::Ptr serv, Peer_socket::Ptr sock)
Records that a Server_socket-contained (i.e., currently un-established, or established but not yet ac...
bool rcv_buf_deqable(Peer_socket::Const_ptr sock) const
Return true if and only if there are enough data in Peer_socket::m_rcv_buf of sock to give the user s...
void async_acknowledge_packet(Peer_socket::Ptr sock, const Sequence_number &seq_num, unsigned int rexmit_id, size_t data_size)
Causes an acknowledgment of the given received packet to be included in a future Ack_packet sent to t...
Socket_id_to_socket_map m_socks
The peer-to-peer connections this Node is currently tracking.
Definition: node.hpp:3750
Peer_socket::Options_lock Options_lock
Short-hand for lock that acquires exclusive access to an Options_mutex.
Definition: node.hpp:1436
static Socket_id socket_id(Peer_socket::Const_ptr sock)
Constructs the socket pair (connection ID) for the given socket.
void handle_syn_ack_to_syn_sent(const Socket_id &socket_id, Peer_socket::Ptr sock, boost::shared_ptr< const Syn_ack_packet > syn_ack)
Handles a just-deserialized, just-demultiplexed low-level SYN_ACK packet delivered to the given peer ...
size_t send(Peer_socket::Ptr sock, const Function< size_t(size_t max_data_size)> &snd_buf_feed_func, Error_code *err_code)
Implementation of non-blocking sock->send() for socket sock in all cases except when sock->state() ==...
void sock_set_int_state(Peer_socket::Ptr sock, Peer_socket::Int_state new_state)
Sets internal state of given socket to the given state and logs a TRACE message about it.
bool sock_is_readable(const boost::any &sock_as_any) const
Returns true if and only if calling sock->receive() with at least some arguments would return either ...
bool sock_data_to_rcv_buf_unless_overflow(Peer_socket::Ptr sock, boost::shared_ptr< Data_packet > packet)
Helper for handle_data_to_established() that aims to pass the payload of the given DATA packet to the...
bool sock_set_options(Peer_socket::Ptr sock, const Peer_socket_options &opts, Error_code *err_code)
Thread W implementation of sock->set_options().
bool running() const
Returns true if and only if the Node is operating.
Definition: node.cpp:420
Port_to_server_map m_servs
The server sockets this Node is currently tracking.
Definition: node.hpp:3756
Event_set::Ev_type_to_socks_map m_sock_events
All sockets that have been detected to be "ready" (by the Event_set doc header definition) at any poi...
Definition: node.hpp:3788
static const uint8_t S_DEFAULT_CONN_METADATA
Type and value to supply as user-supplied metadata in SYN, if user chooses to use [[a]sync_]connect()...
Definition: node.hpp:1400
void setup_drop_timer(const Socket_id &socket_id, Peer_socket::Ptr sock)
Creates a new Drop Timer and saves it to sock->m_snd_drop_timer.
void handle_ack_to_established(Peer_socket::Ptr sock, boost::shared_ptr< const Ack_packet > ack)
Handles a just-deserialized, just-demultiplexed, low-level ACK packet delivered to the given peer soc...
void handle_syn_ack_to_established(Peer_socket::Ptr sock, boost::shared_ptr< const Syn_ack_packet > syn_ack)
Handles a just-deserialized, just-demultiplexed, duplicate (equal to already-received SYN_ACK) low-le...
void setup_connection_timers(const Socket_id &socket_id, Peer_socket::Ptr sock, bool initial)
Assuming we've just sent SYN or SYN_ACK, sets up an asynchronous scheduled task to fire within some a...
void log_rcv_window(Peer_socket::Const_ptr sock, bool force_verbose_info_logging=false) const
Logs TRACE or DATA messages that show the detailed state of the receiving sequence number space.
size_t sock_rcv_wnd(Peer_socket::Const_ptr sock) const
Computes and returns the currently correct rcv_wnd value; that is the amount of space free in Receive...
void connect_worker(const Remote_endpoint &to, const boost::asio::const_buffer &serialized_metadata, const Peer_socket_options *opts, Peer_socket::Ptr *sock)
Thread W implementation of connect().
bool drop_pkts_on_acks(Peer_socket::Ptr sock, const Peer_socket::Sent_pkt_ordered_by_when_iter &last_dropped_pkt_it, size_t *cong_ctl_dropped_pkts, size_t *cong_ctl_dropped_bytes, size_t *dropped_pkts, size_t *dropped_bytes, std::vector< Peer_socket::order_num_t > *pkts_marked_to_drop)
Helper of perform_accumulated_on_recv_tasks() that acts on the determination made by categorize_pkts_...
static const Peer_socket::Sent_packet::ack_count_t S_MAX_LATER_ACKS_BEFORE_CONSIDERING_DROPPED
For a given unacknowledged sent packet P, the maximum number of times any individual packet with high...
Definition: node.hpp:3650
Error_code sock_categorize_data_to_established(Peer_socket::Ptr sock, boost::shared_ptr< const Data_packet > packet, bool *dupe, bool *slide, size_t *slide_size)
Helper for handle_data_to_established() that categorizes the DATA packet received as either illegal; ...
void async_sock_low_lvl_rst_send(Peer_socket::Ptr sock)
Sends an RST to the other side of the given socket asynchronously when possible.
Definition: low_lvl_io.cpp:992
void sock_set_state(Peer_socket::Ptr sock, Peer_socket::State state, Peer_socket::Open_sub_state open_sub_state=Peer_socket::Open_sub_state::S_CONNECTED)
Sets Peer_socket::m_state and Peer_socket::m_open_sub_state.
void receive_emptied_rcv_buf_while_disconnecting(Peer_socket::Ptr sock)
Placed by receive() onto W during a graceful close, after the Receive buffer had been emptied by the ...
void sock_disconnect_detected(Peer_socket::Ptr sock, const Error_code &disconnect_cause, bool close)
Records that thread W shows underlying connection is broken (graceful termination,...
size_t receive(Peer_socket::Ptr sock, const Function< size_t()> &rcv_buf_consume_func, Error_code *err_code)
Implementation of non-blocking sock->receive() for socket sock in all cases except when sock->state()...
void handle_connection_rexmit_timer_event(const Socket_id &socket_id, Peer_socket::Ptr sock)
Handles the triggering of the retransmit timer wait set up by setup_connection_timers(); it will re-s...
Node_options m_opts
This Node's global set of options.
Definition: node.hpp:3668
void close_connection_immediately(const Socket_id &socket_id, Peer_socket::Ptr sock, const Error_code &err_code, bool defer_delta_check)
A thread W method that handles the transition of the given socket from OPEN (any sub-state) to CLOSED...
void sock_disconnect_completed(Peer_socket::Ptr sock)
While in S_OPEN+S_DISCONNECTING state (i.e., after beginning a graceful close with sock_disconnect_de...
Fine_duration compute_rtt_on_ack(Peer_socket::Sent_packet::Const_ptr flying_pkt, const Fine_time_pt &time_now, Ack_packet::Individual_ack::Const_ptr ack, const Peer_socket::Sent_packet::Sent_when **sent_when) const
Helper of perform_accumulated_on_recv_tasks() that computes the RTT implied by a given individual ack...
void async_low_lvl_syn_ack_ack_send(const Peer_socket::Ptr &sock, boost::shared_ptr< const Syn_ack_packet > &syn_ack)
Helper to create, fully fill out, and asynchronously send via async_sock_low_lvl_packet_send_paced() ...
void new_round_trip_time_sample(Peer_socket::Ptr sock, Fine_duration round_trip_time)
Handles a just-computed new RTT (round trip time) measurement for an individual packet earlier sent: ...
void async_sock_low_lvl_packet_send_paced(const Peer_socket::Ptr &sock, Low_lvl_packet::Ptr &&packet)
Begins the process of asynchronously sending the given low-level packet to the remote Node specified ...
Definition: low_lvl_io.cpp:603
bool ok_to_rexmit_or_close(Peer_socket::Ptr sock, const Peer_socket::Sent_pkt_ordered_by_when_iter &pkt_it, bool defer_delta_check)
Checks whether the given sent packet has been retransmitted the maximum number of allowed times; if s...
util::Task_engine m_task_engine
The main loop engine, functioning in the single-threaded-but-asynchronous callback-based "reactor" st...
Definition: node.hpp:3703
Port_space m_ports
Flow port space for both client and server sockets. All threads may access this.
Definition: node.hpp:3741
void rst_and_close_connection_immediately(const Socket_id &socket_id, Peer_socket::Ptr sock, const Error_code &err_code, bool defer_delta_check)
Asynchronously send RST to the other side of the given socket and close_connection_immediately().
void drop_timer_action(Peer_socket::Ptr sock, bool drop_all_packets)
Handles a Drop_timer (Peer_socket::m_snd_drop_timer) event in ESTABLISHED state by dropping the speci...
A class that keeps a Peer_socket_receive_stats data store, includes methods to conveniently accumulat...
void good_data_accepted_packet(size_t data)
Indicates good_data_packet(), and these data are not dropped (so either delivered into Receive buffer...
void good_data_dropped_reassembly_q_overflow_packet(size_t data)
Indicates good_data_packet(), but these data are dropped due to insufficient Receive reassembly queue...
void presumed_dropped_data(size_t data)
Indicates that one or more unreceived data packets have been considered Dropped due to the number of ...
void good_data_delivered_packet(size_t data)
Indicates good_data_accepted_packet(), and these data are delivered into Receive buffer (either immed...
void late_or_dupe_to_send_ack_packet(size_t data)
Indicates that late_or_dupe_data_packet() and therefore an individual acknowledgment for this packet ...
void total_data_packet(size_t data)
Indicates one DATA packet has been received on socket.
void good_to_send_ack_packet(size_t data)
Indicates that good_data_delivered_packet() and therefore an individual acknowledgment for this packe...
void good_data_packet(size_t data)
Indicates total_data_packet(), and these data are new and acceptable into Receive buffer assuming the...
void error_data_packet(size_t data)
Indicates total_data_packet(), but there is some error about the sequence numbers so that they are no...
void buffer_fed(size_t size)
Indicates the Receive buffer was enqueued with data from network (so its data_size() increased).
void good_data_first_qd_packet(size_t data)
Indicates good_data_accepted_packet(), and these data are, upon receipt, queued for reassembly (not i...
void good_data_dropped_buf_overflow_packet(size_t data)
Indicates good_data_packet(), but these data are dropped due to insufficient Receive buffer space.
void late_or_dupe_data_packet(size_t data)
Indicates total_data_packet(), but the arrived data have either already been received before or (more...
A peer (non-server) socket operating over the Flow network protocol, with optional stream-of-bytes an...
bool sync_send_reactor_pattern_impl(const Fine_time_pt &wait_until, Error_code *err_code)
Helper similar to sync_send_impl() but for the nullptr_t versions of sync_send().
void close_abruptly(Error_code *err_code=nullptr)
Acts as if fatal error error::Code::S_USER_CLOSED_ABRUPTLY has been discovered on the connection.
std::map< Sequence_number, Sent_pkt_ordered_by_when_iter > Sent_pkt_by_seq_num_map
Short-hand for m_snd_flying_pkts_by_seq_num type; see that data member.
bool sync_receive_reactor_pattern_impl(const Fine_time_pt &wait_until, Error_code *err_code)
Helper similar to sync_receive_impl() but for the nullptr_t versions of sync_receive().
Remote_endpoint m_remote_endpoint
See remote_endpoint(). Should be set before user gets access to *this and not changed afterwards.
util::Blob m_serialized_metadata
If !m_active_connect, this contains the serialized metadata that the user supplied on the other side ...
size_t node_sync_send(const Function< size_t(size_t max_data_size)> &snd_buf_feed_func_or_empty, const Fine_time_pt &wait_until, Error_code *err_code)
This is to sync_send() as node_send() is to send().
Error_code m_disconnect_cause
The Error_code causing disconnection (if one has occurred or is occurring) on this socket; otherwise ...
Peer_socket(log::Logger *logger_ptr, util::Task_engine *task_engine, const Peer_socket_options &opts)
Constructs object; initializes most values to well-defined (0, empty, etc.) but not necessarily meani...
Definition: peer_socket.cpp:38
Sequence_number m_rcv_init_seq_num
The Initial Sequence Number (ISN) contained in the original Syn_packet or Syn_ack_packet we received.
const Remote_endpoint & remote_endpoint() const
Intended other side of the connection (regardless of success, failure, or current State).
State
State of a Peer_socket.
@ S_OPEN
Future reads or writes may be possible. A socket in this state may be Writable or Readable.
@ S_CLOSED
Neither future reads nor writes are possible, AND Node has disowned the Peer_socket.
Open_sub_state
The sub-state of a Peer_socket when state is State::S_OPEN.
@ S_CONNECTED
This Peer_socket was created through a passive connect (Node::accept() and the like) or an active con...
@ S_CONNECTING
This Peer_socket was created through an active connect (Node::connect() and the like),...
@ S_DISCONNECTING
This Peer_socket was created through a passive connect (Node::accept() and the like) or an active con...
size_t sync_receive(const Mutable_buffer_sequence &target, const boost::chrono::duration< Rep, Period > &max_wait, Error_code *err_code=nullptr)
Blocking (synchronous) version of receive().
size_t sync_send(const Const_buffer_sequence &data, const boost::chrono::duration< Rep, Period > &max_wait, Error_code *err_code=nullptr)
Blocking (synchronous) version of send().
~Peer_socket() override
Boring virtual destructor. Note that deletion is to be handled exclusively via shared_ptr,...
Definition: peer_socket.cpp:78
Error_code disconnect_cause() const
The error code that perviously caused state() to become State::S_CLOSED, or success code if state is ...
Sequence_number::seq_num_t order_num_t
Short-hand for order number type. 0 is reserved. Caution: Keep in sync with Drop_timer::packet_id_t.
flow_port_t local_port() const
The local Flow-protocol port chosen by the Node (if active or passive open) or user (if passive open)...
flow_port_t m_local_port
See local_port(). Should be set before user gets access to *this and not changed afterwards.
size_t node_send(const Function< size_t(size_t max_data_size)> &snd_buf_feed_func, Error_code *err_code)
Non-template helper for template send() that forwards the send() logic to Node::send().
bool rexmit_on() const
Whether retransmission is enabled on this connection.
size_t node_sync_receive(const Function< size_t()> &rcv_buf_consume_func_or_empty, const Fine_time_pt &wait_until, Error_code *err_code)
This is to sync_receive() as node_receive() is to receive().
State state(Open_sub_state *open_sub_state=nullptr) const
Current State of the socket.
Definition: peer_socket.cpp:86
util::Lock_guard< Mutex > Lock_guard
Short-hand for RAII lock guard of Mutex.
Int_state
The state of the socket (and the connection from this end's point of view) for the internal state mac...
@ S_ESTABLISHED
Public state is OPEN+CONNECTED; in our opinion the connection is established.
@ S_SYN_SENT
Public state is OPEN+CONNECTING; user requested active connect; we sent SYN and are awaiting response...
@ S_CLOSED
Closed (dead or new) socket.
@ S_SYN_RCVD
Public state is OPEN+CONNECTING; other side requested passive connect via SYN; we sent SYN_ACK and ar...
util::Lock_guard< Options_mutex > Options_lock
Short-hand for lock that acquires exclusive access to an Options_mutex.
size_t max_block_size() const
The maximum number of bytes of user data per received or sent packet on this connection.
Node * node() const
Node that produced this Peer_socket.
Definition: peer_socket.cpp:96
Peer_socket_info info() const
Returns a structure containing the most up-to-date stats about this connection.
Recvd_pkt_map::iterator Recvd_pkt_iter
Short-hand for m_rcv_packets_with_gaps iterator type.
Mutex m_mutex
This object's mutex.
Sent_pkt_by_sent_when_map::iterator Sent_pkt_ordered_by_when_iter
Short-hand for m_snd_flying_pkts_by_sent_when iterator type.
size_t max_block_size_multiple(const size_t &opt_val_ref, const unsigned int *inflate_pct_val_ptr=nullptr) const
Returns the smallest multiple of max_block_size() that is >= the given option value,...
Sent_pkt_by_seq_num_map::const_iterator Sent_pkt_ordered_by_seq_const_iter
Short-hand for m_snd_flying_pkts_by_seq_num const iterator type.
Peer_socket_info m_info_on_close
This is the final set of stats collected at the time the socket was moved to S_CLOSED m_state.
bool ensure_open(Error_code *err_code) const
Helper that is equivalent to Node::ensure_sock_open(this, err_code).
Sent_pkt_by_sent_when_map::const_iterator Sent_pkt_ordered_by_when_const_iter
Short-hand for m_snd_flying_pkts_by_sent_when const iterator type.
Opt_type opt(const Opt_type &opt_val_ref) const
Analogous to Node::opt() but for per-socket options.
std::string bytes_blocks_str(size_t bytes) const
Helper that, given a byte count, returns a string with that byte count and the number of max_block_si...
size_t get_connect_metadata(const boost::asio::mutable_buffer &buffer, Error_code *err_code=nullptr) const
Obtains the serialized connect metadata, as supplied by the user during the connection handshake.
Peer_socket_options m_opts
This socket's per-socket set of options.
Peer_socket_options options() const
Copies this socket's option set and returns that copy.
Options_mutex m_opts_mutex
The mutex protecting m_opts.
std::map< Sequence_number, boost::shared_ptr< Received_packet > > Recvd_pkt_map
Short-hand for m_rcv_packets_with_gaps type; see that data member.
Recvd_pkt_map::const_iterator Recvd_pkt_const_iter
Short-hand for m_rcv_packets_with_gaps const iterator type.
Open_sub_state m_open_sub_state
See state().
bool set_options(const Peer_socket_options &opts, Error_code *err_code=nullptr)
Dynamically replaces the current options set (options()) with the given options set.
size_t node_receive(const Function< size_t()> &rcv_buf_consume_func, Error_code *err_code)
Non-template helper for template receive() that forwards the receive() logic to Node::receive().
void return_port(flow_port_t port, Error_code *err_code)
Return a previously reserved port (of any type).
Definition: port_space.cpp:176
A per-Peer_socket module that tries to estimate the bandwidth available to the outgoing flow.
Definition: bandwidth.hpp:125
An internal net_flow sequence number identifying a piece of data.
Definition: seq_num.hpp:126
uint64_t seq_num_t
Raw sequence number type.
Definition: seq_num.hpp:138
void set_metadata(char num_line_id=0, const Sequence_number &zero_point={}, seq_num_delta_t multiple_size=0)
Updates the full set of metadata (used at least for convenient convention-based logging but not actua...
Definition: seq_num.cpp:245
Internal net_flow class that implements a socket buffer, as used by Peer_socket for Send and Receive ...
void consume_buf_move(util::Blob *target_buf, size_t max_data_size)
Consumes (removes from the front of the internal byte buffer and returns them to the caller) a byte s...
size_t data_size() const
The total number of bytes of application-layer data stored in this object.
Properties of various container types.
Definition: traits.hpp:47
typename Value_list::const_reverse_iterator Const_reverse_iterator
Type for reverse iterator pointing into an immutable structure of this type.
std::pair< Iterator, bool > insert(const Value &key_and_mapped)
Attempts to insert (copying both key and mapped-value) the given key/mapped-value pair into the map; ...
typename Value_list::reverse_iterator Reverse_iterator
Type for reverse iterator pointing into a mutable structure of this type.
static Ptr ptr_cast(const From_ptr &ptr_to_cast)
Provides syntactic-sugary way to perform a static_pointer_cast<> from a compatible smart pointer type...
boost::shared_ptr< Peer_socket > Ptr
Short-hand for ref-counted pointer to mutable values of type Target_type::element_type (a-la T*).
Const_target_ptr Const_ptr
Short-hand for ref-counted pointer to immutable values of type Target_type::element_type (a-la T cons...
Similar to ostringstream but allows fast read-only access directly into the std::string being written...
#define FLOW_ERROR_SYS_ERROR_LOG_WARNING()
Logs a warning about the (often errno-based or from a library) error code in sys_err_code.
Definition: error.hpp:269
#define FLOW_ERROR_LOG_ERROR(ARG_val)
Logs a warning about the given error code using FLOW_LOG_WARNING().
Definition: error.hpp:233
#define FLOW_ERROR_EXEC_AND_THROW_ON_ERROR(ARG_ret_type, ARG_function_name,...)
Narrow-use macro that implements the error code/exception semantics expected of most public-facing Fl...
Definition: error.hpp:363
#define FLOW_ERROR_EMIT_ERROR(ARG_val)
Sets *err_code to ARG_val and logs a warning about the error using FLOW_LOG_WARNING().
Definition: error.hpp:202
#define FLOW_ERROR_EMIT_ERROR_LOG_INFO(ARG_val)
Identical to FLOW_ERROR_EMIT_ERROR(), but the message logged has flow::log::Sev::S_INFO severity inst...
Definition: error.hpp:218
#define FLOW_LOG_DATA(ARG_stream_fragment)
Logs a DATA message into flow::log::Logger *get_logger() with flow::log::Component get_log_component(...
Definition: log.hpp:245
#define FLOW_LOG_INFO(ARG_stream_fragment)
Logs an INFO message into flow::log::Logger *get_logger() with flow::log::Component get_log_component...
Definition: log.hpp:200
#define FLOW_LOG_WITHOUT_CHECKING(ARG_sev, ARG_stream_fragment)
Identical to FLOW_LOG_WITH_CHECKING() but foregoes the filter (Logger::should_log()) check.
Definition: log.hpp:617
#define FLOW_LOG_WARNING(ARG_stream_fragment)
Logs a WARNING message into flow::log::Logger *get_logger() with flow::log::Component get_log_compone...
Definition: log.hpp:155
#define FLOW_LOG_WITH_CHECKING(ARG_sev, ARG_stream_fragment)
Logs a message of the specified severity into flow::log::Logger *get_logger() with flow::log::Compone...
Definition: log.hpp:574
#define FLOW_LOG_TRACE_WITHOUT_CHECKING(ARG_stream_fragment)
Logs a TRACE message into flow::log::Logger *get_logger() with flow::log::Component get_log_component...
Definition: log.hpp:422
#define FLOW_LOG_DATA_WITHOUT_CHECKING(ARG_stream_fragment)
Logs a DATA message into flow::log::Logger *get_logger() with flow::log::Component get_log_component(...
Definition: log.hpp:440
#define FLOW_LOG_TRACE(ARG_stream_fragment)
Logs a TRACE message into flow::log::Logger *get_logger() with flow::log::Component get_log_component...
Definition: log.hpp:230
Synchronicity
Enumeration indicating the manner in which asio_exec_ctx_post(), and various boost....
Definition: async_fwd.hpp:223
void asio_exec_ctx_post(log::Logger *logger_ptr, Execution_context *exec_ctx, Synchronicity synchronicity, Task &&task)
An extension of boost.asio's post() and dispatch() free function templates, this free function templa...
Definition: util.hpp:31
bool exec_void_and_throw_on_error(const Func &func, Error_code *err_code, util::String_view context)
Equivalent of exec_and_throw_on_error() for operations with void return type.
Definition: error.hpp:168
@ S_DATA
Message satisfies Sev::S_TRACE description AND contains variable-length structure (like packet,...
@ S_TRACE
Message indicates any condition that may occur with great frequency (thus verbose if logged).
@ S_INFO
Message indicates a not-"bad" condition that is not frequent enough to be of severity Sev::S_TRACE.
@ S_CONN_TIMEOUT
Other side did not complete connection handshake within the allowed time; perhaps no one is listening...
@ S_USER_CLOSED_ABRUPTLY
User code on this side abruptly closed connection; other side may be informed of this.
@ S_CONN_RESET_TOO_MANY_REXMITS
Connection reset because a packet has been retransmitted too many times.
@ S_SEQ_NUM_IMPLIES_CONNECTION_COLLISION
Other side has sent packet with sequence number that implies a port collision between two connections...
@ S_SEQ_NUM_ARITHMETIC_FAILURE
Other side has sent packets with inconsistent sequence numbers.
@ S_CONN_METADATA_TOO_LARGE
During connection user supplied metadata that is too large.
@ S_CANNOT_CONNECT_TO_IP_ANY
Cannot ask to connect to "any" IP address. Use specific IP address.
@ S_WAIT_USER_TIMEOUT
A blocking (sync_) or background-blocking (async_) operation timed out versus user-supplied time limi...
@ S_WAIT_INTERRUPTED
A blocking (sync_) or background-blocking (async_) operation was interrupted, such as by a signal.
@ S_EVENT_SET_CLOSED
Attempted operation on an event set, when that event set was closed.
@ S_INTERNAL_ERROR_PORT_COLLISION
Internal error: Ephemeral port double reservation allowed.
@ S_NODE_NOT_RUNNING
Node not running.
Flow module containing the API and implementation of the Flow network protocol, a TCP-inspired stream...
Definition: node.cpp:25
uint16_t flow_port_t
Logical Flow port type (analogous to a UDP/TCP port in spirit but in no way relevant to UDP/TCP).
const flow_port_t S_PORT_ANY
Special Flow port value used to indicate "invalid port" or "please pick a random available ephemeral ...
Definition: port_space.cpp:35
std::ostream & operator<<(std::ostream &os, const Congestion_control_selector::Strategy_choice &strategy_choice)
Serializes a Peer_socket_options::Congestion_control_strategy_choice enum to a standard ostream – the...
Definition: cong_ctl.cpp:146
bool key_exists(const Container &container, const typename Container::key_type &key)
Returns true if and only if the given key is present at least once in the given associative container...
Definition: util.hpp:511
std::string buffers_dump_string(const Const_buffer_sequence &data, const std::string &indentation, size_t bytes_per_line)
Identical to buffers_to_ostream() but returns an std::string instead of writing to a given ostream.
Definition: util.hpp:699
bool subtract_with_floor(Minuend *minuend, const Subtrahend &subtrahend, const Minuend &floor)
Performs *minuend -= subtrahend, subject to a floor of floor.
Definition: util.hpp:527
bool in_open_open_range(T const &min_val, T const &val, T const &max_val)
Returns true if and only if the given value is within the given range, given as a (low,...
Definition: util.hpp:505
Scheduled_task_handle schedule_task_from_now(log::Logger *logger_ptr, const Fine_duration &from_now, bool single_threaded, Task_engine *task_engine, Scheduled_task_handler &&task_body_moved)
Schedule the given function to execute in a certain amount of time: A handy wrapper around Timer (asi...
Definition: sched_task.hpp:34
bool scheduled_task_fired(log::Logger *logger_ptr, Scheduled_task_const_handle task)
Returns whether a previously scheduled (by schedule_task_from_now() or similar) task has already fire...
Definition: sched_task.cpp:238
bool in_open_closed_range(T const &min_val, T const &val, T const &max_val)
Returns true if and only if the given value is within the given range, given as a (low,...
Definition: util.hpp:489
Fine_duration scheduled_task_fires_from_now_or_canceled(log::Logger *logger_ptr, Scheduled_task_const_handle task)
Returns how long remains until a previously scheduled (by schedule_task_from_now() or similar) task f...
Definition: sched_task.cpp:200
void ostream_op_to_string(std::string *target_str, T &&... ostream_args)
Writes to the specified string, as if the given arguments were each passed, via << in sequence,...
Definition: util.hpp:561
boost::shared_ptr< Scheduled_task_handle_state > Scheduled_task_handle
Black-box type that represents a handle to a scheduled task as scheduled by schedule_task_at() or sch...
bool in_closed_range(T const &min_val, T const &val, T const &max_val)
Returns true if and only if the given value is within the given range, inclusive.
Definition: util.hpp:481
boost::asio::io_context Task_engine
Short-hand for boost.asio event service, the central class of boost.asio.
Definition: util_fwd.hpp:168
Auto_cleanup setup_auto_cleanup(Cleanup_func &&func)
Provides a way to execute arbitrary (cleanup) code at the exit of the current block.
Definition: util.hpp:517
bool scheduled_task_cancel(log::Logger *logger_ptr, Scheduled_task_handle task)
Attempts to prevent the execution of a previously scheduled (by schedule_task_from_now() or similar) ...
Definition: sched_task.cpp:26
Blob_with_log_context<> Blob
A concrete Blob_with_log_context that compile-time-disables Basic_blob::share() and the sharing API d...
Definition: blob_fwd.hpp:62
constexpr Integer ceil_div(Integer dividend, Integer2 divisor)
Returns the result of the given non-negative integer divided by a positive integer,...
Definition: util.hpp:407
boost::system::error_code Error_code
Short-hand for a boost.system error code (which basically encapsulates an integer/enum error code and...
Definition: common.hpp:497
Flow_log_component
The flow::log::Component payload enumeration comprising various log components used by Flow's own int...
Definition: common.hpp:627
Fine_clock::duration Fine_duration
A high-res time duration as computed from two Fine_time_pts.
Definition: common.hpp:405
Fine_clock::time_point Fine_time_pt
A high-res time point as returned by Fine_clock::now() and suitable for precise time math in general.
Definition: common.hpp:402
unsigned char uint8_t
Byte. Best way to represent a byte of binary data. This is 8 bits on all modern systems.
Definition: common.hpp:380
Specifies the outgoing (pre-serialization) acknowledgment of a single received Data_packet,...
Equivalent of Individual_ack_rexmit_off but for sockets with retransmission enabled.
Specifies the incoming (post-deserialization) acknowledgment of a single received Data_packet.
boost::shared_ptr< const Individual_ack > Const_ptr
Short-hand for ref-counted pointer to immutable objects of this class.
uint64_t ack_delay_t
Type used to store the ACK delay for a given individual acknowledged packet.
Fine_duration Ack_delay_time_unit
Ack_delay_time_unit{1} is the duration corresponding to the ack_delay_t value 1; and proportionally f...
uint32_t rcv_wnd_t
Type used to store the size of m_rcv_wnd member in a couple of different packet types.
uint8_t rexmit_id_t
Type used to store the retransmission count in DATA and ACK packets.
The data nugget uniquely identifying a peer-to-peer connection from a remote endpoint to a port in th...
Definition: node.hpp:3862
Metadata describing the data sent in the acknowledgment of an individual received packet.
boost::shared_ptr< const Individual_ack > Const_ptr
Short-hand for ref-counted pointer to immutable objects of this class.
boost::shared_ptr< Individual_ack > Ptr
Short-hand for ref-counted pointer to mutable objects of this class.
Metadata (and data, if retransmission is on) for a packet that has been received (and,...
const size_t m_size
Number of bytes in the Data_packet::m_data field of that packet.
Received_packet(log::Logger *logger_ptr, size_t size, util::Blob *src_data)
Constructs object by storing size of data and, if so instructed, the data themselves.
util::Blob m_data
Byte sequence equal to that of Data_packet::m_data of the packet.
Data store to keep timing related info when a packet is sent out.
const order_num_t m_order_num
Order number of the packet.
size_t m_sent_cwnd_bytes
The congestion window size (in bytes) that is used when the packet is sent out.
Fine_time_pt m_sent_time
The timestamp when the packet is sent out.
Metadata (and data, if retransmission is on) for a packet that has been sent one (if retransmission i...
Sent_packet(bool rexmit_on, boost::shared_ptr< Data_packet > packet, const Sent_when &sent_when)
Constructs object with the given values and m_acks_after_me at zero.
std::vector< Sent_when > m_sent_when
Time stamps, order numbers, and other info at the times when the different attempts (including origin...
const size_t m_size
Number of bytes in the Data_packet::m_data field of the sent packet.
const boost::shared_ptr< Data_packet > m_packet
If retransmission is on, this is the DATA packet itself that was sent; otherwise null.
uint16_t ack_count_t
Type used for m_acks_after_me.
ack_count_t m_acks_after_me
The number of times any packet with m_sent_when.back().m_order_num > this->m_sent_when....
A data store that keeps stats about the a Peer_socket connection.
Definition: info.hpp:456
Peer_socket_send_stats m_snd
Stats for outgoing direction of traffic. As opposed to the other m_snd_* members, this typically accu...
Definition: info.hpp:511
Node_options m_node_opts
Per-node options currently set on the socket's Node.
Definition: info.hpp:651
size_t m_low_lvl_max_buf_size
The UDP receive buffer maximum size, as reported by an appropriate call to the appropriate getsockopt...
Definition: info.hpp:526
size_t m_rcv_buf_size
The number of bytes in the internal Receive buffer.
Definition: info.hpp:549
size_t m_rcv_wnd_last_advertised
The last rcv_wnd (receive window) size sent to sender (not necessarily received; packets can be lost)...
Definition: info.hpp:555
Fine_duration m_snd_pacing_slice_period
In pacing, the duration of the current pacing time slice.
Definition: info.hpp:629
size_t m_rcv_reassembly_q_data_size
If rexmit_on is false then 0; otherwise the total DATA payload in the reassembly queue of the socket.
Definition: info.hpp:558
size_t m_snd_pacing_bytes_allowed_this_slice
This many bytes worth of DATA packets may still be sent, at this time, within the time slice defined ...
Definition: info.hpp:635
Peer_socket_options m_sock_opts
Per-socket options currently set on the socket.
Definition: info.hpp:644
size_t m_snd_buf_size
The number of bytes in the internal Send buffer.
Definition: info.hpp:590
size_t m_rcv_syn_rcvd_data_cumulative_size
Total size of DATA payload queued while waiting for SYN_ACK_ACK in SYN_RCVD state.
Definition: info.hpp:573
size_t m_rcv_syn_rcvd_data_q_size
Number of DATA packets queued while waiting for SYN_ACK_ACK in SYN_RCVD state.
Definition: info.hpp:576
std::string m_int_state_str
The internal state of the socket, rendered into string (e.g., "SYN_RECEIVED" or "ESTABLISHED").
Definition: info.hpp:533
Fine_time_pt m_snd_pacing_slice_start
In pacing, the time point marking the beginning of the current pacing time slice.
Definition: info.hpp:626
size_t m_snd_cong_ctl_in_flight_count
In congestion control, the current sent data packets that have been neither acknowledged nor consider...
Definition: info.hpp:611
size_t m_snd_cong_ctl_in_flight_bytes
In congestion control, the current sent data bytes that have been neither acknowledged nor considered...
Definition: info.hpp:608
double m_snd_est_bandwidth_mbit_per_sec
Estimate of the currently available (to this connection) outgoing bandwidth, in megabits per second.
Definition: info.hpp:641
size_t m_rcv_wnd
Receive window size = max Receive buffer space minus space taken. Infinity if flow control disabled.
Definition: info.hpp:552
size_t m_rcv_packets_with_gaps
Number of DATA packets tracked in structure tracking all valid received packets such at least one pac...
Definition: info.hpp:570
size_t m_snd_cong_ctl_wnd_bytes
In congestion control, the current congestion window (number of outgoing data bytes allowed In-flight...
Definition: info.hpp:599
Fine_duration m_snd_smoothed_round_trip_time
Estimated current round trip time of packets, computed as a smooth value over the past individual RTT...
Definition: info.hpp:614
Error_code m_disconnect_cause
If the socket is closing or closed, this is the reason for the closure; otherwise the default-constru...
Definition: info.hpp:539
size_t m_snd_cong_ctl_wnd_count_approx
In congestion control, the approximate equivalent of m_snd_cong_ctl_in_flight_bytes as a full packet ...
Definition: info.hpp:602
size_t m_snd_rcv_wnd
The receive window (rcv_wnd a/k/a free Receive buffer space) value of the peer socket on the other si...
Definition: info.hpp:596
bool m_is_active_connect
true if this is the "client" socket (connect()ed); false otherwise (accept()ed).
Definition: info.hpp:536
size_t m_snd_pacing_packet_q_size
In pacing, number of packets currently queued to be sent out by the pacing module.
Definition: info.hpp:623
Fine_duration m_snd_round_trip_time_variance
RTTVAR used for m_snd_smoothed_round_trip_time calculation; it is the current RTT variance.
Definition: info.hpp:617
Peer_socket_receive_stats m_rcv
Stats for incoming direction of traffic. As opposed to the other m_rcv_* members, this typically accu...
Definition: info.hpp:508
Fine_duration m_snd_drop_timeout
Drop Timeout: how long a given packet must remain unacknowledged to be considered dropped due to Drop...
Definition: info.hpp:620
A set of low-level options affecting a single Peer_socket.
Definition: options.hpp:36
size_t m_st_rcv_buf_max_size
Maximum number of bytes that the Receive buffer can hold.
Definition: options.hpp:141
bool m_st_rexmit_on
Whether to enable reliability via retransmission.
Definition: options.hpp:221
size_t m_st_snd_buf_max_size
Maximum number of bytes that the Send buffer can hold.
Definition: options.hpp:134
Fine_duration m_st_connect_retransmit_period
How often to resend SYN or SYN_ACK while SYN_ACK or SYN_ACK_ACK, respectively, has not been received.
Definition: options.hpp:121
Fine_duration m_st_connect_retransmit_timeout
How long from the first SYN or SYN_ACK to allow for connection handshake before aborting connection.
Definition: options.hpp:124
Fine_duration m_st_delayed_ack_timer_period
The maximum amount of time to delay sending ACK with individual packet's acknowledgment since receivi...
Definition: options.hpp:198
size_t m_st_max_block_size
The size of block that we will strive to (and will, assuming at least that many bytes are available i...
Definition: options.hpp:114
unsigned int m_st_rcv_buf_max_size_to_advertise_percent
% of rcv-buf-max-size that has to be freed, since the last receive window advertisement,...
Definition: options.hpp:178
unsigned int m_st_rcv_max_packets_after_unrecvd_packet_ratio_percent
The limit on the size of Peer_socket::m_rcv_packets_with_gaps, expressed as what percentage the maxim...
Definition: options.hpp:190
Represents the remote endpoint of a Flow-protocol connection; identifies the UDP endpoint of the remo...
Definition: endpoint.hpp:93
util::Udp_endpoint m_udp_endpoint
UDP address (IP address/UDP port) where the Node identified by this endpoint bound its low-level UDP ...
Definition: endpoint.hpp:97
#define FLOW_UTIL_WHERE_AM_I_STR()
Same as FLOW_UTIL_WHERE_AM_I() but evaluates to an std::string.
Definition: util_fwd.hpp:1263