Time Shield Library
C++ library for working with time
Toggle main menu visibility
Loading...
Searching...
No Matches
ntp_packet.hpp
Go to the documentation of this file.
1
// SPDX-License-Identifier: MIT
2
#pragma once
3
#ifndef TIME_SHIELD_HEADER_TIME_SHIELD_NTP_CLIENT_NTP_PACKET_HPP_INCLUDED
4
#define TIME_SHIELD_HEADER_TIME_SHIELD_NTP_CLIENT_NTP_PACKET_HPP_INCLUDED
5
6
#include <cstdint>
7
#include <cstring>
8
9
#if TIME_SHIELD_PLATFORM_WINDOWS
10
# include <winsock2.h>
11
#else
12
# include <arpa/inet.h>
13
#endif
14
15
namespace
time_shield
{
16
namespace
detail
{
17
20
struct
NtpPacket
{
21
uint8_t
li_vn_mode
;
22
uint8_t
stratum
;
23
uint8_t
poll
;
24
uint8_t
precision
;
25
uint32_t
root_delay
;
26
uint32_t
root_dispersion
;
27
uint32_t
ref_id
;
28
uint32_t
ref_ts_sec
;
29
uint32_t
ref_ts_frac
;
30
uint32_t
orig_ts_sec
;
31
uint32_t
orig_ts_frac
;
32
uint32_t
recv_ts_sec
;
33
uint32_t
recv_ts_frac
;
34
uint32_t
tx_ts_sec
;
35
uint32_t
tx_ts_frac
;
36
};
37
38
static_assert
(
sizeof
(
NtpPacket
) == 48,
"NtpPacket must be 48 bytes"
);
39
42
enum
NtpProtoError
{
43
NTP_EPROTO_BASE
= -10000,
44
NTP_E_BAD_MODE
=
NTP_EPROTO_BASE
- 1,
45
NTP_E_BAD_VERSION
=
NTP_EPROTO_BASE
- 2,
46
NTP_E_BAD_LI
=
NTP_EPROTO_BASE
- 3,
47
NTP_E_BAD_STRATUM
=
NTP_EPROTO_BASE
- 4,
48
NTP_E_KOD
=
NTP_EPROTO_BASE
- 5,
49
NTP_E_BAD_TS
=
NTP_EPROTO_BASE
- 6
50
};
51
53
static
inline
uint8_t
ntp_li
(uint8_t li_vn_mode)
noexcept
{
54
return
static_cast<
uint8_t
>
((li_vn_mode >> 6) & 0x03);
55
}
56
58
static
inline
uint8_t
ntp_vn
(uint8_t li_vn_mode)
noexcept
{
59
return
static_cast<
uint8_t
>
((li_vn_mode >> 3) & 0x07);
60
}
61
63
static
inline
uint8_t
ntp_mode
(uint8_t li_vn_mode)
noexcept
{
64
return
static_cast<
uint8_t
>
(li_vn_mode & 0x07);
65
}
66
68
static
inline
uint64_t
ntp_frac_to_us
(uint32_t frac_net)
noexcept
{
69
const
uint64_t frac =
static_cast<
uint64_t
>
(ntohl(frac_net));
70
return
(frac * 1000000ULL) >> 32;
71
}
72
74
static
inline
bool
ntp_ts_to_unix_us
(uint32_t sec_net, uint32_t frac_net, uint64_t& out_us)
noexcept
{
75
static
const
int64_t NTP_TIMESTAMP_DELTA = 2208988800ll;
76
const
int64_t sec =
static_cast<
int64_t
>
(ntohl(sec_net)) - NTP_TIMESTAMP_DELTA;
77
if
(sec < 0)
return
false
;
78
out_us =
static_cast<
uint64_t
>
(sec) * 1000000ULL +
ntp_frac_to_us
(frac_net);
79
return
true
;
80
}
81
83
static
inline
void
fill_client_packet
(
NtpPacket
& pkt, uint64_t now_us) {
84
std::memset(&pkt, 0,
sizeof
(pkt));
85
pkt.
li_vn_mode
=
static_cast<
uint8_t
>
((0 << 6) | (3 << 3) | 3);
// LI=0, VN=3, Mode=3
86
87
const
uint64_t sec = now_us / 1000000 + 2208988800ULL;
88
const
uint64_t frac = ((now_us % 1000000) * 0x100000000ULL) / 1000000;
89
90
pkt.
tx_ts_sec
= htonl(
static_cast<
uint32_t
>
(sec));
91
pkt.
tx_ts_frac
= htonl(
static_cast<
uint32_t
>
(frac));
92
}
93
95
static
inline
bool
parse_server_packet
(
const
NtpPacket
& pkt,
96
uint64_t arrival_us,
97
int64_t& offset_us,
98
int64_t& delay_us,
99
int
& stratum,
100
int
& out_error_code)
noexcept
{
101
const
uint8_t li =
ntp_li
(pkt.li_vn_mode);
102
const
uint8_t vn =
ntp_vn
(pkt.li_vn_mode);
103
const
uint8_t mode =
ntp_mode
(pkt.li_vn_mode);
104
105
if
(mode != 4) {
106
out_error_code =
NTP_E_BAD_MODE
;
107
return
false
;
108
}
109
if
(vn < 3 || vn > 4) {
110
out_error_code =
NTP_E_BAD_VERSION
;
111
return
false
;
112
}
113
if
(li == 3) {
114
out_error_code =
NTP_E_BAD_LI
;
115
return
false
;
116
}
117
if
(pkt.stratum == 0) {
118
out_error_code =
NTP_E_KOD
;
119
return
false
;
120
}
121
if
(pkt.stratum >= 16) {
122
out_error_code =
NTP_E_BAD_STRATUM
;
123
return
false
;
124
}
125
126
uint64_t originate_us = 0;
127
uint64_t receive_us = 0;
128
uint64_t transmit_us = 0;
129
130
if
(!
ntp_ts_to_unix_us
(pkt.orig_ts_sec, pkt.orig_ts_frac, originate_us)) {
131
out_error_code =
NTP_E_BAD_TS
;
132
return
false
;
133
}
134
if
(!
ntp_ts_to_unix_us
(pkt.recv_ts_sec, pkt.recv_ts_frac, receive_us)) {
135
out_error_code =
NTP_E_BAD_TS
;
136
return
false
;
137
}
138
if
(!
ntp_ts_to_unix_us
(pkt.tx_ts_sec, pkt.tx_ts_frac, transmit_us)) {
139
out_error_code =
NTP_E_BAD_TS
;
140
return
false
;
141
}
142
143
const
int64_t t1 =
static_cast<
int64_t
>
(originate_us);
144
const
int64_t t2 =
static_cast<
int64_t
>
(receive_us);
145
const
int64_t t3 =
static_cast<
int64_t
>
(transmit_us);
146
const
int64_t t4 =
static_cast<
int64_t
>
(arrival_us);
147
148
if
(t3 < t2) {
149
out_error_code =
NTP_E_BAD_TS
;
150
return
false
;
151
}
152
153
offset_us = ((t2 - t1) + (t3 - t4)) / 2;
154
delay_us = (t4 - t1) - (t3 - t2);
155
if
(delay_us < 0) {
156
out_error_code =
NTP_E_BAD_TS
;
157
return
false
;
158
}
159
stratum = pkt.stratum;
160
return
true
;
161
}
162
163
}
// namespace detail
164
}
// namespace time_shield
165
166
#endif
// TIME_SHIELD_HEADER_TIME_SHIELD_NTP_CLIENT_NTP_PACKET_HPP_INCLUDED
time_shield::detail::NtpProtoError
NtpProtoError
Protocol-level error codes for NTP parsing.
Definition
ntp_packet.hpp:42
time_shield::detail::NTP_E_BAD_MODE
@ NTP_E_BAD_MODE
Definition
ntp_packet.hpp:44
time_shield::detail::NTP_E_KOD
@ NTP_E_KOD
Definition
ntp_packet.hpp:48
time_shield::detail::NTP_E_BAD_LI
@ NTP_E_BAD_LI
Definition
ntp_packet.hpp:46
time_shield::detail::NTP_E_BAD_STRATUM
@ NTP_E_BAD_STRATUM
Definition
ntp_packet.hpp:47
time_shield::detail::NTP_EPROTO_BASE
@ NTP_EPROTO_BASE
Definition
ntp_packet.hpp:43
time_shield::detail::NTP_E_BAD_VERSION
@ NTP_E_BAD_VERSION
Definition
ntp_packet.hpp:45
time_shield::detail::NTP_E_BAD_TS
@ NTP_E_BAD_TS
Definition
ntp_packet.hpp:49
time_shield::detail
Definition
fast_date.hpp:14
time_shield::detail::ntp_frac_to_us
static uint64_t ntp_frac_to_us(uint32_t frac_net) noexcept
Convert NTP fractional seconds to microseconds.
Definition
ntp_packet.hpp:68
time_shield::detail::ntp_li
static uint8_t ntp_li(uint8_t li_vn_mode) noexcept
Extract leap indicator from LI/VN/Mode field.
Definition
ntp_packet.hpp:53
time_shield::detail::ntp_mode
static uint8_t ntp_mode(uint8_t li_vn_mode) noexcept
Extract mode from LI/VN/Mode field.
Definition
ntp_packet.hpp:63
time_shield::detail::ntp_vn
static uint8_t ntp_vn(uint8_t li_vn_mode) noexcept
Extract version number from LI/VN/Mode field.
Definition
ntp_packet.hpp:58
time_shield::detail::ntp_ts_to_unix_us
static bool ntp_ts_to_unix_us(uint32_t sec_net, uint32_t frac_net, uint64_t &out_us) noexcept
Convert NTP timestamp parts to Unix microseconds.
Definition
ntp_packet.hpp:74
time_shield::detail::parse_server_packet
static bool parse_server_packet(const NtpPacket &pkt, uint64_t arrival_us, int64_t &offset_us, int64_t &delay_us, int &stratum, int &out_error_code) noexcept
Parse server response and compute offset and delay.
Definition
ntp_packet.hpp:95
time_shield::detail::fill_client_packet
static void fill_client_packet(NtpPacket &pkt, uint64_t now_us)
Fill an NTP client request packet using local time.
Definition
ntp_packet.hpp:83
time_shield
Main namespace for the Time Shield library.
time_shield::detail::NtpPacket
NTP packet layout (48 bytes).
Definition
ntp_packet.hpp:20
time_shield::detail::NtpPacket::ref_ts_sec
uint32_t ref_ts_sec
Definition
ntp_packet.hpp:28
time_shield::detail::NtpPacket::root_delay
uint32_t root_delay
Definition
ntp_packet.hpp:25
time_shield::detail::NtpPacket::ref_ts_frac
uint32_t ref_ts_frac
Definition
ntp_packet.hpp:29
time_shield::detail::NtpPacket::tx_ts_frac
uint32_t tx_ts_frac
Definition
ntp_packet.hpp:35
time_shield::detail::NtpPacket::li_vn_mode
uint8_t li_vn_mode
Definition
ntp_packet.hpp:21
time_shield::detail::NtpPacket::ref_id
uint32_t ref_id
Definition
ntp_packet.hpp:27
time_shield::detail::NtpPacket::stratum
uint8_t stratum
Definition
ntp_packet.hpp:22
time_shield::detail::NtpPacket::tx_ts_sec
uint32_t tx_ts_sec
Definition
ntp_packet.hpp:34
time_shield::detail::NtpPacket::orig_ts_frac
uint32_t orig_ts_frac
Definition
ntp_packet.hpp:31
time_shield::detail::NtpPacket::root_dispersion
uint32_t root_dispersion
Definition
ntp_packet.hpp:26
time_shield::detail::NtpPacket::precision
uint8_t precision
Definition
ntp_packet.hpp:24
time_shield::detail::NtpPacket::orig_ts_sec
uint32_t orig_ts_sec
Definition
ntp_packet.hpp:30
time_shield::detail::NtpPacket::poll
uint8_t poll
Definition
ntp_packet.hpp:23
time_shield::detail::NtpPacket::recv_ts_frac
uint32_t recv_ts_frac
Definition
ntp_packet.hpp:33
time_shield::detail::NtpPacket::recv_ts_sec
uint32_t recv_ts_sec
Definition
ntp_packet.hpp:32
include
time_shield
ntp_client
ntp_packet.hpp
Generated by
1.17.0