ntpquery.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. /*
  2. * Copyright (c) 2020, Nico Weber <thakis@chromium.org>
  3. * All rights reserved.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are met:
  7. *
  8. * 1. Redistributions of source code must retain the above copyright notice, this
  9. * list of conditions and the following disclaimer.
  10. *
  11. * 2. Redistributions in binary form must reproduce the above copyright notice,
  12. * this list of conditions and the following disclaimer in the documentation
  13. * and/or other materials provided with the distribution.
  14. *
  15. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  16. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  17. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  18. * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  19. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  20. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  21. * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  22. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  23. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  24. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  25. */
  26. #define _GNU_SOURCE
  27. #include <AK/Random.h>
  28. #include <LibCore/ArgsParser.h>
  29. #include <arpa/inet.h>
  30. #include <endian.h>
  31. #include <math.h>
  32. #include <netdb.h>
  33. #include <netinet/in.h>
  34. #include <stdio.h>
  35. #include <string.h>
  36. #include <sys/socket.h>
  37. #include <sys/time.h>
  38. #include <sys/uio.h>
  39. // An NtpTimestamp is a 64-bit integer that's a 32.32 binary-fixed point number.
  40. // The integral part in the upper 32 bits represents seconds since 1900-01-01.
  41. // The fractional part in the lower 32 bits stores fractional bits times 2 ** 32.
  42. typedef uint64_t NtpTimestamp;
  43. struct [[gnu::packed]] NtpPacket
  44. {
  45. uint8_t li_vn_mode;
  46. uint8_t stratum;
  47. int8_t poll;
  48. int8_t precision;
  49. uint32_t root_delay;
  50. uint32_t root_dispersion;
  51. uint32_t reference_id;
  52. NtpTimestamp reference_timestamp;
  53. NtpTimestamp origin_timestamp;
  54. NtpTimestamp receive_timestamp;
  55. NtpTimestamp transmit_timestamp;
  56. };
  57. static_assert(sizeof(NtpPacket) == 48);
  58. // NTP measures time in seconds since 1900-01-01, POSIX in seconds since 1970-01-01.
  59. // 1900 wasn't a leap year, so there are 70/4 leap years between 1900 and 1970.
  60. // Overflows a 32-bit signed int, but not a 32-bit unsigned int.
  61. const unsigned SecondsFrom1900To1970 = (70u * 365u + 70u / 4u) * 24u * 60u * 60u;
  62. static NtpTimestamp ntp_timestamp_from_timeval(const timeval& t)
  63. {
  64. ASSERT(t.tv_usec >= 0 && t.tv_usec < 1'000'000); // Fits in 20 bits when normalized.
  65. // Seconds just need translation to the different origin.
  66. uint32_t seconds = t.tv_sec + SecondsFrom1900To1970;
  67. // Fractional bits are decimal fixed point (*1'000'000) in timeval, but binary fixed-point (* 2**32) in NTP timestamps.
  68. uint32_t fractional_bits = static_cast<uint32_t>((static_cast<uint64_t>(t.tv_usec) << 32) / 1'000'000);
  69. return (static_cast<NtpTimestamp>(seconds) << 32) | fractional_bits;
  70. }
  71. static timeval timeval_from_ntp_timestamp(const NtpTimestamp& ntp_timestamp)
  72. {
  73. timeval t;
  74. t.tv_sec = static_cast<time_t>(ntp_timestamp >> 32) - SecondsFrom1900To1970;
  75. t.tv_usec = static_cast<suseconds_t>((static_cast<uint64_t>(ntp_timestamp & 0xFFFFFFFFu) * 1'000'000) >> 32);
  76. return t;
  77. }
  78. static String format_ntp_timestamp(NtpTimestamp ntp_timestamp)
  79. {
  80. char buffer[28]; // YYYY-MM-DDTHH:MM:SS.UUUUUUZ is 27 characters long.
  81. timeval t = timeval_from_ntp_timestamp(ntp_timestamp);
  82. struct tm tm;
  83. gmtime_r(&t.tv_sec, &tm);
  84. size_t written = strftime(buffer, sizeof(buffer), "%Y-%m-%dT%T.", &tm);
  85. ASSERT(written == 20);
  86. written += snprintf(buffer + written, sizeof(buffer) - written, "%06d", t.tv_usec);
  87. ASSERT(written == 26);
  88. buffer[written++] = 'Z';
  89. buffer[written] = '\0';
  90. return buffer;
  91. }
  92. int main(int argc, char** argv)
  93. {
  94. if (pledge("stdio inet dns settime", nullptr) < 0) {
  95. perror("pledge");
  96. return 1;
  97. }
  98. bool set_time = false;
  99. bool verbose = false;
  100. // FIXME: Change to serenityos.pool.ntp.org once https://manage.ntppool.org/manage/vendor/zone?a=km5a8h&id=vz-14154g is approved.
  101. // Other NTP servers:
  102. // - time.nist.gov
  103. // - time.apple.com
  104. // - time.cloudflare.com (has NTS), https://blog.cloudflare.com/secure-time/
  105. // - time.windows.com
  106. //
  107. // Leap seconds smearing NTP servers:
  108. // - time.facebook.com , https://engineering.fb.com/production-engineering/ntp-service/ , sine-smears over 18 hours
  109. // - time.google.com , https://developers.google.com/time/smear , linear-smears over 24 hours
  110. const char* host = "time.google.com";
  111. Core::ArgsParser args_parser;
  112. args_parser.add_option(set_time, "Adjust system time (requires root)", "set", 's');
  113. args_parser.add_option(verbose, "Verbose output", "verbose", 'v');
  114. args_parser.add_positional_argument(host, "NTP server", "host", Core::ArgsParser::Required::No);
  115. args_parser.parse(argc, argv);
  116. if (!set_time) {
  117. if (pledge("stdio inet dns", nullptr) < 0) {
  118. perror("pledge");
  119. return 1;
  120. }
  121. }
  122. auto* hostent = gethostbyname(host);
  123. if (!hostent) {
  124. fprintf(stderr, "Lookup failed for '%s'\n", host);
  125. return 1;
  126. }
  127. if (pledge(set_time ? "stdio inet settime" : "stdio inet", nullptr) < 0) {
  128. perror("pledge");
  129. return 1;
  130. }
  131. unveil(nullptr, nullptr);
  132. int fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
  133. if (fd < 0) {
  134. perror("socket");
  135. return 1;
  136. }
  137. struct timeval timeout {
  138. 5, 0
  139. };
  140. if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) < 0) {
  141. perror("setsockopt");
  142. return 1;
  143. }
  144. int enable = 1;
  145. if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMP, &enable, sizeof(enable)) < 0) {
  146. perror("setsockopt");
  147. return 1;
  148. }
  149. sockaddr_in peer_address;
  150. memset(&peer_address, 0, sizeof(peer_address));
  151. peer_address.sin_family = AF_INET;
  152. peer_address.sin_port = htons(123);
  153. peer_address.sin_addr.s_addr = *(const in_addr_t*)hostent->h_addr_list[0];
  154. NtpPacket packet;
  155. memset(&packet, 0, sizeof(packet));
  156. packet.li_vn_mode = (4 << 3) | 3; // Version 4, client connection.
  157. // The server will copy the transmit_timestamp to origin_timestamp in the reply.
  158. // To not leak the local time, keep the time we sent the packet locally and
  159. // send random bytes to the server.
  160. auto random_transmit_timestamp = get_random<NtpTimestamp>();
  161. timeval local_transmit_time;
  162. gettimeofday(&local_transmit_time, nullptr);
  163. packet.transmit_timestamp = random_transmit_timestamp;
  164. ssize_t rc;
  165. rc = sendto(fd, &packet, sizeof(packet), 0, (const struct sockaddr*)&peer_address, sizeof(peer_address));
  166. if (rc < 0) {
  167. perror("sendto");
  168. return 1;
  169. }
  170. if ((size_t)rc < sizeof(packet)) {
  171. fprintf(stderr, "incomplete packet send\n");
  172. return 1;
  173. }
  174. iovec iov { &packet, sizeof(packet) };
  175. char control_message_buffer[CMSG_SPACE(sizeof(timeval))];
  176. msghdr msg = { &peer_address, sizeof(peer_address), &iov, 1, control_message_buffer, sizeof(control_message_buffer), 0 };
  177. rc = recvmsg(fd, &msg, 0);
  178. if (rc < 0) {
  179. perror("recvmsg");
  180. return 1;
  181. }
  182. timeval userspace_receive_time;
  183. gettimeofday(&userspace_receive_time, nullptr);
  184. if ((size_t)rc < sizeof(packet)) {
  185. fprintf(stderr, "incomplete packet recv\n");
  186. return 1;
  187. }
  188. cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
  189. ASSERT(cmsg->cmsg_level == SOL_SOCKET);
  190. ASSERT(cmsg->cmsg_type == SCM_TIMESTAMP);
  191. ASSERT(!CMSG_NXTHDR(&msg, cmsg));
  192. timeval kernel_receive_time;
  193. memcpy(&kernel_receive_time, CMSG_DATA(cmsg), sizeof(kernel_receive_time));
  194. if (packet.origin_timestamp != random_transmit_timestamp) {
  195. fprintf(stderr, "expected %#016llx as origin timestamp, got %#016llx\n", random_transmit_timestamp, packet.origin_timestamp);
  196. return 1;
  197. }
  198. NtpTimestamp origin_timestamp = ntp_timestamp_from_timeval(local_transmit_time);
  199. NtpTimestamp receive_timestamp = be64toh(packet.receive_timestamp);
  200. NtpTimestamp transmit_timestamp = be64toh(packet.transmit_timestamp);
  201. NtpTimestamp destination_timestamp = ntp_timestamp_from_timeval(kernel_receive_time);
  202. timeval kernel_to_userspace_latency;
  203. timersub(&userspace_receive_time, &kernel_receive_time, &kernel_to_userspace_latency);
  204. if (set_time) {
  205. // FIXME: Do all the time filtering described in 5905, or at least correct for time of flight.
  206. timeval t = timeval_from_ntp_timestamp(transmit_timestamp);
  207. if (settimeofday(&t, nullptr) < 0) {
  208. perror("settimeofday");
  209. return 1;
  210. }
  211. }
  212. if (verbose) {
  213. printf("NTP response from %s:\n", inet_ntoa(peer_address.sin_addr));
  214. printf("Leap Information: %d\n", packet.li_vn_mode >> 6);
  215. printf("Version Number: %d\n", (packet.li_vn_mode >> 3) & 7);
  216. printf("Mode: %d\n", packet.li_vn_mode & 7);
  217. printf("Stratum: %d\n", packet.stratum);
  218. printf("Poll: %d\n", packet.stratum);
  219. printf("Precision: %d\n", packet.precision);
  220. printf("Root delay: %#x\n", ntohl(packet.root_delay));
  221. printf("Root dispersion: %#x\n", ntohl(packet.root_dispersion));
  222. printf("Reference ID: %#x\n", ntohl(packet.reference_id));
  223. printf("Reference timestamp: %#016llx (%s)\n", be64toh(packet.reference_timestamp), format_ntp_timestamp(be64toh(packet.reference_timestamp)).characters());
  224. printf("Origin timestamp: %#016llx (%s)\n", origin_timestamp, format_ntp_timestamp(origin_timestamp).characters());
  225. printf("Receive timestamp: %#016llx (%s)\n", receive_timestamp, format_ntp_timestamp(receive_timestamp).characters());
  226. printf("Transmit timestamp: %#016llx (%s)\n", transmit_timestamp, format_ntp_timestamp(transmit_timestamp).characters());
  227. printf("Destination timestamp: %#016llx (%s)\n", destination_timestamp, format_ntp_timestamp(destination_timestamp).characters());
  228. // 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,
  229. // 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.
  230. printf("Receive latency: %lld.%06d s\n", kernel_to_userspace_latency.tv_sec, kernel_to_userspace_latency.tv_usec);
  231. }
  232. // Parts of the "Clock Filter" computations, https://tools.ietf.org/html/rfc5905#section-10
  233. NtpTimestamp T1 = origin_timestamp;
  234. NtpTimestamp T2 = receive_timestamp;
  235. NtpTimestamp T3 = transmit_timestamp;
  236. NtpTimestamp T4 = destination_timestamp;
  237. auto timestamp_difference_in_seconds = [](NtpTimestamp from, NtpTimestamp to) {
  238. return static_cast<int64_t>(to - from) / pow(2.0, 32);
  239. };
  240. // The network round-trip time of the request.
  241. // T4-T1 is the wall clock roundtrip time, in local ticks.
  242. // T3-T2 is the server side processing time, in server ticks.
  243. double delay_s = timestamp_difference_in_seconds(T1, T4) - timestamp_difference_in_seconds(T2, T3);
  244. // The offset from local time to server time, ignoring network delay.
  245. // Both T2-T1 and T3-T4 estimate this; this takes the average of both.
  246. // Or, equivalently, (T1+T4)/2 estimates local time, (T2+T3)/2 estimate server time, this is the difference.
  247. double offset_s = 0.5 * (timestamp_difference_in_seconds(T1, T2) + timestamp_difference_in_seconds(T4, T3));
  248. if (verbose)
  249. printf("Delay: %f\n", delay_s);
  250. printf("Offset: %f\n", offset_s);
  251. }