20 #include <ripple/basics/Log.h>
21 #include <ripple/basics/random.h>
22 #include <ripple/beast/core/CurrentThreadName.h>
23 #include <ripple/core/impl/SNTPClock.h>
24 #include <boost/asio.hpp>
36 0x1B, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
37 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
39 using namespace std::chrono_literals;
47 #define NTP_SAMPLE_WINDOW 9
56 #define NTP_OFF_INFO 0
57 #define NTP_OFF_ROOTDELAY 1
58 #define NTP_OFF_ROOTDISP 2
59 #define NTP_OFF_REFERENCEID 3
60 #define NTP_OFF_REFTS_INT 4
61 #define NTP_OFF_REFTS_FRAC 5
62 #define NTP_OFF_ORGTS_INT 6
63 #define NTP_OFF_ORGTS_FRAC 7
64 #define NTP_OFF_RECVTS_INT 8
65 #define NTP_OFF_RECVTS_FRAC 9
66 #define NTP_OFF_XMITTS_INT 10
67 #define NTP_OFF_XMITTS_FRAC 11
72 template <
class Duration>
84 : replied(false), sent(j)
97 boost::asio::basic_waitable_timer<std::chrono::system_clock>
timer_;
104 boost::asio::ip::udp::endpoint
ep_;
112 , socket_(io_service_)
113 , timer_(io_service_)
114 , resolver_(io_service_)
128 work_ = std::nullopt;
140 if (it == servers.
end())
142 JLOG(j_.
info()) <<
"SNTP: no server specified";
148 for (
auto const& item : servers)
154 socket_.open(ip::udp::v4());
156 socket_.async_receive_from(
162 std::placeholders::_1,
163 std::placeholders::_2));
176 auto const when = time_point_cast<seconds>(clock_type::now());
177 if ((lastUpdate_ == sys_seconds::max()) ||
179 time_point_cast<seconds>(clock_type::now())))
181 return when + offset_;
204 if (ec == error::operation_aborted)
208 JLOG(j_.
error()) <<
"SNTPClock::onTimer: " << ec.message();
223 if (ec == error::operation_aborted)
234 JLOG(j_.
trace()) <<
"SNTP: Packet from " << ep_;
236 auto const query = queries_.
find(ep_);
237 if (query == queries_.
end())
239 JLOG(j_.
debug()) <<
"SNTP: Reply from " << ep_
240 <<
" found without matching query";
242 else if (query->second.replied)
244 JLOG(j_.
debug()) <<
"SNTP: Duplicate response from " << ep_;
248 query->second.replied =
true;
250 if (time_point_cast<seconds>(clock_type::now()) >
251 (query->second.sent + 1s))
253 JLOG(j_.
warn()) <<
"SNTP: Late response from " << ep_;
255 else if (bytes_xferd < 48)
257 JLOG(j_.
warn()) <<
"SNTP: Short reply from " << ep_ <<
" ("
258 << bytes_xferd <<
") " << buf_.
size();
262 &buf_[0])[NTP_OFF_ORGTS_FRAC] != query->second.nonce)
265 <<
"SNTP: Reply from " << ep_ <<
"had wrong nonce";
274 socket_.async_receive_from(
280 std::placeholders::_1,
281 std::placeholders::_2));
305 auto best = servers_.
end();
307 for (
auto iter = servers_.
begin(), end = best; iter != end; ++iter)
308 if ((best == end) || (iter->second == sys_seconds::max()) ||
309 (iter->second < best->second))
312 if (best == servers_.
end())
314 JLOG(j_.
trace()) <<
"SNTP: No server to query";
319 auto now = time_point_cast<seconds>(clock_type::now());
321 if ((best->second != sys_seconds::max()) &&
324 JLOG(j_.
trace()) <<
"SNTP: All servers recently queried";
330 boost::asio::ip::udp::resolver::query query(
331 boost::asio::ip::udp::v4(), best->first,
"ntp");
332 resolver_.async_resolve(
337 std::placeholders::_1,
338 std::placeholders::_2));
339 JLOG(j_.
trace()) <<
"SNTPClock: Resolve pending for " << best->first;
346 boost::asio::ip::udp::resolver::iterator it)
349 if (ec == error::operation_aborted)
353 JLOG(j_.
trace()) <<
"SNTPClock::resolveComplete: " << ec.message();
357 assert(it != ip::udp::resolver::iterator());
362 while (++it != ip::udp::resolver::iterator())
368 if (sel != ip::udp::resolver::iterator())
371 Query& query = queries_[*sel];
373 auto now = time_point_cast<seconds>(clock_type::now());
375 if ((query.
sent == now) || ((query.
sent + 1s) == now))
379 JLOG(j_.
trace()) <<
"SNTP: Redundant query suppressed";
385 query.
nonce = rand_int<std::uint32_t>();
392 (time_point_cast<seconds>(clock_type::now()) +
398 socket_.async_send_to(
404 std::placeholders::_1,
405 std::placeholders::_2));
412 if (ec == boost::asio::error::operation_aborted)
417 JLOG(j_.
warn()) <<
"SNTPClock::onSend: " << ec.message();
426 assert(buf_.
size() >= 48);
430 unsigned info = ntohl(recvBuffer[NTP_OFF_INFO]);
431 auto timev =
seconds{ntohl(recvBuffer[NTP_OFF_RECVTS_INT])};
432 unsigned stratum = (info >> 16) & 0xff;
434 if ((info >> 30) == 3)
436 JLOG(j_.
info()) <<
"SNTP: Alarm condition " << ep_;
440 if ((stratum == 0) || (stratum > 14))
442 JLOG(j_.
info()) <<
"SNTP: Unreasonable stratum (" << stratum
448 auto now = time_point_cast<seconds>(clock_type::now());
449 timev -= now.time_since_epoch();
455 if (offsets_.
size() >= NTP_SAMPLE_WINDOW)
461 auto offsetList = offsets_;
462 std::sort(offsetList.begin(), offsetList.end());
463 auto j = offsetList.size();
464 auto it =
std::next(offsetList.begin(), j / 2);
468 offset_ = (offset_ + (*--it)) / 2;
472 if ((offset_ == -1s) || (offset_ == 1s))
475 if (timev != 0s || offset_ != 0s)
477 JLOG(j_.
trace()) <<
"SNTP: Offset is " << timev.count()
478 <<
", new system offset is " << offset_.
count();
488 return std::make_unique<SNTPClientImp>(j);