ProtoCore v1.0.16
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
auth_lockout.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 auth_lockout.h
6 * @brief Per-source-address authentication lockout (PROTOCORE_ENABLE_AUTH_LOCKOUT).
7 *
8 * A small fixed table of buckets, one per recently-seen source address: consecutive failures
9 * escalate a lockout, a success clears it, and the least recently used bucket is evicted when the
10 * table is full. The window arithmetic is unsigned throughout, so a millisecond-counter rollover
11 * cannot unlock an address early or lock one out forever.
12 *
13 * @author Douglas Quigg (dstroy0)
14 * @date 2026
15 */
16
17#ifndef PROTOCORE_AUTH_LOCKOUT_H
18#define PROTOCORE_AUTH_LOCKOUT_H
19
20#include "protocore_config.h" // the entry point: protocore_ip, and the widths
21
22#if PROTOCORE_ENABLE_AUTH_LOCKOUT
23
25
26// PROTOCORE_AUTH_LOCKOUT_BORROW - the bytes the table lives in - is stated in protocore_config.h,
27// which sums it into the secure arena. A caller takes them once for the life of the program and
28// passes the pointer to every call. How they are carved is this module's and is never named here.
29
30/** @brief The source address a call is about; see shared/ip/ip.h. */
31struct protocore_ip;
32
33/** @brief The address a call is about, and the clock it is asked at. */
34typedef struct
35{
36 const struct protocore_ip *ip; ///< the source address; unspecified addresses are untrackable
37 uint32_t now_ms; ///< the caller's monotonic clock, so this module keeps none
38} AuthLockoutArgs;
39
40/**
41 * @brief Per-source-address authentication lockout.
42 *
43 * A caller sets the members a call takes, invokes it through ::AuthLockout with the bytes it runs
44 * out of, and reads the outcome off the same handle.
45 *
46 * AuthLockout.args.ip = &peer;
47 * AuthLockout.args.now_ms = Clock.ms;
48 * AuthLockout.remaining(work);
49 * // AuthLockout.ms is 0 when the address may try again
50 *
51 * @var AuthLockoutNs::args the address a call is about, and the clock it is asked at
52 * @var AuthLockoutNs::ok a call's true/false outcome
53 * @var AuthLockoutNs::ms milliseconds until the lockout expires; 0 when not locked out
54 * @var AuthLockoutNs::remaining read the remaining lockout for the address
55 * @var AuthLockoutNs::fail record a failed authentication; may start or escalate a lockout
56 * @var AuthLockoutNs::succeed clear the address's failure and lockout state
57 * @var AuthLockoutNs::reset empty the whole table
58 *
59 * @c work is PROTOCORE_AUTH_LOCKOUT_BORROW secure bytes the CALLER took, at an address it knows. It
60 * is not held past the call, so nothing here aliases it. The table is in
61 * those bytes rather than in this module, so a caller takes them once for the life of the program
62 * and every call runs out of the same span. The caller releases it, and the pool wipes on release;
63 * this module neither takes it, holds it, nor releases it.
64 *
65 * No storage member and no context: a caller sets operands and reads @ref AuthLockoutNs::ok, and
66 * that is all the surface there is.
67 */
68typedef struct
69{
70 AuthLockoutArgs args;
71 proto_bool ok;
72 uint32_t ms;
73} AuthLockoutVars;
74
75/** @brief The operands and the outcome. */
76extern AuthLockoutVars AuthLockoutV;
77
78/** @brief The entries. */
79typedef struct
80{
81 void (*const remaining)(uint8_t *work);
82 void (*const fail)(uint8_t *work);
83 void (*const succeed)(uint8_t *work);
84 void (*const reset)(uint8_t *work);
85} AuthLockoutNs;
86
87// What the table binds, defined once in the .c and taking one parameter each: everything
88// else an entry needs is an operand in AuthLockoutV or a region of the borrow at a fixed offset.
89void protocore_auth_lockout_remaining(uint8_t *work);
90void protocore_auth_lockout_fail(uint8_t *work);
91void protocore_auth_lockout_succeed(uint8_t *work);
92void protocore_auth_lockout_reset(uint8_t *work);
93
94// `static const`, initialised HERE rather than `extern` against a definition in the .c: a
95// const object whose initializer every translation unit can see is a COMPILE-TIME FACT, so
96// `AuthLockout.remaining(work)` resolves to a named function and becomes a DIRECT call. An extern table
97// leaves the call indirect and the symbol live at every level, -O2 -flto included.
98static const AuthLockoutNs AuthLockout __attribute__((unused)) = {
99 .remaining = protocore_auth_lockout_remaining,
100 .fail = protocore_auth_lockout_fail,
101 .succeed = protocore_auth_lockout_succeed,
102 .reset = protocore_auth_lockout_reset,
103};
104
105/**
106 * @brief The PROTOCORE_AUTH_LOCKOUT_BORROW bytes the program's table lives in.
107 *
108 * Stated beside the namespace rather than on it: an entry takes a borrow, and this is where that
109 * borrow comes from. Taken once from the end of the secure pool, which no mark and no release walks,
110 * so the table lasts the life of the program.
111 *
112 * @return the span.
113 */
114uint8_t *protocore_auth_lockout_span(void);
115
117
118#endif // PROTOCORE_ENABLE_AUTH_LOCKOUT
119
120#endif // PROTOCORE_AUTH_LOCKOUT_H
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