Flow 2.0.0
Flow project: Full implementation reference.
options.hpp
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
19#pragma once
20
22#include <boost/program_options.hpp>
23
24namespace flow::net_flow
25{
26// Types.
27
28/**
29 * A set of low-level options affecting a single Peer_socket. All comments for `struct`
30 * Node_options apply equally to Peer_socket_options (except, of course, those pertaining to the
31 * nested Peer_socket_options instance).
32 *
33 * @see `struct` Node_options.
34 */
36{
37 // Types.
38
39 /// Short-hand for boost.program_options config options description. See setup_config_parsing().
40 using Options_description = boost::program_options::options_description;
41
42 /**
43 * A time duration, with fine precision, that can be positive, zero, or negative (unless a specific option
44 * specifies an additional constraint to that effect). This is used for most (all?) time-based options in
45 * Node_options and Peer_socket_options.
46 *
47 * @internal
48 *
49 * This used to be an alias from `duration<uint16_t, Fine_duration::period>` to `Fine_duration_non_neg`,
50 * meaning it was specifcally intended to be non-negative. That constraint made sense for all time-based
51 * options at the time and probably still does... but there was a problem: In Boost 1.50, the non-negative
52 * implementation integer type caused a comparison to -1 somewhere inside their headers, which triggered a
53 * (reasonable) gcc-4.2 warning. Ironically, in Boost 1.48, using a negative-allowing type caused some
54 * other comparison triggering a similar warning. Anyway, as of Boost 1.50, I've decided to bite the bullet
55 * and use a negative-allowing type after all (which causes no warning in that version) and enforce any
56 * required non-negativeness (or even positiveness, where applicable) explicitly when calling
57 * `VALIDATE_CHECK()` at option parse time. (That was way longer an explanation than this deserved.)
58 */
60
61 /// The possible choices for congestion control strategy for the socket.
63 {
64 /// Classic (Reno-style) AIMD congestion control.
65 S_CLASSIC = 0,
66 /// Classic congestion control but with loss resulting in a window chosen from an outgoing bandwidth estimate.
67 S_CLASSIC_BANDWIDTH_ESTIMATED
68 };
69
70 // Constructors/destructor.
71
72 /**
73 * Constructs a Peer_socket_options with values equal to those used by Node when the Node
74 * creator chooses not to supply custom options.
75 */
76 explicit Peer_socket_options();
77
78 // Methods.
79
80 /**
81 * Analogous to Node_options::setup_config_parsing(). See that method.
82 *
83 * @param opts_desc
84 * The #Options_description object into which to load the help information, defaults, and
85 * mapping to members of `*this`.
86 */
88
89 // Data.
90
91 /**
92 * The size of block that we will strive to (and will, assuming at least that many bytes are
93 * available in Send buffer) pack into each outgoing DATA packet. It is assumed the other side is
94 * following the same policiy (any packets that do not -- i.e., exceed this size -- are dropped).
95 * This is an important control; the higher it is the better for performance AS LONG AS it's not so
96 * high it undergoes IP fragmentation (or does that even happen in UDP? if not, even worse --
97 * it'll just be dropped and not sent!). The performance impact is major; e.g., assuming no
98 * fragmentation/dropping, we've seen a ~1500 byte MBS result in 20-30% higher throughput than
99 * 1024 byte MBS.
100 *
101 * Additionally, if using `net_flow` module with no reliability feature -- i.e., if you want to perform FEC
102 * or something else outside the Flow protocol -- then it is absolutely essential that EVERY `send*()` call
103 * provides a buffer whose size is a multiple of max-block-size. Otherwise packet boundaries
104 * will not be what you expect, and you will get what seems to be corrupted data at the
105 * application layer (since our stream-based API has no way of knowing where your message begins
106 * or ends). Alternatively you can encode message terminators or packet sizes, but since in
107 * unreliable mode integrity of a given block is guaranteed only if all blocks align with
108 * max-block-size boundaries, you'll still probably be screwed.
109 *
110 * @todo max-block-size should be dynamically determined (but overridable by this option). That
111 * is a complex topic, however, with considerations such as MTU discovery, ICMP errors, and who knows
112 * what else.
113 */
115
116 /**
117 * How often to resend SYN or SYN_ACK while SYN_ACK or SYN_ACK_ACK, respectively, has not been received.
118 * In other words, this controls the pause between retries during the connection opening phase, by either side,
119 * if the other side is not responding with the appropriate response.
120 */
122
123 /// How long from the first SYN or SYN_ACK to allow for connection handshake before aborting connection.
125
126 /**
127 * Maximum number of bytes that the Send buffer can hold. This determines how many bytes user can
128 * `send()` while peer cannot send over network until send() refuses to take any more bytes. Notew
129 * that any value given will be increased, if necessary, to the nearest multiple of
130 * max-block-size. This is important to preserve message boundaries when operating in
131 * unreliable mode (guaranteed max-block-size-sized chunks of data will be sent out in their
132 * entirety instead of being fragmented).
133 */
135
136 /**
137 * Maximum number of bytes that the Receive buffer can hold. This determines how many bytes
138 * can be received in the background by the Node without user doing any receive()s. It is also
139 * rounded up to to the nearest multiple of max-block-size.
140 */
142
143 /**
144 * Due to loss or reordering we may receive DATA packets before receiving the handshake-finishing SYN_ACK_ACK;
145 * any such SYN_RCVD-state DATA packets beyond this cumulative payload size shall be silently dropped.
146 * The value 0 will drop all such DATA packets.
147 */
149
150 /**
151 * Whether flow control (a/k/a receive window a/k/a rcv_wnd management) is enabled. If this is
152 * disabled, an infinite rcv_wnd will always be advertised to the sender; so if the Receive buffer
153 * is exceeded packets are dropped as normal, but the sender will not know it should stop sending
154 * until Receive buffer space is freed. If this is enabled, we keep the sender informed of how
155 * much Receive buffer space is available, so it can suspend the flow as necessary.
156 */
158
159 /**
160 * % of rcv-buf-max-size such that if Receive buffer stores up to (100 + this many) % of
161 * rcv-buf-max-size bytes, the bytes will still be accepted. In other words, this allows the max
162 * Receive buffer to hold slightly more than rcv-buf-max-size bytes. However, the current Receive
163 * buffer capacity advertised to the other side of the connection will be based on the
164 * non-inflated rcv-buf-max-size. This option provides some protection against the fact that the
165 * receive window value sent to the other side will lag behind reality somewhat.
166 */
168
169 /**
170 * % of rcv-buf-max-size that has to be freed, since the last receive window advertisement, via
171 * user popping data from Receive buffer, before we must send a receive window advertisement.
172 * Normally we send rcv_wnd to the other side opportunistically in every ACK; but there can be
173 * situations when there is no packet to acknowledge, and hence we must specifically make a
174 * packet just to send over rcv_wnd. Typically we should only need to do this if the buffer was
175 * exceeded and is now significantly freed. This value must be in [1, 100], but anything over 50
176 * is probably pushing it.
177 */
179
180 /**
181 * The limit on the size of Peer_socket::m_rcv_packets_with_gaps, expressed as what percentage the
182 * maximal size of that structure times max-block-size is of the maximal receive buffer size. For
183 * example, if this is 200, then Peer_socket::m_rcv_packets_with_gaps can represent up to roughly
184 * 2x as many full-sized blocks as the Receive buffer can. This should also by far exceed any
185 * sender congestion window max size to avoid packet loss.
186 *
187 * @see Peer_socket::m_rcv_packets_with_gaps for details.
188 * @see Peer_socket::m_rcv_syn_rcvd_data_q.
189 */
191
192 /**
193 * The maximum amount of time to delay sending ACK with individual packet's acknowledgment since
194 * receiving that individual packet. If set to zero duration, any given individual acknowledgment
195 * is sent within a non-blocking amount of time of its DATA packet being read. Inspired by RFC
196 * 1122-4.2.3.2.
197 */
199
200 /**
201 * If there are at least this many TIMES max-block-size bytes' worth of individual acknowledgments
202 * to be sent, then the delayed ACK timer is to be short-circuited, and the accumulated
203 * acknowledgments are to be sent as soon as possible. Inspired by RFC 5681.
204 */
206
207 /**
208 * Whether to enable reliability via retransmission. If `false`, a detected lost packet may have
209 * implications on congestion control (speed at which further data are sent) but will not cause
210 * that packet to be resent; receiver application code either has to be OK with missing packets or
211 * must implement its own reliability (e.g., FEC). Packets may also be delivered in an order
212 * different from the way they were sent. If `true`, the receiver need not worry about it, as any
213 * lost packets will be retransmitted with no participation from the application code on either
214 * side, as in TCP. Also as in TCP, this adds order preservation, so that the stream of bytes sent
215 * will be exactly equal to the stream of bytes received. Retransmission removes the requirement
216 * for the very exacting block-based way in which `send()` and friends must be called.
217 *
218 * This option must have the same value on both sides of the connection, or the server will refuse
219 * the connection.
220 */
222
223 /**
224 * If retransmission is enabled and a given packet is retransmitted this many times and has to be
225 * retransmitted yet again, the connection is reset. Should be positive.
226 */
228
229 /**
230 * Once socket enters ESTABLISHED state, this is the value for Peer_socket::m_snd_drop_timeout until the first RTT
231 * measurement is made (the first valid acknowledgment arrives).
232 */
234
235 /**
236 * If `true`, when scheduling Drop Timer, schedule it for Drop Timeout relative to the send time of
237 * the earliest In-flight packet at the time. If `false`, also schedule DTO relative to the time of
238 * scheduling. The latter is less aggressive and is recommended by RFC 6298.
239 */
241
242 /**
243 * If `true`, when the Drop Timer fires, all In-flight packets are to be considered Dropped (and thus the
244 * timer is to be disabled). If `false`, only the earliest In-flight packet is to be considered Dropped (and
245 * thus the timer is to restart). RFC 6298 recommends `false`. `true` is more aggressive.
246 */
248
249 /**
250 * If an In-flight packet is acknowledged, but it is not the earliest In-flight packet (i.e., it's
251 * an out-of-order acknowledgment), and this is `true`, the timer is restarted. Otherwise the timer
252 * continues to run. The former is less aggressive. RFC 6298 wording is ambiguous on what it
253 * recommends (not clear if cumulative ACK only, or if SACK also qualifies).
254 */
256
257 /**
258 * Enables or disables packet pacing, which attempts to spread out, without sacrificing overall
259 * send throughput, outgoing low-level packets to prevent loss. If disabled, any packet that is
260 * allowed by congestion/flow control to be sent over the wire is immediately sent to the UDP
261 * net-stack; so for example if 200 packets are ready to send and are allowed to be sent, they're sent
262 * at the same time. If enabled, they will be spread out over a reasonable time period instead.
263 * Excessive burstiness can lead to major packet drops, so this can really help.
264 */
266
267 /**
268 * When estimating the available send bandwidth, each sample must be compiled over at least this
269 * long of a time period, even if the SRTT is lower. Normally a sample is collected over at
270 * least an SRTT, but computing a bandwidth sample over a quite short time period can produce
271 * funky results, hence this floor. Send bandwidth estimation is used at least for some forms of
272 * congestion control.
273 */
275
276 /// The congestion control algorithm to use for the connection or connections.
278
279 /**
280 * The initial size of the congestion window, given in units of max-block-size-sized blocks.
281 * The special value 0 means RFC 5681's automatic max-block-size-based computation should
282 * be used instead.
283 */
285
286 /**
287 * The constant that determines the CWND limit in Congestion_control_classic_data::congestion_window_at_limit() and
288 * Congestion_control_classic_data::clamp_congestion_window() (in multiples of max-block-size). When choosing this
289 * value, use these constraints:
290 *
291 * - This limits total outgoing throughput. The throughput B will be <= CWND/RTT, where RTT is
292 * roughly the RTT of the connection, and CWND == max-cong-wnd-blocks * max-block-size.
293 * Therefore, choose B and RTT values and set max-cong-wnd-blocks = B * RTT / max-block-size
294 * (B in bytes/second, RTT in seconds).
295 * - Until we implement Receive window, this value should be much (say, 4x) less than the size
296 * of the Receive buffer, to avoid situations where even a well-behaving user (i.e., user that
297 * `receive()`s all data ASAP) cannot keep up with reading data off Receive buffer, forcing
298 * `net_flow` to drop large swaths of incoming traffic. If CWND is much smaller than Receive
299 * buffer size, then this avoids that problem.
300 *
301 * @todo Reconsider this value after Receive window feature is implemented.
302 */
304
305 /// On Drop Timeout, set congestion window to this value times max-block-size.
307
308 /**
309 * The multiple of max-block-size by which to increment CWND in congestion avoidance mode after receiving
310 * at least a full CWND's worth of clean acknowledgments. RFC 5681 (classic Reno) mandates this is set to 1,
311 * but we allow it to be overridden. The special value 0 causes the RFC value to be used.
312 */
314
315 /**
316 * In classic congestion control, RFC 5681 specifies the window should be halved on loss; this
317 * option allows one to use a custom percentage instead. This should be a value in [1, 100] to
318 * have the window decay to that percentage of its previous value, or 0 to use the RFC
319 * 5681-recommended constant (50).
320 */
322
323 /// Ceiling to impose on the Drop Timeout.
325
326 /**
327 * Whenever the Drop Timer fires, upon the requisite Dropping of packet(s), the DTO (Drop Timeout)
328 * is set to its current value times this factor, and then the timer is rescheduled accordingly.
329 * RFC 6298 recommends 2. Another value might be 1 (disable feature). The lower the more
330 * aggressive.
331 */
333
334 /**
335 * When the mode triggered by rcv-buf-max-size-to-advertise-percent being exceeded is in effect,
336 * to counteract the possibility of ACK loss the receive window is periodically advertised
337 * subsequently -- with the period given by this option -- until either some new data arrive or
338 * rcv-wnd-recovery-max-period is exceeded.
339 */
341
342 /**
343 * Approximate amount of time since the beginning of rcv_wnd recovery due to
344 * rcv-buf-max-size-to-advertise-percent until we give up and end that phase.
345 */
347
348private:
349 // Friends.
350
351 /**
352 * The two `struct`s work together (since Node_options contains an instance of us).
353 * @see Node_options.
354 */
355 friend struct Node_options;
356 // Friend of Peer_socket_options: For access to our internals.
357 friend std::ostream& operator<<(std::ostream& os, const Peer_socket_options& opts);
358
359 // Methods.
360
361 /**
362 * Analogous to Node_options::setup_config_parsing_helper(). See that method.
363 *
364 * @param opts_desc
365 * See Node_options::setup_config_parsing_helper().
366 * @param target
367 * See Node_options::setup_config_parsing_helper().
368 * @param defaults_source
369 * See Node_options::setup_config_parsing_helper().
370 * @param printout_only
371 * See Node_options::setup_config_parsing_helper().
372 */
374 Peer_socket_options* target,
375 const Peer_socket_options& defaults_source,
376 bool printout_only);
377}; // struct Peer_socket_options
378
379/**
380 * A set of low-level options affecting a single Flow Node, including Peer_socket objects and other
381 * objects subsequently generated by that Flow Node. Typically these values can be left at
382 * defaults, and thus the `net_flow` user normally need not deal with these objects. However, if
383 * low-level tuning of Flow internals is needed, you can use this `struct`.
384 *
385 * This is just a simple `struct` with a couple of utilities to serialize (to `ostream`) and deserialize
386 * (from a config file or command line) the values in the `struct`; it is not connected to any other
387 * object (for example, it can exist with no Node).
388 *
389 * You may read from and write to members of this `struct` at will, and no checking will be performed;
390 * moreover, doing so will have no effect other than the field being read or written.
391 *
392 * Fields are validated and potentially take effect, however, when you pass a Node_options object
393 * to Node. Node always copies the given Node_options and never saves a reference to it. All of
394 * that applies equally to Peer_socket_options.
395 *
396 * Node_options contains a Peer_socket_options #m_dyn_sock_opts (a very similar, in spirit, `struct`)
397 * within it. Options stored within that are per-::flow::net_flow::Peer_socket
398 * options. For example, #m_st_low_lvl_max_buf_size
399 * applies to the entire Node at all times and is stored directly in `*this` (as there is only one
400 * UDP buffer for the entire Node); whereas max-block-size can be different for each
401 * individual Peer_socket and is thus stored in `this->m_dyn_sock_opts`. See Node::listen() and
402 * Node::connect() for how per-socket options are distributed to subsequent sockets as connections
403 * are made.
404 *
405 * The `struct`, when default-constructed, contains sane default values as used by Node when not
406 * provided with a Node_options object. Alternatively, you can get a Node's existing set of
407 * options via Node::options().
408 *
409 * After obtaining a Node_options either way, you may assign values to it manually.
410 *
411 * Alternatively, you can fill it parsing a config file or command line using
412 * boost.program_options (which makes it a snap) with the help of
413 * Node_options::setup_config_parsing(), which will provide a program_options-suitable
414 * #Options_description object to enable this parsing. You may conversely print #Options_description
415 * to an `ostream` (e.g., `cout`) for full help text on the meaning of each option and the defaults.
416 *
417 * You may also print a filled Node_options itself to an `ostream` for the current settings stored in
418 * that object.
419 *
420 * ### Thready safety ###
421 * Same as any `struct` with no locking done therein.
422 *
423 * @see Node constructor, Node::set_options(), Node::listen(), Node::connect().
424 * @see Peer_socket::set_options(), Peer_socket::options().
425 * @see Peer_socket_options.
426 *
427 * @internal
428 *
429 * If you want to add an option, follow these steps:
430 *
431 * 1. Decide whether it's a per-socket or per-Node option (usually the former) and add it to
432 * Peer_socket_options or Node_options, respectively, using the below steps.
433 * 2. Decide whether the option's value may be changed after the enclosing Node_options or
434 * Peer_socket_options has been accepted by a running Node or Peer_socket, respectively. Most
435 * options tend to be static (cannot be changed once a Node or Peer_socket has accepted them).
436 * Based on this, the data member must start with `m_st_` (static) or `m_dyn_` (dynamic). Choose
437 * the rest of the member name as well.
438 * 3. Add the data member into the chosen `struct`. Fully comment the meaning, as for other
439 * present options.
440 * 4. Add an ADD_CONFIG_OPTION() line into the proper `struct` setup_config_parsing_helper() by analogy
441 * with other present options. The description string should usually be a copy of the comment
442 * from step 3.
443 * 5. Add the default value (very important) into the proper `struct` constructor.
444 * Explain the choice of default with a comment.
445 * 6. Is this a static option (step 2)? If so, add static validation (by analogy with present
446 * options) to Node::validate_options() or Node::sock_validate_options().
447 * 7. Add semantic validation (such as for illegal values, including illegal values due to
448 * inconsistency with other option values) to Node::validate_options() or
449 * Node::sock_validate_options(). For some options all values are legal, in which case you
450 * needn't add anything.
451 * 8. Use the option. EVERY read of the option should be performed through Node::opt() or
452 * Peer_socket::opt(), for thread safety. The only exception is if you manually lock
453 * Node::m_opt_mutex or Peer_socket::m_opt_mutex, respectively.
454 */
456{
457 // Types.
458
459 /// Short-hand for boost.program_options config options description. See setup_config_parsing().
461
462 /// A time duration, with fine precision.
464
465 // Constructors/destructor.
466
467 /**
468 * Constructs a Node_options with values equal to those used by Node when the Node
469 * creator chooses not to supply custom options.
470 */
471 explicit Node_options();
472
473 // Methods.
474
475 /**
476 * Modifies a boost.program_options options description object to enable subsequent parsing of a
477 * command line or config file into the data members of this object, as well printing a help
478 * message about these options to an `ostream`. In particular, after executing this method:
479 *
480 * - If you output `*opts_desc` to an `ostream`, the `ostream` will get a detailed description of each
481 * option, its symbolic name (for config file or command line), and its value if the config
482 * file or command line omits this option (the default). This default will equal to the value
483 * of that particular option in a default-constructed Node_options.
484 *
485 * - If you perform something like the following code snippet,
486 * then `*this` will now be filled with values parsed from `ifstream F` (and default values
487 * for any omitted values):
488 *
489 * ~~~
490 * namespace opts = boost::program_options;
491 * Node_options flow_opts;
492 * opts::options_description opts_desc;
493 * std::ifstream F(...);
494 * flow_opts.setup_config_parsing(&opts_desc);
495 * opts::variables_map cfg_vars;
496 * opts::store(opts::parse_config_file(F, *opts_desc), cfg_vars);
497 * opts::notify(cfg_vars);
498 * ~~~
499 *
500 * Note the default values will come from the current values inside `*this`.
501 *
502 * @param opts_desc
503 * The #Options_description object into which to load the help information, defaults, and
504 * mapping to members of `*this`.
505 */
507
508 // Data.
509
510 /**
511 * If and only if this is `true`, the Node will detect SIGINT and SIGTERM (or your OS's version thereof);
512 * upon seeing such a signal, it will fire Node::interrupt_all_waits(), which will interrupt all
513 * blocking operations, conceptually similarly to POSIX's `EINTR`. If this is `true`, the user may register
514 * their own signal handler(s) (for any purpose whatsoever) using `boost::asio::signal_set`. However, behavior
515 * is undefined if the program registers signal handlers via any other API, such as `sigaction()` or `signal()`.
516 * If you need to set up such a non-`signal_set` signal handler, AND you require `EINTR`-like behavior,
517 * then (1) set this option to `false`; (2) trap SIGINT and SIGTERM yourself; (3) in your handlers for the
518 * latter, simply call Node::interrupt_all_waits(). Similarly, if you want custom behavior regarding
519 * Node::interrupt_all_waits(), feel free to call it whenever you want (not necessarily even from a signal
520 * handler), and set this to `false`. However, if a typical, common-sense behavior is what you're after -- and either
521 * don't need additional signal handling or are OK with using `signal_set` for it -- then setting this to `true` is a
522 * good option.
523 */
525
526 /**
527 * The max size to ask the OS to set our UDP socket's receive buffer to in order to minimize loss
528 * if we can't process datagrams fast enough. This should be as high as possible while being
529 * "acceptable" in terms of memory. However, the OS will probably have its own limit
530 * and may well pick a limit that is the minimum of that limit and this value.
531 */
533
534 /**
535 * A time period such that the boost.asio timer implementation for this platform is able to
536 * accurately able to schedule events within this time period or greater. If you select 0, the
537 * code will decide what this value is based on the platform, but its logic for this may or may not
538 * be correct (actually it will probably be correct but possibly too conservative [large], causing
539 * timer coarseness in mechanisms like the rather critical packet pacing).
540 */
542
543 /**
544 * The UDP net-stack may deliver 2 or more datagrams to the Flow Node at the same time. To lower
545 * overhead and increase efficiency, Flow will process all such datagrams -- and any more that
546 * may arrive during this processing -- before preparing any resulting outgoing messages, such as
547 * acknowledgments or more data packets. In high-speed conditions this may result in excessive
548 * burstiness of outgoing traffic. This option's value places a limit on the number of datagrams
549 * to process before constructing and sending any resulting outgoing messages to prevent this
550 * burstiness. If 0, there is no limit.
551 */
553
554 /**
555 * Any incoming low-level (UDP) packet will be truncated to this size. This should be well above
556 * per-socket max-block-size (# of bytes of user payload per DATA packet). There will only be one buffer
557 * of this size in memory at a time, so no need to be too stingy, but on the other hand certain
558 * allocation/deallocation behavior may cause performance drops if this unnecessarily large.
559 */
561
562 /**
563 * This very inside-baseball setting controls the allocation/copy behavior of the UDP receive-deserialize
564 * operation sequence. When enabled, there is exactly one input buffer large enough to
565 * hold any one serialized incoming packet; any deserialized data (including DATA and ACK payloads) are
566 * stored in separately allocated per-packet buffers; and and the input buffer is repeatedly reused
567 * without reallocation. When disabled, however, at least some packet types (most notably DATA) will
568 * use the zero-copy principle, having the deserializer take ownership of the input buffer and access pieces
569 * inside it as post-deserialization values (most notably the DATA payload); in this case the input buffer
570 * has to be reallocated between UDP reads. As of this writing the former behavior seems to be somewhat
571 * faster, especially if low-lvl-max-packet-size is unnecessarily large; but arguably the zero-copy behavior
572 * may become faster if some implementation details related to this change. So this switch seemed worth
573 * keeping.
574 */
576
577 /**
578 * Maximum backlog size for each `Server_socket` subsequently created via `Node::listen()`. The backlog
579 * (for a given `Server_socket`) is defined as the total number of connections either in SYN_RCVD state
580 * (SYN_ACK sent, awaiting SYN_ACK_ACK) or in ESTABLISHED state but not yet user-accepted via
581 * `Server_socket::*accept()`. When a SYN arrives while the backlog is full, it is rejected with an RST response.
582 *
583 * This value is captured at `Node::listen()` time and fixed for the resulting `Server_socket`'s
584 * lifetime; subsequent changes affect only `Server_socket`s created by later `listen()` calls.
585 *
586 * It *is* dynamic at the `Node` level, but does *not* dynamically affect existing listening `Server_socket`s.
587 */
589
590 /**
591 * The set of per-Peer_socket options in this per-Node set of options. This represents the
592 * per-socket options each subsequent socket generated in the corresponding Node will get, unless
593 * user specifies a custom set of such options.
594 *
595 * Note that, in the context of the per-Node options set, this is dynamic; any per-socket option
596 * (even a static one) must be changeable in the global per-Node options set, as otherwise it would just
597 * be a regular per-Node data member in Node_options, as it could never change anyway, so no point in having
598 * it be per-Peer_socket.
599 */
601
602private:
603 // Friends.
604
605 /**
606 * The two `struct`s work together (since Peer_socket_options needs add_config_option()).
607 * @see Peer_socket_options.
608 */
609 friend struct Peer_socket_options;
610 /**
611 * Node needs opt_id_to_str().
612 * @see Node.
613 */
614 friend class Node;
615 // Friend of Node_options: For access to our internals.
616 friend std::ostream& operator<<(std::ostream& os, const Node_options& opts);
617
618 // Methods.
619
620 /**
621 * Helper that, for a given option `m_blah`, takes something like `"m_blah_blah"` and returns the
622 * similar more suitable option name the user can use in a config file or command line. For
623 * example, `"m_st_max_block_size"` yields `"max-block-size"`.
624 *
625 * @param opt_id
626 * A string whose content equals the name (in C++ code) of a data member of Node_options or
627 * Peer_socket_options (presumably generated using the macro hash technique).
628 * @return See above.
629 */
630 static std::string opt_id_to_str(const std::string& opt_id);
631
632 /**
633 * A helper that adds a single option to a given #Options_description, for use either in printing
634 * out the current state of a Node_options or Peer_socket_options; or for parsing of config into such
635 * an object.
636 *
637 * @tparam Opt_type
638 * The type of a data member of of Node_options or Peer_socket_options.
639 * @param opts_desc
640 * The #Options_description object into which to load a single `option_description`.
641 * @param opt_id
642 * A string whose content equals the name (in C++ code) of a data member of Node_options or
643 * Peer_socket_options (presumably generated using the macro hash technique).
644 * @param target_val
645 * If `!printout_only`, and the user parses via `*opts_desc`, the parsed option value will be
646 * loaded into `*target_val`. Otherwise ignored.
647 * @param default_val
648 * The default value to this option in `*opts_desc` will be this.
649 * @param description
650 * If `!printout_only`, the detailed description of the option will be this. Otherwise ignored.
651 * @param printout_only
652 * See above.
653 */
654 template<typename Opt_type>
655 static void add_config_option(Options_description* opts_desc,
656 const std::string& opt_id,
657 Opt_type* target_val, const Opt_type& default_val,
658 const char* description, bool printout_only);
659
660 /**
661 * Loads the full set of boost.program_options config options into the given #Options_description,
662 * either for the full help message and parsing config into a Node_options, or for purposes
663 * of printout of the state of an existing Node_options object (e.g., when printing
664 * Node_options to an `ostream`).
665 *
666 * ### `printout_only` case ###
667 * Will generate an #Options_description with "defaults" set to values from a
668 * `defaults_source` object (which should be the object you're trying to output to a stream), the
669 * names of each option, no detailed option description, and no target for each value should the
670 * caller try to actually parse using `*opts_desc` (which he should not). This is great when
671 * printing the current contents of a given Node_options (it just reuses the
672 * boost.program_options machinery for this purpose).
673 *
674 * ### `!printout_only` case ###
675 * Will generate an #Options_description with "defaults" set to values from a
676 * `defaults_source` object (which should be a default-constructed Node_options), the
677 * names of each option, detailed option descriptions, and each option's target inside `*target`,
678 * should the caller actually parse using `*opts_desc`. This is useful for parsing the options set
679 * from a config file or command line.
680 *
681 * This is the main specification for the different possible options.
682 *
683 * @param opts_desc
684 * The #Options_description object to load.
685 * @param target
686 * If `!printout_only`, and the user parses using `*opts_desc`, the parsed values will end up
687 * in members of `*target`.
688 * @param defaults_source
689 * The defaults listed (and used, if `!printout_only`, and you choose to parse using
690 * `*opts_desc`) for each option.
691 * @param printout_only
692 * See above.
693 */
695 Node_options* target,
696 const Node_options& defaults_source,
697 bool printout_only);
698
699}; // struct Node_options
700
701// Free functions: in *_fwd.hpp.
702
703// However the following refer to inner type(s) and hence must be declared here and not _fwd.hpp.
704
705/**
706 * Deserializes a Peer_socket_options::Congestion_control_strategy_choice `enum` from
707 * a standard input stream. Reads a single space-delimited token
708 * from the given stream. Maps that token to an aforementioned enumeration value.
709 * If the token is not recognized or cannot be read, some reasonable
710 * default strategy is chosen. This enables a few key things to
711 * work, including parsing from config file/command line via and conversion from `string` via `boost::lexical_cast`.
712 *
713 * @relatesalso Peer_socket_options
714 *
715 * @param is
716 * Stream from which to deserialize.
717 * @param strategy_choice
718 * Reference to `enum` value which to set to the mapped strategy.
719 * @return `is`.
720 *
721 * @internal
722 * @todo Peer_socket_options::Congestion_control_strategy_choice stream inserter `<<` and `>>` operators should use
723 * the flow::util::istream_to_enum() pattern which is much easier than the overwrought old thing in there now involving
724 * two `map`s. Perhaps add a generic `enum_to_strings()` to provide the body for
725 * net_flow::Congestion_control_selector::get_ids().
726 */
727std::istream& operator>>(std::istream& is,
729
730/**
731 * Serializes a Peer_socket_options::Congestion_control_strategy_choice `enum` to a standard `ostream` -- the reverse of
732 * operator>>(). Writes a space-less token to the given stream based on the given strategy `enum` value. This
733 * enables a few key things to work, including output of defaults and values in the help via
734 * Peer_socket_options, and conversion to `string` via `boost::lexical_cast`.
735 *
736 * @relatesalso Peer_socket_options
737 *
738 * @param os
739 * Stream to which to serialize.
740 * @param strategy_choice
741 * Value to serialize.
742 * @return `os`.
743 */
744std::ostream& operator<<(std::ostream& os,
746
747
748} // namespace flow::net_flow
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
Flow module containing the API and implementation of the Flow network protocol, a TCP-inspired stream...
Definition: node.cpp:25
std::istream & operator>>(std::istream &is, Congestion_control_selector::Strategy_choice &strategy_choice)
Deserializes a Peer_socket_options::Congestion_control_strategy_choice enum from a standard input str...
Definition: cong_ctl.cpp:124
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
Fine_clock::duration Fine_duration
A high-res time duration as computed from two Fine_time_pts.
Definition: common.hpp:405
A set of low-level options affecting a single Flow Node, including Peer_socket objects and other obje...
Definition: options.hpp:456
unsigned int m_dyn_accept_backlog_limit
Maximum backlog size for each Server_socket subsequently created via Node::listen().
Definition: options.hpp:588
static void setup_config_parsing_helper(Options_description *opts_desc, Node_options *target, const Node_options &defaults_source, bool printout_only)
Loads the full set of boost.program_options config options into the given Options_description,...
Definition: options.cpp:82
void setup_config_parsing(Options_description *opts_desc)
Modifies a boost.program_options options description object to enable subsequent parsing of a command...
Definition: options.cpp:157
Node_options()
Constructs a Node_options with values equal to those used by Node when the Node creator chooses not t...
Definition: options.cpp:41
Peer_socket_options::Fine_duration Fine_duration
A time duration, with fine precision.
Definition: options.hpp:463
friend std::ostream & operator<<(std::ostream &os, const Node_options &opts)
Prints the name of each option in the given Node_options, along with its current value,...
Definition: options.cpp:163
Peer_socket_options m_dyn_sock_opts
The set of per-Peer_socket options in this per-Node set of options.
Definition: options.hpp:600
size_t m_st_low_lvl_max_buf_size
The max size to ask the OS to set our UDP socket's receive buffer to in order to minimize loss if we ...
Definition: options.hpp:532
size_t m_dyn_low_lvl_max_packet_size
Any incoming low-level (UDP) packet will be truncated to this size.
Definition: options.hpp:560
unsigned int m_dyn_max_packets_per_main_loop_iteration
The UDP net-stack may deliver 2 or more datagrams to the Flow Node at the same time.
Definition: options.hpp:552
static void add_config_option(Options_description *opts_desc, const std::string &opt_id, Opt_type *target_val, const Opt_type &default_val, const char *description, bool printout_only)
A helper that adds a single option to a given Options_description, for use either in printing out the...
Definition: options.cpp:63
static std::string opt_id_to_str(const std::string &opt_id)
Helper that, for a given option m_blah, takes something like "m_blah_blah" and returns the similar mo...
Definition: options.cpp:171
Fine_duration m_st_timer_min_period
A time period such that the boost.asio timer implementation for this platform is able to accurately a...
Definition: options.hpp:541
bool m_dyn_guarantee_one_low_lvl_in_buf_per_socket
This very inside-baseball setting controls the allocation/copy behavior of the UDP receive-deserializ...
Definition: options.hpp:575
bool m_st_capture_interrupt_signals_internally
If and only if this is true, the Node will detect SIGINT and SIGTERM (or your OS's version thereof); ...
Definition: options.hpp:524
Peer_socket_options::Options_description Options_description
Short-hand for boost.program_options config options description. See setup_config_parsing().
Definition: options.hpp:460
A set of low-level options affecting a single Peer_socket.
Definition: options.hpp:36
Fine_duration m_st_init_drop_timeout
Once socket enters ESTABLISHED state, this is the value for Peer_socket::m_snd_drop_timeout until the...
Definition: options.hpp:233
unsigned int m_st_max_rexmissions_per_packet
If retransmission is enabled and a given packet is retransmitted this many times and has to be retran...
Definition: options.hpp:227
size_t m_st_rcv_buf_max_size
Maximum number of bytes that the Receive buffer can hold.
Definition: options.hpp:141
size_t m_st_cong_ctl_max_cong_wnd_blocks
The constant that determines the CWND limit in Congestion_control_classic_data::congestion_window_at_...
Definition: options.hpp:303
Fine_duration m_st_snd_bandwidth_est_sample_period_floor
When estimating the available send bandwidth, each sample must be compiled over at least this long of...
Definition: options.hpp:274
unsigned int m_st_cong_ctl_cong_avoidance_increment_blocks
The multiple of max-block-size by which to increment CWND in congestion avoidance mode after receivin...
Definition: options.hpp:313
size_t m_st_cong_ctl_cong_wnd_on_drop_timeout_blocks
On Drop Timeout, set congestion window to this value times max-block-size.
Definition: options.hpp:306
Fine_duration m_dyn_rcv_wnd_recovery_max_period
Approximate amount of time since the beginning of rcv_wnd recovery due to rcv-buf-max-size-to-adverti...
Definition: options.hpp:346
friend std::ostream & operator<<(std::ostream &os, const Peer_socket_options &opts)
Prints the name of each option in the given Peer_socket_options, along with its current value,...
Definition: options.cpp:503
Congestion_control_strategy_choice
The possible choices for congestion control strategy for the socket.
Definition: options.hpp:63
size_t m_st_cong_ctl_init_cong_wnd_blocks
The initial size of the congestion window, given in units of max-block-size-sized blocks.
Definition: options.hpp:284
bool m_st_drop_packet_exactly_after_drop_timeout
If true, when scheduling Drop Timer, schedule it for Drop Timeout relative to the send time of the ea...
Definition: options.hpp:240
bool m_st_rexmit_on
Whether to enable reliability via retransmission.
Definition: options.hpp:221
flow::Fine_duration Fine_duration
A time duration, with fine precision, that can be positive, zero, or negative (unless a specific opti...
Definition: options.hpp:59
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
unsigned int m_st_rcv_buf_max_size_slack_percent
% of rcv-buf-max-size such that if Receive buffer stores up to (100 + this many) % of rcv-buf-max-siz...
Definition: options.hpp:167
void setup_config_parsing(Options_description *opts_desc)
Analogous to Node_options::setup_config_parsing().
Definition: options.cpp:497
size_t m_st_rcv_sync_rcvd_data_q_cumulative_max_size
Due to loss or reordering we may receive DATA packets before receiving the handshake-finishing SYN_AC...
Definition: options.hpp:148
Peer_socket_options()
Constructs a Peer_socket_options with values equal to those used by Node when the Node creator choose...
Definition: options.cpp:195
Fine_duration m_dyn_rcv_wnd_recovery_timer_period
When the mode triggered by rcv-buf-max-size-to-advertise-percent being exceeded is in effect,...
Definition: options.hpp:340
bool m_st_drop_all_on_drop_timeout
If true, when the Drop Timer fires, all In-flight packets are to be considered Dropped (and thus the ...
Definition: options.hpp:247
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
Congestion_control_strategy_choice m_st_cong_ctl_strategy
The congestion control algorithm to use for the connection or connections.
Definition: options.hpp:277
static void setup_config_parsing_helper(Options_description *opts_desc, Peer_socket_options *target, const Peer_socket_options &defaults_source, bool printout_only)
Analogous to Node_options::setup_config_parsing_helper().
Definition: options.cpp:285
boost::program_options::options_description Options_description
Short-hand for boost.program_options config options description. See setup_config_parsing().
Definition: options.hpp:40
size_t m_st_max_full_blocks_before_ack_send
If there are at least this many TIMES max-block-size bytes' worth of individual acknowledgments to be...
Definition: options.hpp:205
bool m_st_snd_pacing_enabled
Enables or disables packet pacing, which attempts to spread out, without sacrificing overall send thr...
Definition: options.hpp:265
bool m_st_rcv_flow_control_on
Whether flow control (a/k/a receive window a/k/a rcv_wnd management) is enabled.
Definition: options.hpp:157
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
unsigned int m_dyn_drop_timeout_backoff_factor
Whenever the Drop Timer fires, upon the requisite Dropping of packet(s), the DTO (Drop Timeout) is se...
Definition: options.hpp:332
bool m_st_out_of_order_ack_restarts_drop_timer
If an In-flight packet is acknowledged, but it is not the earliest In-flight packet (i....
Definition: options.hpp:255
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_cong_ctl_classic_wnd_decay_percent
In classic congestion control, RFC 5681 specifies the window should be halved on loss; this option al...
Definition: options.hpp:321
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
Fine_duration m_dyn_drop_timeout_ceiling
Ceiling to impose on the Drop Timeout.
Definition: options.hpp:324