ntpquery.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. /*
  2. * Copyright (c) 2020, Nico Weber <thakis@chromium.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #define _BSD_SOURCE
  7. #define _DEFAULT_SOURCE
  8. #include <AK/Assertions.h>
  9. #include <AK/Endian.h>
  10. #include <AK/Random.h>
  11. #include <LibCore/ArgsParser.h>
  12. #include <arpa/inet.h>
  13. #include <inttypes.h>
  14. #include <math.h>
  15. #include <netdb.h>
  16. #include <netinet/in.h>
  17. #include <stdio.h>
  18. #include <string.h>
  19. #include <sys/socket.h>
  20. #include <sys/time.h>
  21. #include <sys/uio.h>
  22. #include <time.h>
  23. // An NtpTimestamp is a 64-bit integer that's a 32.32 binary-fixed point number.
  24. // The integral part in the upper 32 bits represents seconds since 1900-01-01.
  25. // The fractional part in the lower 32 bits stores fractional bits times 2 ** 32.
  26. using NtpTimestamp = uint64_t;
  27. struct [[gnu::packed]] NtpPacket {
  28. uint8_t li_vn_mode;
  29. uint8_t stratum;
  30. int8_t poll;
  31. int8_t precision;
  32. uint32_t root_delay;
  33. uint32_t root_dispersion;
  34. uint32_t reference_id;
  35. NtpTimestamp reference_timestamp;
  36. NtpTimestamp origin_timestamp;
  37. NtpTimestamp receive_timestamp;
  38. NtpTimestamp transmit_timestamp;
  39. uint8_t leap_information() const { return li_vn_mode >> 6; }
  40. uint8_t version_number() const { return (li_vn_mode >> 3) & 7; }
  41. uint8_t mode() const { return li_vn_mode & 7; }
  42. };
  43. static_assert(sizeof(NtpPacket) == 48);
  44. // NTP measures time in seconds since 1900-01-01, POSIX in seconds since 1970-01-01.
  45. // 1900 wasn't a leap year, so there are 70/4 leap years between 1900 and 1970.
  46. // Overflows a 32-bit signed int, but not a 32-bit unsigned int.
  47. const unsigned SecondsFrom1900To1970 = (70u * 365u + 70u / 4u) * 24u * 60u * 60u;
  48. static NtpTimestamp ntp_timestamp_from_timeval(const timeval& t)
  49. {
  50. VERIFY(t.tv_usec >= 0 && t.tv_usec < 1'000'000); // Fits in 20 bits when normalized.
  51. // Seconds just need translation to the different origin.
  52. uint32_t seconds = t.tv_sec + SecondsFrom1900To1970;
  53. // Fractional bits are decimal fixed point (*1'000'000) in timeval, but binary fixed-point (* 2**32) in NTP timestamps.
  54. uint32_t fractional_bits = static_cast<uint32_t>((static_cast<uint64_t>(t.tv_usec) << 32) / 1'000'000);
  55. return (static_cast<NtpTimestamp>(seconds) << 32) | fractional_bits;
  56. }
  57. static timeval timeval_from_ntp_timestamp(const NtpTimestamp& ntp_timestamp)
  58. {
  59. timeval t;
  60. t.tv_sec = static_cast<time_t>(ntp_timestamp >> 32) - SecondsFrom1900To1970;
  61. t.tv_usec = static_cast<suseconds_t>((static_cast<uint64_t>(ntp_timestamp & 0xFFFFFFFFu) * 1'000'000) >> 32);
  62. return t;
  63. }
  64. static String format_ntp_timestamp(NtpTimestamp ntp_timestamp)
  65. {
  66. char buffer[28]; // YYYY-MM-DDTHH:MM:SS.UUUUUUZ is 27 characters long.
  67. timeval t = timeval_from_ntp_timestamp(ntp_timestamp);
  68. struct tm tm;
  69. gmtime_r(&t.tv_sec, &tm);
  70. size_t written = strftime(buffer, sizeof(buffer), "%Y-%m-%dT%T.", &tm);
  71. VERIFY(written == 20);
  72. written += snprintf(buffer + written, sizeof(buffer) - written, "%06d", (int)t.tv_usec);
  73. VERIFY(written == 26);
  74. buffer[written++] = 'Z';
  75. buffer[written] = '\0';
  76. return buffer;
  77. }
  78. int main(int argc, char** argv)
  79. {
  80. #ifdef __serenity__
  81. if (pledge("stdio inet unix settime", nullptr) < 0) {
  82. perror("pledge");
  83. return 1;
  84. }
  85. #endif
  86. bool adjust_time = false;
  87. bool set_time = false;
  88. bool verbose = false;
  89. // FIXME: Change to serenityos.pool.ntp.org once https://manage.ntppool.org/manage/vendor/zone?a=km5a8h&id=vz-14154g is approved.
  90. // Other NTP servers:
  91. // - time.nist.gov
  92. // - time.apple.com
  93. // - time.cloudflare.com (has NTS), https://blog.cloudflare.com/secure-time/
  94. // - time.windows.com
  95. //
  96. // Leap seconds smearing NTP servers:
  97. // - time.facebook.com , https://engineering.fb.com/production-engineering/ntp-service/ , sine-smears over 18 hours
  98. // - time.google.com , https://developers.google.com/time/smear , linear-smears over 24 hours
  99. const char* host = "time.google.com";
  100. Core::ArgsParser args_parser;
  101. args_parser.add_option(adjust_time, "Gradually adjust system time (requires root)", "adjust", 'a');
  102. args_parser.add_option(set_time, "Immediately set system time (requires root)", "set", 's');
  103. args_parser.add_option(verbose, "Verbose output", "verbose", 'v');
  104. args_parser.add_positional_argument(host, "NTP server", "host", Core::ArgsParser::Required::No);
  105. args_parser.parse(argc, argv);
  106. if (adjust_time && set_time) {
  107. fprintf(stderr, "-a and -s are mutually exclusive\n");
  108. return 1;
  109. }
  110. #ifdef __serenity__
  111. if (!adjust_time && !set_time) {
  112. if (pledge("stdio inet unix", nullptr) < 0) {
  113. perror("pledge");
  114. return 1;
  115. }
  116. }
  117. #endif
  118. auto* hostent = gethostbyname(host);
  119. if (!hostent) {
  120. fprintf(stderr, "Lookup failed for '%s'\n", host);
  121. return 1;
  122. }
  123. #ifdef __serenity__
  124. if (pledge((adjust_time || set_time) ? "stdio inet settime" : "stdio inet", nullptr) < 0) {
  125. perror("pledge");
  126. return 1;
  127. }
  128. unveil(nullptr, nullptr);
  129. #endif
  130. int fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
  131. if (fd < 0) {
  132. perror("socket");
  133. return 1;
  134. }
  135. struct timeval timeout {
  136. 5, 0
  137. };
  138. if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) < 0) {
  139. perror("setsockopt");
  140. return 1;
  141. }
  142. int enable = 1;
  143. if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMP, &enable, sizeof(enable)) < 0) {
  144. perror("setsockopt");
  145. return 1;
  146. }
  147. sockaddr_in peer_address;
  148. memset(&peer_address, 0, sizeof(peer_address));
  149. peer_address.sin_family = AF_INET;
  150. peer_address.sin_port = htons(123);
  151. peer_address.sin_addr.s_addr = *(const in_addr_t*)hostent->h_addr_list[0];
  152. NtpPacket packet;
  153. memset(&packet, 0, sizeof(packet));
  154. packet.li_vn_mode = (4 << 3) | 3; // Version 4, client connection.
  155. // The server will copy the transmit_timestamp to origin_timestamp in the reply.
  156. // To not leak the local time, keep the time we sent the packet locally and
  157. // send random bytes to the server.
  158. auto random_transmit_timestamp = get_random<NtpTimestamp>();
  159. timeval local_transmit_time;
  160. gettimeofday(&local_transmit_time, nullptr);
  161. packet.transmit_timestamp = random_transmit_timestamp;
  162. ssize_t rc;
  163. rc = sendto(fd, &packet, sizeof(packet), 0, (const struct sockaddr*)&peer_address, sizeof(peer_address));
  164. if (rc < 0) {
  165. perror("sendto");
  166. return 1;
  167. }
  168. if ((size_t)rc < sizeof(packet)) {
  169. fprintf(stderr, "incomplete packet send\n");
  170. return 1;
  171. }
  172. iovec iov { &packet, sizeof(packet) };
  173. char control_message_buffer[CMSG_SPACE(sizeof(timeval))];
  174. msghdr msg = { &peer_address, sizeof(peer_address), &iov, 1, control_message_buffer, sizeof(control_message_buffer), 0 };
  175. rc = recvmsg(fd, &msg, 0);
  176. if (rc < 0) {
  177. perror("recvmsg");
  178. return 1;
  179. }
  180. timeval userspace_receive_time;
  181. gettimeofday(&userspace_receive_time, nullptr);
  182. if ((size_t)rc < sizeof(packet)) {
  183. fprintf(stderr, "incomplete packet recv\n");
  184. return 1;
  185. }
  186. cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
  187. VERIFY(cmsg->cmsg_level == SOL_SOCKET);
  188. VERIFY(cmsg->cmsg_type == SCM_TIMESTAMP);
  189. VERIFY(!CMSG_NXTHDR(&msg, cmsg));
  190. timeval kernel_receive_time;
  191. memcpy(&kernel_receive_time, CMSG_DATA(cmsg), sizeof(kernel_receive_time));
  192. // Checks 3 and 4 from end of section 5 of rfc4330.
  193. if (packet.version_number() != 3 && packet.version_number() != 4) {
  194. fprintf(stderr, "unexpected version number %d\n", packet.version_number());
  195. return 1;
  196. }
  197. if (packet.mode() != 4) { // 4 means "server", which should be the reply to our 3 ("client") request.
  198. fprintf(stderr, "unexpected mode %d\n", packet.mode());
  199. return 1;
  200. }
  201. if (packet.stratum == 0 || packet.stratum >= 16) {
  202. fprintf(stderr, "unexpected stratum value %d\n", packet.stratum);
  203. return 1;
  204. }
  205. if (packet.origin_timestamp != random_transmit_timestamp) {
  206. fprintf(stderr, "expected %#016" PRIx64 " as origin timestamp, got %#016" PRIx64 "\n", random_transmit_timestamp, packet.origin_timestamp);
  207. return 1;
  208. }
  209. if (packet.transmit_timestamp == 0) {
  210. fprintf(stderr, "got transmit_timestamp 0\n");
  211. return 1;
  212. }
  213. NtpTimestamp origin_timestamp = ntp_timestamp_from_timeval(local_transmit_time);
  214. NtpTimestamp receive_timestamp = be64toh(packet.receive_timestamp);
  215. NtpTimestamp transmit_timestamp = be64toh(packet.transmit_timestamp);
  216. NtpTimestamp destination_timestamp = ntp_timestamp_from_timeval(kernel_receive_time);
  217. timeval kernel_to_userspace_latency;
  218. timersub(&userspace_receive_time, &kernel_receive_time, &kernel_to_userspace_latency);
  219. if (set_time) {
  220. // FIXME: Do all the time filtering described in 5905, or at least correct for time of flight.
  221. timeval t = timeval_from_ntp_timestamp(transmit_timestamp);
  222. if (settimeofday(&t, nullptr) < 0) {
  223. perror("settimeofday");
  224. return 1;
  225. }
  226. }
  227. if (verbose) {
  228. printf("NTP response from %s:\n", inet_ntoa(peer_address.sin_addr));
  229. printf("Leap Information: %d\n", packet.leap_information());
  230. printf("Version Number: %d\n", packet.version_number());
  231. printf("Mode: %d\n", packet.mode());
  232. printf("Stratum: %d\n", packet.stratum);
  233. printf("Poll: %d\n", packet.stratum);
  234. printf("Precision: %d\n", packet.precision);
  235. printf("Root delay: %#x\n", ntohl(packet.root_delay));
  236. printf("Root dispersion: %#x\n", ntohl(packet.root_dispersion));
  237. u32 ref_id = ntohl(packet.reference_id);
  238. printf("Reference ID: %#x", ref_id);
  239. if (packet.stratum == 1) {
  240. printf(" ('%c%c%c%c')", (ref_id & 0xff000000) >> 24, (ref_id & 0xff0000) >> 16, (ref_id & 0xff00) >> 8, ref_id & 0xff);
  241. }
  242. printf("\n");
  243. printf("Reference timestamp: %#016" PRIx64 " (%s)\n", be64toh(packet.reference_timestamp), format_ntp_timestamp(be64toh(packet.reference_timestamp)).characters());
  244. printf("Origin timestamp: %#016" PRIx64 " (%s)\n", origin_timestamp, format_ntp_timestamp(origin_timestamp).characters());
  245. printf("Receive timestamp: %#016" PRIx64 " (%s)\n", receive_timestamp, format_ntp_timestamp(receive_timestamp).characters());
  246. printf("Transmit timestamp: %#016" PRIx64 " (%s)\n", transmit_timestamp, format_ntp_timestamp(transmit_timestamp).characters());
  247. printf("Destination timestamp: %#016" PRIx64 " (%s)\n", destination_timestamp, format_ntp_timestamp(destination_timestamp).characters());
  248. // When the system isn't under load, user-space t and packet_t are identical. If a shell with `yes` is running, it can be as high as 30ms in this program,
  249. // which gets user-space time immediately after the recvmsg() call. In programs that have an event loop reading from multiple sockets, it could be higher.
  250. printf("Receive latency: %" PRId64 ".%06d s\n", (i64)kernel_to_userspace_latency.tv_sec, (int)kernel_to_userspace_latency.tv_usec);
  251. }
  252. // Parts of the "Clock Filter" computations, https://tools.ietf.org/html/rfc5905#section-10
  253. NtpTimestamp T1 = origin_timestamp;
  254. NtpTimestamp T2 = receive_timestamp;
  255. NtpTimestamp T3 = transmit_timestamp;
  256. NtpTimestamp T4 = destination_timestamp;
  257. auto timestamp_difference_in_seconds = [](NtpTimestamp from, NtpTimestamp to) {
  258. return static_cast<int64_t>(to - from) / pow(2.0, 32);
  259. };
  260. // The network round-trip time of the request.
  261. // T4-T1 is the wall clock roundtrip time, in local ticks.
  262. // T3-T2 is the server side processing time, in server ticks.
  263. double delay_s = timestamp_difference_in_seconds(T1, T4) - timestamp_difference_in_seconds(T2, T3);
  264. // The offset from local time to server time, ignoring network delay.
  265. // Both T2-T1 and T3-T4 estimate this; this takes the average of both.
  266. // Or, equivalently, (T1+T4)/2 estimates local time, (T2+T3)/2 estimate server time, this is the difference.
  267. double offset_s = 0.5 * (timestamp_difference_in_seconds(T1, T2) + timestamp_difference_in_seconds(T4, T3));
  268. if (verbose)
  269. printf("Delay: %f\n", delay_s);
  270. printf("Offset: %f\n", offset_s);
  271. if (adjust_time) {
  272. long delta_us = static_cast<long>(round(offset_s * 1'000'000));
  273. timeval delta_timeval;
  274. delta_timeval.tv_sec = delta_us / 1'000'000;
  275. delta_timeval.tv_usec = delta_us % 1'000'000;
  276. if (delta_timeval.tv_usec < 0) {
  277. delta_timeval.tv_sec--;
  278. delta_timeval.tv_usec += 1'000'000;
  279. }
  280. if (adjtime(&delta_timeval, nullptr) < 0) {
  281. perror("adjtime set");
  282. return 1;
  283. }
  284. }
  285. }