ProtoCore v1.0.16
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
ntp_server.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 ntp_server.h
6 * @brief NTP/SNTP time server (RFC 5905 / RFC 4330 server mode) on UDP/123.
7 *
8 * Answers client NTP requests from the device's own clock. Stateless request/response: a
9 * client sends a 48-octet packet, the server fills in the reference/receive/transmit
10 * timestamps, echoes the client's transmit stamp as the origin, and sends it back. Zero
11 * heap; gated by PROTOCORE_ENABLE_NTP_SERVER (default off).
12 *
13 * protocore_ntp_server_build_response() is pure: request bytes and an NTP-epoch time in, reply
14 * bytes out. protocore_ntp_server_begin() binds UDP/123 via the transport UDP service and drives
15 * it from `protocore_time_now()` (seconds) plus a `protocore_millis()`-derived sub-second fraction.
16 *
17 * @author Douglas Quigg (dstroy0)
18 * @date 2026
19 */
20
21#ifndef PROTOCORE_NTP_SERVER_H
22#define PROTOCORE_NTP_SERVER_H
23
24#include "protocore_config.h" // the entry point: protocore_types.h for the widths
25
26#if PROTOCORE_ENABLE_NTP_SERVER
27
28#include "network_drivers/application/ntp/ntp.h" // the complete type a public struct below holds by value
29
31
32// PROTOCORE_NTP_SERVER_BORROW - the bytes this module runs out of - is stated in protocore_config.h, which sums
33// it into its arena. A caller takes them once and passes the pointer to every call. How they
34// are carved is this module's and is never named here.
35
36/** @brief What build_response takes: req, req_len, stratum, refid, ... */
37typedef struct
38{
39 const uint8_t *req; ///< the received request bytes
40 size_t req_len; ///< length of req (must be >= PROTOCORE_NTP_PACKET_LEN)
41 uint8_t stratum; ///< stratum to advertise (1-15)
42 uint32_t refid; ///< reference identifier (e.g. PROTOCORE_NTP_REFID_LOCL)
43 uint32_t
44 protocore_ntp_secs; ///< current time, seconds since the NTP epoch (Unix seconds + PROTOCORE_NTP_UNIX_OFFSET)
45 uint32_t protocore_ntp_frac; ///< sub-second fraction as a 32-bit binary fraction of a second
46 uint8_t *out; ///< output buffer
47 size_t out_cap; ///< capacity of out (must be >= PROTOCORE_NTP_PACKET_LEN)
48} NtpServerBuildResponseArgs;
49
50/** @brief What begin takes: stratum, refid. */
51typedef struct
52{
53 uint8_t stratum; ///< the stratum to advertise (1 for a GPS/reference clock, 2-15 for a relay)
54 uint32_t refid; ///< the reference identifier to advertise (PROTOCORE_NTP_REFID_LOCL, PROTOCORE_NTP_REFID_GPS, ...
55} NtpServerBeginArgs;
56
57/**
58 * @brief NTP/SNTP time server (RFC 5905 / RFC 4330 server mode) on UDP/123.
59 *
60 * A caller sets the members a call takes, invokes it through ::NtpServer with the bytes it runs
61 * out of, and reads the outcome off the same handle.
62 *
63 * NtpServer.build_response_args.req = ...;
64 * NtpServer.build_response_args.req_len = ...;
65 * NtpServer.build_response_args.stratum = ...;
66 * NtpServer.build_response_args.refid = ...;
67 * NtpServer.build_response_args.protocore_ntp_secs = ...;
68 * NtpServer.build_response_args.protocore_ntp_frac = ...;
69 * NtpServer.build_response_args.out = ...;
70 * NtpServer.build_response_args.out_cap = ...;
71 * NtpServer.build_response(work);
72 * // NtpServer.n is what the call reports
73 *
74 * @var NtpServerNs::build_response_args what build_response takes: req, req_len, stratum, refid,
75 * @var NtpServerNs::begin_args what begin takes: stratum, refid
76 * @var NtpServerNs::ok true if the UDP listener bound; false on a host build or if the ...
77 * @var NtpServerNs::n PROTOCORE_NTP_PACKET_LEN on success, or 0 if a length is too small
78 * @var NtpServerNs::build_response build a server (mode 4) reply to a client NTP request. Pure - no ...
79 * @var NtpServerNs::begin start answering NTP requests on UDP/123 from the device's own ...
80 *
81 * @c work is PROTOCORE_NTP_SERVER_BORROW bytes the CALLER took, at an address it knows. It is not held past the call,
82 * so nothing here aliases it. How those bytes are carved is this module's and is never named here.
83 */
84typedef struct
85{
86 NtpServerBuildResponseArgs build_response_args;
87 NtpServerBeginArgs begin_args;
88 proto_bool ok;
89 size_t n;
90} NtpServerVars;
91
92/** @brief The operands and the outcome. */
93extern NtpServerVars NtpServerV;
94
95/** @brief The entries. */
96typedef struct
97{
98 void (*const build_response)(uint8_t *work);
99 void (*const begin)(uint8_t *work);
100} NtpServerNs;
101
102// What the table binds, defined once in the .c and taking one parameter each: everything
103// else an entry needs is an operand in NtpServerV or a region of the borrow at a fixed offset.
104void protocore_ntp_server_build_response(uint8_t *work);
105void protocore_ntp_server_begin(uint8_t *work);
106
107// `static const`, initialised HERE rather than `extern` against a definition in the .c: a
108// const object whose initializer every translation unit can see is a COMPILE-TIME FACT, so
109// `NtpServer.build_response(work)` resolves to a named function and becomes a DIRECT call. An extern table
110// leaves the call indirect and the symbol live at every level, -O2 -flto included.
111static const NtpServerNs NtpServer __attribute__((unused)) = {
112 .build_response = protocore_ntp_server_build_response,
113 .begin = protocore_ntp_server_begin,
114};
115
116/**
117 * @brief The PROTOCORE_NTP_SERVER_BORROW bytes this module's state lives in.
118 *
119 * Stated beside the namespace rather than on it: an entry takes a borrow, and this is where
120 * that borrow comes from. Taken once from the end of the pool, which no mark and no release
121 * walks, so the state lasts the life of the program.
122 *
123 * @return the span.
124 */
125uint8_t *protocore_ntp_server_span(void);
126
128
129#endif // PROTOCORE_ENABLE_NTP_SERVER
130
131#endif // PROTOCORE_NTP_SERVER_H
The NTP packet on the wire (RFC 5905 sec 7.3, Figure 8), shared by the client and server.
#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