ntp: Use mnemonics when checking reply flags
[platform/upstream/connman.git] / src / ntp.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License version 2 as
9  *  published by the Free Software Foundation.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #define _GNU_SOURCE
27 #include <errno.h>
28 #include <fcntl.h>
29 #include <unistd.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <sys/time.h>
33 #include <sys/socket.h>
34 #include <netinet/in.h>
35 #include <netinet/ip.h>
36 #include <arpa/inet.h>
37
38 #include <glib.h>
39
40 #include "connman.h"
41
42 struct ntp_short {
43         uint16_t seconds;
44         uint16_t fraction;
45 } __attribute__ ((packed));
46
47 struct ntp_time {
48         uint32_t seconds;
49         uint32_t fraction;
50 } __attribute__ ((packed));
51
52 struct ntp_msg {
53         uint8_t flags;                  /* Mode, version and leap indicator */
54         uint8_t stratum;                /* Stratum details */
55         int8_t poll;                    /* Maximum interval in log2 seconds */
56         int8_t precision;               /* Clock precision in log2 seconds */
57         struct ntp_short rootdelay;     /* Root delay */
58         struct ntp_short rootdisp;      /* Root dispersion */
59         uint32_t refid;                 /* Reference ID */
60         struct ntp_time reftime;        /* Reference timestamp */
61         struct ntp_time orgtime;        /* Origin timestamp */
62         struct ntp_time rectime;        /* Receive timestamp */
63         struct ntp_time xmttime;        /* Transmit timestamp */
64 } __attribute__ ((packed));
65
66 #define OFFSET_1900_1970  2208988800UL  /* 1970 - 1900 in seconds */
67
68 #define STEPTIME_MIN_OFFSET  0.128
69
70 #define LOGTOD(a)  ((a) < 0 ? 1. / (1L << -(a)) : 1L << (int)(a))
71
72 #define NTP_FLAG_LI_SHIFT      6
73 #define NTP_FLAG_LI_MASK       0x3
74 #define NTP_FLAG_LI_NOWARNING  0x0
75 #define NTP_FLAG_LI_ADDSECOND  0x1
76 #define NTP_FLAG_LI_DELSECOND  0x2
77 #define NTP_FLAG_LI_NOTINSYNC  0x3
78
79 #define NTP_FLAG_VN_SHIFT      3
80 #define NTP_FLAG_VN_MASK       0x7
81
82 #define NTP_FLAG_MD_SHIFT      0
83 #define NTP_FLAG_MD_MASK       0x7
84 #define NTP_FLAG_MD_UNSPEC     0
85 #define NTP_FLAG_MD_ACTIVE     1
86 #define NTP_FLAG_MD_PASSIVE    2
87 #define NTP_FLAG_MD_CLIENT     3
88 #define NTP_FLAG_MD_SERVER     4
89 #define NTP_FLAG_MD_BROADCAST  5
90 #define NTP_FLAG_MD_CONTROL    6
91 #define NTP_FLAG_MD_PRIVATE    7
92
93 #define NTP_FLAGS_ENCODE(li, vn, md)  ((uint8_t)( \
94                       (((li) & NTP_FLAG_LI_MASK) << NTP_FLAG_LI_SHIFT) | \
95                       (((vn) & NTP_FLAG_VN_MASK) << NTP_FLAG_VN_SHIFT) | \
96                       (((md) & NTP_FLAG_MD_MASK) << NTP_FLAG_MD_SHIFT)))
97
98 #define NTP_FLAGS_LI_DECODE(flags)    ((uint8_t)(((flags) >> NTP_FLAG_LI_SHIFT) & NTP_FLAG_LI_MASK))
99 #define NTP_FLAGS_VN_DECODE(flags)    ((uint8_t)(((flags) >> NTP_FLAG_VN_SHIFT) & NTP_FLAG_VN_MASK))
100 #define NTP_FLAGS_MD_DECODE(flags)    ((uint8_t)(((flags) >> NTP_FLAG_MD_SHIFT) & NTP_FLAG_MD_MASK))
101
102 static guint channel_watch = 0;
103 static struct timeval transmit_timeval;
104 static int transmit_fd = 0;
105
106 static char *timeserver = NULL;
107 static gint poll_id = 0;
108 static gint timeout_id = 0;
109
110 static void send_packet(int fd, const char *server)
111 {
112         struct ntp_msg msg;
113         struct sockaddr_in addr;
114         ssize_t len;
115
116         memset(&msg, 0, sizeof(msg));
117         msg.flags = NTP_FLAGS_ENCODE(NTP_FLAG_LI_NOTINSYNC, 4, NTP_FLAG_MD_CLIENT);
118         msg.poll = 4;   // min
119         msg.poll = 10;  // max
120
121         memset(&addr, 0, sizeof(addr));
122         addr.sin_family = AF_INET;
123         addr.sin_port = htons(123);
124         addr.sin_addr.s_addr = inet_addr(server);
125
126         gettimeofday(&transmit_timeval, NULL);
127
128         msg.xmttime.seconds = htonl(transmit_timeval.tv_sec + OFFSET_1900_1970);
129         msg.xmttime.fraction = htonl(transmit_timeval.tv_usec * 1000);
130
131         len = sendto(fd, &msg, sizeof(msg), MSG_DONTWAIT,
132                                                 &addr, sizeof(addr));
133         if (len < 0) {
134                 connman_error("Time request for server %s failed (%d/%s)",
135                         server, errno, strerror(errno));
136
137                 if (errno == ENETUNREACH)
138                         __connman_timeserver_sync_next();
139
140                 return;
141         }
142
143         if (len != sizeof(msg)) {
144                 connman_error("Broken time request for server %s", server);
145                 return;
146         }
147 }
148
149 static gboolean next_server(gpointer user_data)
150 {
151         if (timeserver != NULL) {
152                 g_free(timeserver);
153                 timeserver = NULL;
154         }
155
156         __connman_timeserver_sync_next();
157
158         return FALSE;
159 }
160
161 static gboolean next_poll(gpointer user_data)
162 {
163         if (timeserver == NULL || transmit_fd == 0)
164                 return FALSE;
165
166         send_packet(transmit_fd, timeserver);
167
168         return FALSE;
169 }
170
171 static void decode_msg(void *base, size_t len, struct timeval *tv)
172 {
173         struct ntp_msg *msg = base;
174         double org, rec, xmt, dst;
175         double delay, offset;
176         static guint transmit_delay;
177
178         if (len < sizeof(*msg)) {
179                 connman_error("Invalid response from time server");
180                 return;
181         }
182
183         if (tv == NULL) {
184                 connman_error("Invalid packet timestamp from time server");
185                 return;
186         }
187
188         DBG("flags      : 0x%02x", msg->flags);
189         DBG("stratum    : %u", msg->stratum);
190         DBG("poll       : %f seconds (%d)",
191                                 LOGTOD(msg->poll), msg->poll);
192         DBG("precision  : %f seconds (%d)",
193                                 LOGTOD(msg->precision), msg->precision);
194         DBG("root delay : %u seconds (fraction %u)",
195                         msg->rootdelay.seconds, msg->rootdelay.fraction);
196         DBG("root disp. : %u seconds (fraction %u)",
197                         msg->rootdisp.seconds, msg->rootdisp.fraction);
198         DBG("reference  : 0x%04x", msg->refid);
199
200         transmit_delay = LOGTOD(msg->poll);
201
202         if (NTP_FLAGS_LI_DECODE(msg->flags) == NTP_FLAG_LI_NOTINSYNC) {
203                 DBG("ignoring unsynchronized peer");
204                 return;
205         }
206
207         if (NTP_FLAGS_VN_DECODE(msg->flags) != 4) {
208                 DBG("unsupported version %d", NTP_FLAGS_VN_DECODE(msg->flags));
209                 return;
210         }
211
212         if (NTP_FLAGS_MD_DECODE(msg->flags) != NTP_FLAG_MD_SERVER) {
213                 DBG("unsupported mode %d", NTP_FLAGS_MD_DECODE(msg->flags));
214                 return;
215         }
216
217         org = transmit_timeval.tv_sec +
218                         (1.0e-6 * transmit_timeval.tv_usec) + OFFSET_1900_1970;
219         rec = ntohl(msg->rectime.seconds) +
220                         ((double) ntohl(msg->rectime.fraction) / UINT_MAX);
221         xmt = ntohl(msg->xmttime.seconds) +
222                         ((double) ntohl(msg->xmttime.fraction) / UINT_MAX);
223         dst = tv->tv_sec + (1.0e-6 * tv->tv_usec) + OFFSET_1900_1970;
224
225         DBG("org=%f rec=%f xmt=%f dst=%f", org, rec, xmt, dst);
226
227         offset = ((rec - org) + (xmt - dst)) / 2;
228         delay = (dst - org) - (xmt - rec);
229
230         DBG("offset=%f delay=%f", offset, delay);
231
232         /* Remove the timeout, as timeserver has responded */
233         if (timeout_id > 0)
234                 g_source_remove(timeout_id);
235
236         /*
237          * Now poll the server every transmit_delay seconds
238          * for time correction.
239          */
240         if (poll_id > 0)
241                 g_source_remove(poll_id);
242
243         DBG("Timeserver %s, next sync in %d seconds", timeserver, transmit_delay);
244
245         poll_id = g_timeout_add_seconds(transmit_delay, next_poll, NULL);
246
247         connman_info("ntp: time slew %+.6f s", offset);
248
249         if (offset < STEPTIME_MIN_OFFSET && offset > -STEPTIME_MIN_OFFSET) {
250                 struct timeval adj;
251
252                 adj.tv_sec = (long) offset;
253                 adj.tv_usec = (offset - adj.tv_sec) * 1000000;
254
255                 DBG("adjusting time");
256
257                 if (adjtime(&adj, &adj) < 0) {
258                         connman_error("Failed to adjust time");
259                         return;
260                 }
261
262                 DBG("%lu seconds, %lu msecs", adj.tv_sec, adj.tv_usec);
263         } else {
264                 struct timeval cur;
265                 double dtime;
266
267                 gettimeofday(&cur, NULL);
268                 dtime = offset + cur.tv_sec + 1.0e-6 * cur.tv_usec;
269                 cur.tv_sec = (long) dtime;
270                 cur.tv_usec = (dtime - cur.tv_sec) * 1000000;
271
272                 DBG("setting time");
273
274                 if (settimeofday(&cur, NULL) < 0) {
275                         connman_error("Failed to set time");
276                         return;
277                 }
278
279                 DBG("%lu seconds, %lu msecs", cur.tv_sec, cur.tv_usec);
280         }
281 }
282
283 static gboolean received_data(GIOChannel *channel, GIOCondition condition,
284                                                         gpointer user_data)
285 {
286         unsigned char buf[128];
287         struct msghdr msg;
288         struct iovec iov;
289         struct cmsghdr *cmsg;
290         struct timeval *tv;
291         char aux[128];
292         ssize_t len;
293         int fd;
294
295         if (condition & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) {
296                 connman_error("Problem with timer server channel");
297                 channel_watch = 0;
298                 return FALSE;
299         }
300
301         fd = g_io_channel_unix_get_fd(channel);
302
303         iov.iov_base = buf;
304         iov.iov_len = sizeof(buf);
305
306         memset(&msg, 0, sizeof(msg));
307         msg.msg_iov = &iov;
308         msg.msg_iovlen = 1;
309         msg.msg_control = aux;
310         msg.msg_controllen = sizeof(aux);
311
312         len = recvmsg(fd, &msg, MSG_DONTWAIT);
313         if (len < 0)
314                 return TRUE;
315
316         tv = NULL;
317
318         for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
319                 if (cmsg->cmsg_level != SOL_SOCKET)
320                         continue;
321
322                 switch (cmsg->cmsg_type) {
323                 case SCM_TIMESTAMP:
324                         tv = (struct timeval *) CMSG_DATA(cmsg);
325                         break;
326                 }
327         }
328
329         decode_msg(iov.iov_base, iov.iov_len, tv);
330
331         return TRUE;
332 }
333
334 static void start_ntp(char *server)
335 {
336         GIOChannel *channel;
337         struct sockaddr_in addr;
338         int tos = IPTOS_LOWDELAY, timestamp = 1;
339
340         if (server == NULL)
341                 return;
342
343         DBG("server %s", server);
344
345         if (channel_watch > 0)
346                 goto send;
347
348         transmit_fd = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
349         if (transmit_fd < 0) {
350                 connman_error("Failed to open time server socket");
351                 return;
352         }
353
354         memset(&addr, 0, sizeof(addr));
355         addr.sin_family = AF_INET;
356
357         if (bind(transmit_fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
358                 connman_error("Failed to bind time server socket");
359                 close(transmit_fd);
360                 return;
361         }
362
363         if (setsockopt(transmit_fd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) < 0) {
364                 connman_error("Failed to set type of service option");
365                 close(transmit_fd);
366                 return;
367         }
368
369         if (setsockopt(transmit_fd, SOL_SOCKET, SO_TIMESTAMP, &timestamp,
370                                                 sizeof(timestamp)) < 0) {
371                 connman_error("Failed to enable timestamp support");
372                 close(transmit_fd);
373                 return;
374         }
375
376         channel = g_io_channel_unix_new(transmit_fd);
377         if (channel == NULL) {
378                 close(transmit_fd);
379                 return;
380         }
381
382         g_io_channel_set_encoding(channel, NULL, NULL);
383         g_io_channel_set_buffered(channel, FALSE);
384
385         g_io_channel_set_close_on_unref(channel, TRUE);
386
387         channel_watch = g_io_add_watch_full(channel, G_PRIORITY_DEFAULT,
388                                 G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
389                                 received_data, NULL, NULL);
390
391         g_io_channel_unref(channel);
392
393 send:
394         send_packet(transmit_fd, server);
395 }
396
397 int __connman_ntp_start(char *server)
398 {
399         DBG("%s", server);
400
401         if (server == NULL)
402                 return -EINVAL;
403
404         if (timeserver != NULL)
405                 g_free(timeserver);
406
407         timeserver = g_strdup(server);
408
409         start_ntp(timeserver);
410
411         /*
412          * Add a fallback timeout , preferably short, 5 sec here,
413          * to fallback on the next server.
414          */
415
416         timeout_id = g_timeout_add_seconds(5, next_server, NULL);
417
418         return 0;
419 }
420
421 void __connman_ntp_stop()
422 {
423         DBG("");
424
425         if (poll_id > 0)
426                 g_source_remove(poll_id);
427
428         if (timeout_id > 0)
429                 g_source_remove(timeout_id);
430
431         if (channel_watch > 0) {
432                 g_source_remove(channel_watch);
433                 channel_watch = 0;
434                 transmit_fd = 0;
435         }
436
437         if (timeserver != NULL) {
438                 g_free(timeserver);
439                 timeserver = NULL;
440         }
441 }