Serenity Operating System
at master 324 lines 13 kB view raw
1/* 2 * Copyright (c) 2020, Nico Weber <thakis@chromium.org> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#define _BSD_SOURCE 8#define _DEFAULT_SOURCE 9#include <AK/Assertions.h> 10#include <AK/Endian.h> 11#include <AK/Random.h> 12#include <LibCore/ArgsParser.h> 13#include <LibCore/System.h> 14#include <LibMain/Main.h> 15#include <arpa/inet.h> 16#include <inttypes.h> 17#include <math.h> 18#include <netdb.h> 19#include <netinet/in.h> 20#include <stdio.h> 21#include <string.h> 22#include <sys/socket.h> 23#include <sys/time.h> 24#include <sys/uio.h> 25#include <time.h> 26 27// An NtpTimestamp is a 64-bit integer that's a 32.32 binary-fixed point number. 28// The integral part in the upper 32 bits represents seconds since 1900-01-01. 29// The fractional part in the lower 32 bits stores fractional bits times 2 ** 32. 30using NtpTimestamp = uint64_t; 31 32struct [[gnu::packed]] NtpPacket { 33 uint8_t li_vn_mode; 34 uint8_t stratum; 35 int8_t poll; 36 int8_t precision; 37 38 uint32_t root_delay; 39 uint32_t root_dispersion; 40 uint32_t reference_id; 41 42 NtpTimestamp reference_timestamp; 43 NtpTimestamp origin_timestamp; 44 NtpTimestamp receive_timestamp; 45 NtpTimestamp transmit_timestamp; 46 47 uint8_t leap_information() const { return li_vn_mode >> 6; } 48 uint8_t version_number() const { return (li_vn_mode >> 3) & 7; } 49 uint8_t mode() const { return li_vn_mode & 7; } 50}; 51static_assert(AssertSize<NtpPacket, 48>()); 52 53// NTP measures time in seconds since 1900-01-01, POSIX in seconds since 1970-01-01. 54// 1900 wasn't a leap year, so there are 70/4 leap years between 1900 and 1970. 55// Overflows a 32-bit signed int, but not a 32-bit unsigned int. 56unsigned const SecondsFrom1900To1970 = (70u * 365u + 70u / 4u) * 24u * 60u * 60u; 57 58static NtpTimestamp ntp_timestamp_from_timeval(timeval const& t) 59{ 60 VERIFY(t.tv_usec >= 0 && t.tv_usec < 1'000'000); // Fits in 20 bits when normalized. 61 62 // Seconds just need translation to the different origin. 63 uint32_t seconds = t.tv_sec + SecondsFrom1900To1970; 64 65 // Fractional bits are decimal fixed point (*1'000'000) in timeval, but binary fixed-point (* 2**32) in NTP timestamps. 66 uint32_t fractional_bits = static_cast<uint32_t>((static_cast<uint64_t>(t.tv_usec) << 32) / 1'000'000); 67 68 return (static_cast<NtpTimestamp>(seconds) << 32) | fractional_bits; 69} 70 71static timeval timeval_from_ntp_timestamp(NtpTimestamp const& 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 79static DeprecatedString format_ntp_timestamp(NtpTimestamp ntp_timestamp) 80{ 81 char buffer[28]; // YYYY-MM-DDTHH:MM:SS.UUUUUUZ is 27 characters long. 82 timeval t = timeval_from_ntp_timestamp(ntp_timestamp); 83 struct tm tm; 84 gmtime_r(&t.tv_sec, &tm); 85 size_t written = strftime(buffer, sizeof(buffer), "%Y-%m-%dT%T.", &tm); 86 VERIFY(written == 20); 87 written += snprintf(buffer + written, sizeof(buffer) - written, "%06d", (int)t.tv_usec); 88 VERIFY(written == 26); 89 buffer[written++] = 'Z'; 90 buffer[written] = '\0'; 91 return buffer; 92} 93ErrorOr<int> serenity_main(Main::Arguments arguments) 94{ 95 TRY(Core::System::pledge("stdio inet unix settime wpath rpath")); 96 97 bool adjust_time = false; 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 DeprecatedString host = "time.google.com"sv; 111 Core::ArgsParser args_parser; 112 args_parser.add_option(adjust_time, "Gradually adjust system time (requires root)", "adjust", 'a'); 113 args_parser.add_option(set_time, "Immediately set system time (requires root)", "set", 's'); 114 args_parser.add_option(verbose, "Verbose output", "verbose", 'v'); 115 args_parser.add_positional_argument(host, "NTP server", "host", Core::ArgsParser::Required::No); 116 args_parser.parse(arguments); 117 118 TRY(Core::System::unveil("/tmp/portal/lookup", "rw")); 119 TRY(Core::System::unveil("/etc/timezone", "r")); 120 TRY(Core::System::unveil(nullptr, nullptr)); 121 122 if (adjust_time && set_time) { 123 warnln("-a and -s are mutually exclusive"); 124 return 1; 125 } 126 127 if (!adjust_time && !set_time) { 128 TRY(Core::System::pledge("stdio inet unix rpath")); 129 } 130 131 auto* hostent = gethostbyname(host.characters()); 132 if (!hostent) { 133 warnln("Lookup failed for '{}'", host); 134 return 1; 135 } 136 137 TRY(Core::System::pledge((adjust_time || set_time) ? "stdio inet settime wpath rpath"sv : "stdio inet rpath"sv)); 138 139 int fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); 140 if (fd < 0) { 141 perror("socket"); 142 return 1; 143 } 144 145 struct timeval timeout { 146 5, 0 147 }; 148 if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) < 0) { 149 perror("setsockopt"); 150 return 1; 151 } 152 153 int enable = 1; 154 if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMP, &enable, sizeof(enable)) < 0) { 155 perror("setsockopt"); 156 return 1; 157 } 158 159 sockaddr_in peer_address; 160 memset(&peer_address, 0, sizeof(peer_address)); 161 peer_address.sin_family = AF_INET; 162 peer_address.sin_port = htons(123); 163 peer_address.sin_addr.s_addr = *(in_addr_t const*)hostent->h_addr_list[0]; 164 165 NtpPacket packet; 166 memset(&packet, 0, sizeof(packet)); 167 packet.li_vn_mode = (4 << 3) | 3; // Version 4, client connection. 168 169 // The server will copy the transmit_timestamp to origin_timestamp in the reply. 170 // To not leak the local time, keep the time we sent the packet locally and 171 // send random bytes to the server. 172 auto random_transmit_timestamp = get_random<NtpTimestamp>(); 173 timeval local_transmit_time; 174 gettimeofday(&local_transmit_time, nullptr); 175 packet.transmit_timestamp = random_transmit_timestamp; 176 177 ssize_t rc; 178 rc = sendto(fd, &packet, sizeof(packet), 0, (const struct sockaddr*)&peer_address, sizeof(peer_address)); 179 if (rc < 0) { 180 perror("sendto"); 181 return 1; 182 } 183 if ((size_t)rc < sizeof(packet)) { 184 warnln("incomplete packet send"); 185 return 1; 186 } 187 188 iovec iov { &packet, sizeof(packet) }; 189 char control_message_buffer[CMSG_SPACE(sizeof(timeval))]; 190 msghdr msg = {}; 191 msg.msg_name = &peer_address; 192 msg.msg_namelen = sizeof(peer_address); 193 msg.msg_iov = &iov; 194 msg.msg_iovlen = 1; 195 msg.msg_control = control_message_buffer; 196 msg.msg_controllen = sizeof(control_message_buffer); 197 msg.msg_flags = 0; 198 199 rc = recvmsg(fd, &msg, 0); 200 if (rc < 0) { 201 perror("recvmsg"); 202 return 1; 203 } 204 timeval userspace_receive_time; 205 gettimeofday(&userspace_receive_time, nullptr); 206 if ((size_t)rc < sizeof(packet)) { 207 warnln("incomplete packet recv"); 208 return 1; 209 } 210 211 cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); 212 VERIFY(cmsg->cmsg_level == SOL_SOCKET); 213 VERIFY(cmsg->cmsg_type == SCM_TIMESTAMP); 214 VERIFY(!CMSG_NXTHDR(&msg, cmsg)); 215 timeval kernel_receive_time; 216 memcpy(&kernel_receive_time, CMSG_DATA(cmsg), sizeof(kernel_receive_time)); 217 218 // Checks 3 and 4 from end of section 5 of rfc4330. 219 if (packet.version_number() != 3 && packet.version_number() != 4) { 220 warnln("unexpected version number {}", packet.version_number()); 221 return 1; 222 } 223 if (packet.mode() != 4) { // 4 means "server", which should be the reply to our 3 ("client") request. 224 warnln("unexpected mode {}", packet.mode()); 225 return 1; 226 } 227 if (packet.stratum == 0 || packet.stratum >= 16) { 228 warnln("unexpected stratum value {}", packet.stratum); 229 return 1; 230 } 231 if (packet.origin_timestamp != random_transmit_timestamp) { 232 warnln("expected {:#016x} as origin timestamp, got {:#016x}", random_transmit_timestamp, packet.origin_timestamp); 233 return 1; 234 } 235 if (packet.transmit_timestamp == 0) { 236 warnln("got transmit_timestamp 0"); 237 return 1; 238 } 239 240 NtpTimestamp origin_timestamp = ntp_timestamp_from_timeval(local_transmit_time); 241 NtpTimestamp receive_timestamp = be64toh(packet.receive_timestamp); 242 NtpTimestamp transmit_timestamp = be64toh(packet.transmit_timestamp); 243 NtpTimestamp destination_timestamp = ntp_timestamp_from_timeval(kernel_receive_time); 244 245 timeval kernel_to_userspace_latency; 246 timersub(&userspace_receive_time, &kernel_receive_time, &kernel_to_userspace_latency); 247 248 if (set_time) { 249 // FIXME: Do all the time filtering described in 5905, or at least correct for time of flight. 250 timeval t = timeval_from_ntp_timestamp(transmit_timestamp); 251 if (settimeofday(&t, nullptr) < 0) { 252 perror("settimeofday"); 253 return 1; 254 } 255 } 256 257 if (verbose) { 258 outln("NTP response from {}:", inet_ntoa(peer_address.sin_addr)); 259 outln("Leap Information: {}", packet.leap_information()); 260 outln("Version Number: {}", packet.version_number()); 261 outln("Mode: {}", packet.mode()); 262 outln("Stratum: {}", packet.stratum); 263 outln("Poll: {}", packet.stratum); 264 outln("Precision: {}", packet.precision); 265 outln("Root delay: {:x}", ntohl(packet.root_delay)); 266 outln("Root dispersion: {:x}", ntohl(packet.root_dispersion)); 267 268 u32 ref_id = ntohl(packet.reference_id); 269 out("Reference ID: {:x}", ref_id); 270 if (packet.stratum == 1) { 271 out(" ('{:c}{:c}{:c}{:c}')", (ref_id & 0xff000000) >> 24, (ref_id & 0xff0000) >> 16, (ref_id & 0xff00) >> 8, ref_id & 0xff); 272 } 273 outln(); 274 275 outln("Reference timestamp: {:#016x} ({})", be64toh(packet.reference_timestamp), format_ntp_timestamp(be64toh(packet.reference_timestamp)).characters()); 276 outln("Origin timestamp: {:#016x} ({})", origin_timestamp, format_ntp_timestamp(origin_timestamp).characters()); 277 outln("Receive timestamp: {:#016x} ({})", receive_timestamp, format_ntp_timestamp(receive_timestamp).characters()); 278 outln("Transmit timestamp: {:#016x} ({})", transmit_timestamp, format_ntp_timestamp(transmit_timestamp).characters()); 279 outln("Destination timestamp: {:#016x} ({})", destination_timestamp, format_ntp_timestamp(destination_timestamp).characters()); 280 281 // 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, 282 // 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. 283 outln("Receive latency: {}.{:06} s", (i64)kernel_to_userspace_latency.tv_sec, (int)kernel_to_userspace_latency.tv_usec); 284 } 285 286 // Parts of the "Clock Filter" computations, https://tools.ietf.org/html/rfc5905#section-10 287 NtpTimestamp T1 = origin_timestamp; 288 NtpTimestamp T2 = receive_timestamp; 289 NtpTimestamp T3 = transmit_timestamp; 290 NtpTimestamp T4 = destination_timestamp; 291 auto timestamp_difference_in_seconds = [](NtpTimestamp from, NtpTimestamp to) { 292 return static_cast<i64>(to - from) >> 32; 293 }; 294 295 // The network round-trip time of the request. 296 // T4-T1 is the wall clock roundtrip time, in local ticks. 297 // T3-T2 is the server side processing time, in server ticks. 298 double delay_s = timestamp_difference_in_seconds(T1, T4) - timestamp_difference_in_seconds(T2, T3); 299 300 // The offset from local time to server time, ignoring network delay. 301 // Both T2-T1 and T3-T4 estimate this; this takes the average of both. 302 // Or, equivalently, (T1+T4)/2 estimates local time, (T2+T3)/2 estimate server time, this is the difference. 303 double offset_s = 0.5 * (timestamp_difference_in_seconds(T1, T2) + timestamp_difference_in_seconds(T4, T3)); 304 if (verbose) 305 outln("Delay: {}", delay_s); 306 outln("Offset: {}", offset_s); 307 308 if (adjust_time) { 309 long delta_us = static_cast<long>(round(offset_s * 1'000'000)); 310 timeval delta_timeval; 311 delta_timeval.tv_sec = delta_us / 1'000'000; 312 delta_timeval.tv_usec = delta_us % 1'000'000; 313 if (delta_timeval.tv_usec < 0) { 314 delta_timeval.tv_sec--; 315 delta_timeval.tv_usec += 1'000'000; 316 } 317 if (adjtime(&delta_timeval, nullptr) < 0) { 318 perror("adjtime set"); 319 return 1; 320 } 321 } 322 323 return 0; 324}