dnsproxy: Only one copy of the relevant buffers will be made to a TCP request
[framework/connectivity/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 static guint channel_watch = 0;
73 static struct timeval transmit_timeval;
74 static int transmit_fd = 0;
75
76 static char *timeserver = NULL;
77 static gint poll_id = 0;
78 static gint timeout_id = 0;
79
80 static void send_packet(int fd, const char *server)
81 {
82         struct ntp_msg msg;
83         struct sockaddr_in addr;
84         ssize_t len;
85
86         memset(&msg, 0, sizeof(msg));
87         msg.flags = 0x23;
88         msg.poll = 4;   // min
89         msg.poll = 10;  // max
90         msg.xmttime.seconds = random();
91         msg.xmttime.fraction = random();
92
93         memset(&addr, 0, sizeof(addr));
94         addr.sin_family = AF_INET;
95         addr.sin_port = htons(123);
96         addr.sin_addr.s_addr = inet_addr(server);
97
98         gettimeofday(&transmit_timeval, NULL);
99
100         len = sendto(fd, &msg, sizeof(msg), MSG_DONTWAIT,
101                                                 &addr, sizeof(addr));
102         if (len < 0) {
103                 connman_error("Time request for server %s failed", server);
104                 return;
105         }
106
107         if (len != sizeof(msg)) {
108                 connman_error("Broken time request for server %s", server);
109                 return;
110         }
111 }
112
113 static gboolean next_server(gpointer user_data)
114 {
115         if (timeserver != NULL) {
116                 g_free(timeserver);
117                 timeserver = NULL;
118         }
119
120         __connman_timeserver_sync_next();
121
122         return FALSE;
123 }
124
125 static gboolean next_poll(gpointer user_data)
126 {
127         if (timeserver == NULL || transmit_fd == 0)
128                 return FALSE;
129
130         send_packet(transmit_fd, timeserver);
131
132         return FALSE;
133 }
134
135 static void decode_msg(void *base, size_t len, struct timeval *tv)
136 {
137         struct ntp_msg *msg = base;
138         double org, rec, xmt, dst;
139         double delay, offset;
140         static guint transmit_delay;
141
142         if (len < sizeof(*msg)) {
143                 connman_error("Invalid response from time server");
144                 return;
145         }
146
147         if (tv == NULL) {
148                 connman_error("Invalid packet timestamp from time server");
149                 return;
150         }
151
152         DBG("flags      : 0x%02x", msg->flags);
153         DBG("stratum    : %u", msg->stratum);
154         DBG("poll       : %f seconds (%d)",
155                                 LOGTOD(msg->poll), msg->poll);
156         DBG("precision  : %f seconds (%d)",
157                                 LOGTOD(msg->precision), msg->precision);
158         DBG("root delay : %u seconds (fraction %u)",
159                         msg->rootdelay.seconds, msg->rootdelay.fraction);
160         DBG("root disp. : %u seconds (fraction %u)",
161                         msg->rootdisp.seconds, msg->rootdisp.fraction);
162         DBG("reference  : 0x%04x", msg->refid);
163
164         transmit_delay = LOGTOD(msg->poll);
165
166         if (msg->flags != 0x24)
167                 return;
168
169         org = transmit_timeval.tv_sec +
170                         (1.0e-6 * transmit_timeval.tv_usec) + OFFSET_1900_1970;
171         rec = ntohl(msg->rectime.seconds) +
172                         ((double) ntohl(msg->rectime.fraction) / UINT_MAX);
173         xmt = ntohl(msg->xmttime.seconds) +
174                         ((double) ntohl(msg->xmttime.fraction) / UINT_MAX);
175         dst = tv->tv_sec + (1.0e-6 * tv->tv_usec) + OFFSET_1900_1970;
176
177         DBG("org=%f rec=%f xmt=%f dst=%f", org, rec, xmt, dst);
178
179         offset = ((rec - org) + (xmt - dst)) / 2;
180         delay = (dst - org) - (xmt - rec);
181
182         DBG("offset=%f delay=%f", offset, delay);
183
184         /* Remove the timeout, as timeserver has responded */
185         if (timeout_id > 0)
186                 g_source_remove(timeout_id);
187
188         /*
189          * Now poll the server every transmit_delay seconds
190          * for time correction.
191          */
192         if (poll_id > 0)
193                 g_source_remove(poll_id);
194
195         DBG("Timeserver %s, next sync in %d seconds", timeserver, transmit_delay);
196
197         poll_id = g_timeout_add_seconds(transmit_delay, next_poll, NULL);
198
199         connman_info("ntp: time slew %+.6f s", offset);
200
201         if (offset < STEPTIME_MIN_OFFSET && offset > -STEPTIME_MIN_OFFSET) {
202                 struct timeval adj;
203
204                 adj.tv_sec = (long) offset;
205                 adj.tv_usec = (offset - adj.tv_sec) * 1000000;
206
207                 DBG("adjusting time");
208
209                 if (adjtime(&adj, &adj) < 0) {
210                         connman_error("Failed to adjust time");
211                         return;
212                 }
213
214                 DBG("%lu seconds, %lu msecs", adj.tv_sec, adj.tv_usec);
215         } else {
216                 struct timeval cur;
217                 double dtime;
218
219                 gettimeofday(&cur, NULL);
220                 dtime = offset + cur.tv_sec + 1.0e-6 * cur.tv_usec;
221                 cur.tv_sec = (long) dtime;
222                 cur.tv_usec = (dtime - cur.tv_sec) * 1000000;
223
224                 DBG("setting time");
225
226                 if (settimeofday(&cur, NULL) < 0) {
227                         connman_error("Failed to set time");
228                         return;
229                 }
230
231                 DBG("%lu seconds, %lu msecs", cur.tv_sec, cur.tv_usec);
232         }
233 }
234
235 static gboolean received_data(GIOChannel *channel, GIOCondition condition,
236                                                         gpointer user_data)
237 {
238         unsigned char buf[128];
239         struct msghdr msg;
240         struct iovec iov;
241         struct cmsghdr *cmsg;
242         struct timeval *tv;
243         char aux[128];
244         ssize_t len;
245         int fd;
246
247         if (condition & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) {
248                 connman_error("Problem with timer server channel");
249                 channel_watch = 0;
250                 return FALSE;
251         }
252
253         fd = g_io_channel_unix_get_fd(channel);
254
255         iov.iov_base = buf;
256         iov.iov_len = sizeof(buf);
257
258         memset(&msg, 0, sizeof(msg));
259         msg.msg_iov = &iov;
260         msg.msg_iovlen = 1;
261         msg.msg_control = aux;
262         msg.msg_controllen = sizeof(aux);
263
264         len = recvmsg(fd, &msg, MSG_DONTWAIT);
265         if (len < 0)
266                 return TRUE;
267
268         tv = NULL;
269
270         for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
271                 if (cmsg->cmsg_level != SOL_SOCKET)
272                         continue;
273
274                 switch (cmsg->cmsg_type) {
275                 case SCM_TIMESTAMP:
276                         tv = (struct timeval *) CMSG_DATA(cmsg);
277                         break;
278                 }
279         }
280
281         decode_msg(iov.iov_base, iov.iov_len, tv);
282
283         return TRUE;
284 }
285
286 static void start_ntp(char *server)
287 {
288         GIOChannel *channel;
289         struct sockaddr_in addr;
290         int tos = IPTOS_LOWDELAY, timestamp = 1;
291
292         if (server == NULL)
293                 return;
294
295         DBG("server %s", server);
296
297         if (channel_watch > 0)
298                 goto send;
299
300         transmit_fd = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
301         if (transmit_fd < 0) {
302                 connman_error("Failed to open time server socket");
303                 return;
304         }
305
306         memset(&addr, 0, sizeof(addr));
307         addr.sin_family = AF_INET;
308
309         if (bind(transmit_fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
310                 connman_error("Failed to bind time server socket");
311                 close(transmit_fd);
312                 return;
313         }
314
315         if (setsockopt(transmit_fd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) < 0) {
316                 connman_error("Failed to set type of service option");
317                 close(transmit_fd);
318                 return;
319         }
320
321         if (setsockopt(transmit_fd, SOL_SOCKET, SO_TIMESTAMP, &timestamp,
322                                                 sizeof(timestamp)) < 0) {
323                 connman_error("Failed to enable timestamp support");
324                 close(transmit_fd);
325                 return;
326         }
327
328         channel = g_io_channel_unix_new(transmit_fd);
329         if (channel == NULL) {
330                 close(transmit_fd);
331                 return;
332         }
333
334         g_io_channel_set_encoding(channel, NULL, NULL);
335         g_io_channel_set_buffered(channel, FALSE);
336
337         g_io_channel_set_close_on_unref(channel, TRUE);
338
339         channel_watch = g_io_add_watch_full(channel, G_PRIORITY_DEFAULT,
340                                 G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
341                                 received_data, NULL, NULL);
342
343         g_io_channel_unref(channel);
344
345 send:
346         send_packet(transmit_fd, server);
347 }
348
349 int __connman_ntp_start(char *server)
350 {
351         DBG("%s", server);
352
353         if (server == NULL)
354                 return -EINVAL;
355
356         if (timeserver != NULL)
357                 g_free(timeserver);
358
359         timeserver = g_strdup(server);
360
361         start_ntp(timeserver);
362
363         /*
364          * Add a fallback timeout , preferably short, 5 sec here,
365          * to fallback on the next server.
366          */
367
368         timeout_id = g_timeout_add_seconds(5, next_server, NULL);
369
370         return 0;
371 }
372
373 void __connman_ntp_stop()
374 {
375         DBG("");
376
377         if (poll_id > 0)
378                 g_source_remove(poll_id);
379
380         if (timeout_id > 0)
381                 g_source_remove(timeout_id);
382
383         if (channel_watch > 0) {
384                 g_source_remove(channel_watch);
385                 channel_watch = 0;
386                 transmit_fd = 0;
387         }
388
389         if (timeserver != NULL) {
390                 g_free(timeserver);
391                 timeserver = NULL;
392         }
393 }