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