ProtoCore v1.0.16
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
happy_eyeballs.h
Go to the documentation of this file.
1// ProtoCore v1.0.16 - Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file happy_eyeballs.h
6 * @brief Dual-stack destination selection + Happy Eyeballs fallback (PROTOCORE_ENABLE_HAPPY_EYEBALLS).
7 *
8 * On a dual-stack device (PROTOCORE_ENABLE_IPV6), an outbound connection often has both IPv6 and IPv4
9 * candidate addresses for the same host. RFC 8305 (Happy Eyeballs v2) says: sort them by RFC 6724
10 * preference, interleave the families so you do not try every IPv6 before any IPv4, then start
11 * connection attempts staggered by a short "Connection Attempt Delay" and take whichever connects first.
12 * That gives fast IPv6 when it works and a quick fallback to IPv4 when it does not.
13 *
14 * This is the pure decision layer on top of the shipped `protocore_ip`: a preference score, the ordering +
15 * family interleave over a candidate list, and the attempt-delay gate. The app owns the sockets and the
16 * DNS; this owns *which address to try next, and when*. No heap, no stdlib, host-testable.
17 */
18
19#ifndef PROTOCORE_HAPPY_EYEBALLS_H
20#define PROTOCORE_HAPPY_EYEBALLS_H
21
22#include "protocore_config.h" // the entry point: protocore_types.h for the widths
23
24#if PROTOCORE_ENABLE_HAPPY_EYEBALLS
25
27
28// PROTOCORE_HAPPY_EYEBALLS_BORROW - the bytes this module runs out of - is stated in protocore_config.h, which sums
29// it into its arena. A caller takes them once and passes the pointer to every call. How they
30// are carved is this module's and is never named here.
31
32#ifndef PROTOCORE_HE_MAX
33#define PROTOCORE_HE_MAX 16 ///< candidate list size the interleave step handles (larger lists are only sorted).
34#endif
35
36/** @brief RFC 8305 recommended Connection Attempt Delay (ms); the spec floor is 100, default 250. */
37#define PROTOCORE_HE_ATTEMPT_DELAY_MS 250
38
39#include "shared/ip/ip.h" // protocore_ip: the type a parameter points at
40
41/** @brief What pref takes: ip. */
42typedef struct
43{
44 const protocore_ip *ip;
45} HappyEyeballsPrefArgs;
46
47/** @brief What order takes: list, n. */
48typedef struct
49{
50 protocore_ip *list;
51 size_t n;
52} HappyEyeballsOrderArgs;
53
54/** @brief What attempt_due takes: last_start_ms, now_ms, ... */
55typedef struct
56{
57 uint32_t last_start_ms;
58 uint32_t now_ms;
59 uint32_t attempt_delay_ms;
60} HappyEyeballsAttemptDueArgs;
61
62/**
63 * @brief Dual-stack destination selection + Happy Eyeballs fallback (PROTOCORE_ENABLE_HAPPY_EYEBALLS).
64 *
65 * A caller sets the members a call takes, invokes it through ::HappyEyeballs with the bytes it runs
66 * out of, and reads the outcome off the same handle.
67 *
68 * HappyEyeballs.pref_args.ip = ...;
69 * HappyEyeballs.pref(work);
70 * // HappyEyeballs.n is what the call reports
71 *
72 * @var HappyEyeballsNs::pref_args what pref takes: ip
73 * @var HappyEyeballsNs::order_args what order takes: list, n
74 * @var HappyEyeballsNs::attempt_due_args what attempt_due takes: last_start_ms, now_ms,
75 * @var HappyEyeballsNs::ok true when now_ms - last_start_ms >= attempt_delay_ms (wrap-safe)
76 * @var HappyEyeballsNs::n the count a call reports
77 * @var HappyEyeballsNs::pref RFC 6724-style preference score for a destination address (higher ...
78 * @var HappyEyeballsNs::order order a candidate list for Happy Eyeballs: stable-sort by ...
79 * @var HappyEyeballsNs::attempt_due connection Attempt Delay gate (RFC 8305 sec 5): may the next ...
80 *
81 * @c work is PROTOCORE_HAPPY_EYEBALLS_BORROW bytes the CALLER took, at an address it knows. It is not held past the
82 * call, so nothing here aliases it. How those bytes are carved is this module's and is never named here.
83 */
84typedef struct
85{
86 HappyEyeballsPrefArgs pref_args;
87 HappyEyeballsOrderArgs order_args;
88 HappyEyeballsAttemptDueArgs attempt_due_args;
89 proto_bool ok;
90 int n;
91} HappyEyeballsVars;
92
93/** @brief The operands and the outcome. */
94extern HappyEyeballsVars HappyEyeballsV;
95
96/** @brief The entries. */
97typedef struct
98{
99 void (*const pref)(uint8_t *work);
100 void (*const order)(uint8_t *work);
101 void (*const attempt_due)(uint8_t *work);
102} HappyEyeballsNs;
103
104// What the table binds, defined once in the .c and taking one parameter each: everything
105// else an entry needs is an operand in HappyEyeballsV or a region of the borrow at a fixed offset.
106void protocore_happy_eyeballs_pref(uint8_t *work);
107void protocore_happy_eyeballs_order(uint8_t *work);
108void protocore_happy_eyeballs_attempt_due(uint8_t *work);
109
110// `static const`, initialised HERE rather than `extern` against a definition in the .c: a
111// const object whose initializer every translation unit can see is a COMPILE-TIME FACT, so
112// `HappyEyeballs.pref(work)` resolves to a named function and becomes a DIRECT call. An extern table
113// leaves the call indirect and the symbol live at every level, -O2 -flto included.
114static const HappyEyeballsNs HappyEyeballs __attribute__((unused)) = {
115 .pref = protocore_happy_eyeballs_pref,
116 .order = protocore_happy_eyeballs_order,
117 .attempt_due = protocore_happy_eyeballs_attempt_due,
118};
119
120/**
121 * @brief The PROTOCORE_HAPPY_EYEBALLS_BORROW bytes this module's state lives in.
122 *
123 * Stated beside the namespace rather than on it: an entry takes a borrow, and this is where
124 * that borrow comes from. Taken once from the end of the pool, which no mark and no release
125 * walks, so the state lasts the life of the program.
126 *
127 * @return the span.
128 */
129uint8_t *protocore_happy_eyeballs_span(void);
130
132
133#endif // PROTOCORE_ENABLE_HAPPY_EYEBALLS
134
135#endif // PROTOCORE_HAPPY_EYEBALLS_H
Layer 3 (Network) - a family-tagged IP address (IPv4 or IPv6) with RFC-faithful text parsing,...
A v4 or v6 address in network (big-endian) byte order.
Definition ip.h:56
#define PROTOCORE_BEGIN_DECLS
Give a header's declarations C linkage, so their symbol names carry no parameter types.
Definition types.h:96
_Bool proto_bool
The truth value.
Definition types.h:64
#define PROTOCORE_END_DECLS
Definition types.h:97