ntp: Add skeleton for Simple NTP support
[framework/connectivity/connman.git] / src / ntp.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2010  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 <gweb/gresolv.h>
41
42 #include "connman.h"
43
44 struct ntp_short {
45         uint16_t seconds;
46         uint16_t fraction;
47 } __attribute__ ((packed));
48
49 struct ntp_time {
50         uint32_t seconds;
51         uint32_t fraction;
52 } __attribute__ ((packed));
53
54 struct ntp_msg {
55         uint8_t flags;                  /* Mode, version and leap indicator */
56         uint8_t stratum;                /* Stratum details */
57         int8_t poll;                    /* Maximum interval in log2 seconds */
58         int8_t precision;               /* Clock precision in log2 seconds */
59         struct ntp_short rootdelay;     /* Root delay */
60         struct ntp_short rootdisp;      /* Root dispersion */
61         uint32_t refid;                 /* Reference ID */
62         struct ntp_time reftime;        /* Reference timestamp */
63         struct ntp_time orgtime;        /* Origin timestamp */
64         struct ntp_time rectime;        /* Receive timestamp */
65         struct ntp_time xmttime;        /* Transmit timestamp */
66 } __attribute__ ((packed));
67
68 #define OFFSET_1900_1970  2208988800UL  /* 1970 - 1900 in seconds */
69
70 #define STEPTIME_MIN_OFFSET  0.128
71
72 #define LOGTOD(a)  ((a) < 0 ? 1. / (1L << -(a)) : 1L << (int)(a))
73
74 static struct timeval transmit_timeval;
75 static char *transmit_server;
76 static int transmit_fd;
77 static guint transmit_delay = 16;
78
79 static void send_packet(int fd, const char *server)
80 {
81         struct ntp_msg msg;
82         struct sockaddr_in addr;
83         ssize_t len;
84
85         memset(&msg, 0, sizeof(msg));
86         msg.flags = 0x23;
87         msg.poll = 4;   // min
88         msg.poll = 10;  // max
89         msg.xmttime.seconds = random();
90         msg.xmttime.fraction = random();
91
92         memset(&addr, 0, sizeof(addr));
93         addr.sin_family = AF_INET;
94         addr.sin_port = htons(123);
95         addr.sin_addr.s_addr = inet_addr(server);
96
97         gettimeofday(&transmit_timeval, NULL);
98
99         len = sendto(fd, &msg, sizeof(msg), MSG_DONTWAIT,
100                                                 &addr, sizeof(addr));
101         if (len < 0) {
102                 connman_error("Time request for server %s failed", server);
103                 return;
104         }
105
106         if (len != sizeof(msg)) {
107                 connman_error("Broken time request for server %s", server);
108                 return;
109         }
110 }
111
112 static gboolean next_request(gpointer user_data)
113 {
114         DBG("server %s", transmit_server);
115
116         send_packet(transmit_fd, transmit_server);
117
118         return FALSE;
119 }
120
121 static void decode_msg(void *base, size_t len, struct timeval *tv)
122 {
123         struct ntp_msg *msg = base;
124         double org, rec, xmt, dst;
125         double delay, offset;
126
127         if (len < sizeof(*msg)) {
128                 connman_error("Invalid response from time server");
129                 return;
130         }
131
132         if (tv == NULL) {
133                 connman_error("Invalid packet timestamp from time server");
134                 return;
135         }
136
137         DBG("flags      : 0x%02x", msg->flags);
138         DBG("stratum    : %u", msg->stratum);
139         DBG("poll       : %f seconds (%d)",
140                                 LOGTOD(msg->poll), msg->poll);
141         DBG("precision  : %f seconds (%d)",
142                                 LOGTOD(msg->precision), msg->precision);
143         DBG("root delay : %u seconds (fraction %u)",
144                         msg->rootdelay.seconds, msg->rootdelay.fraction);
145         DBG("root disp. : %u seconds (fraction %u)",
146                         msg->rootdisp.seconds, msg->rootdisp.fraction);
147         DBG("reference  : 0x%04x", msg->refid);
148
149         transmit_delay = LOGTOD(msg->poll);
150
151         if (msg->flags != 0x24)
152                 return;
153
154         org = transmit_timeval.tv_sec +
155                         (1.0e-6 * transmit_timeval.tv_usec) + OFFSET_1900_1970;
156         rec = ntohl(msg->rectime.seconds) +
157                         ((double) ntohl(msg->rectime.fraction) / UINT_MAX);
158         xmt = ntohl(msg->xmttime.seconds) +
159                         ((double) ntohl(msg->xmttime.fraction) / UINT_MAX);
160         dst = tv->tv_sec + (1.0e-6 * tv->tv_usec) + OFFSET_1900_1970;
161
162         DBG("org=%f rec=%f xmt=%f dst=%f", org, rec, xmt, dst);
163
164         offset = ((rec - org) + (xmt - dst)) / 2;
165         delay = (dst - org) - (xmt - rec);
166
167         DBG("offset=%f delay=%f", offset, delay);
168
169         if (offset < STEPTIME_MIN_OFFSET && offset > -STEPTIME_MIN_OFFSET) {
170                 struct timeval adj;
171
172                 adj.tv_sec = (long) offset;
173                 adj.tv_usec = (offset - adj.tv_sec) * 1000000;
174
175                 DBG("adjusting time");
176
177                 if (adjtime(&adj, &adj) < 0) {
178                         connman_error("Failed to adjust time");
179                         return;
180                 }
181
182                 DBG("%lu seconds, %lu msecs", adj.tv_sec, adj.tv_usec);
183         } else {
184                 struct timeval cur;
185                 double dtime;
186
187                 gettimeofday(&cur, NULL);
188                 dtime = offset + cur.tv_sec + 1.0e-6 * cur.tv_usec;
189                 cur.tv_sec = (long) dtime;
190                 cur.tv_usec = (dtime - cur.tv_sec) * 1000000;
191
192                 DBG("setting time");
193
194                 if (settimeofday(&cur, NULL) < 0) {
195                         connman_error("Failed to set time");
196                         return;
197                 }
198
199                 DBG("%lu seconds, %lu msecs", cur.tv_sec, cur.tv_usec);
200         }
201 }
202
203 static guint channel_watch = 0;
204
205 static gboolean received_data(GIOChannel *channel, GIOCondition condition,
206                                                         gpointer user_data)
207 {
208         unsigned char buf[128];
209         struct msghdr msg;
210         struct iovec iov;
211         struct cmsghdr *cmsg;
212         struct timeval *tv;
213         char aux[128];
214         ssize_t len;
215         int fd;
216
217         if (condition & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) {
218                 connman_error("Problem with timer server channel");
219                 channel_watch = 0;
220                 return FALSE;
221         }
222
223         fd = g_io_channel_unix_get_fd(channel);
224
225         iov.iov_base = buf;
226         iov.iov_len = sizeof(buf);
227
228         memset(&msg, 0, sizeof(msg));
229         msg.msg_iov = &iov;
230         msg.msg_iovlen = 1;
231         msg.msg_control = aux;
232         msg.msg_controllen = sizeof(aux);
233
234         len = recvmsg(fd, &msg, MSG_DONTWAIT);
235         if (len < 0)
236                 return TRUE;
237
238         tv = NULL;
239
240         for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
241                 if (cmsg->cmsg_level != SOL_SOCKET)
242                         continue;
243
244                 switch (cmsg->cmsg_type) {
245                 case SCM_TIMESTAMP:
246                         tv = (struct timeval *) CMSG_DATA(cmsg);
247                         break;
248                 }
249         }
250
251         decode_msg(iov.iov_base, iov.iov_len, tv);
252
253         g_timeout_add_seconds(transmit_delay, next_request, NULL);
254
255         return TRUE;
256 }
257
258 static void start_ntp(const char *server)
259 {
260         GIOChannel *channel;
261         struct sockaddr_in addr;
262         int fd, tos = IPTOS_LOWDELAY, timestamp = 1;
263
264         DBG("server %s", server);
265
266         if (channel_watch > 0)
267                 return;
268
269         fd = socket(PF_INET, SOCK_DGRAM, 0);
270         if (fd < 0) {
271                 connman_error("Failed to open time server socket");
272                 return;
273         }
274
275         memset(&addr, 0, sizeof(addr));
276         addr.sin_family = AF_INET;
277
278         if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
279                 connman_error("Failed to bind time server socket");
280                 close(fd);
281                 return;
282         }
283
284         if (setsockopt(fd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) < 0) {
285                 connman_error("Failed to set type of service option");
286                 close(fd);
287                 return;
288         }
289
290         if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMP, &timestamp,
291                                                 sizeof(timestamp)) < 0) {
292                 connman_error("Failed to enable timestamp support");
293                 close(fd);
294                 return;
295         }
296
297         channel = g_io_channel_unix_new(fd);
298         if (channel == NULL) {
299                 close(fd);
300                 return;
301         }
302
303         g_io_channel_set_encoding(channel, NULL, NULL);
304         g_io_channel_set_buffered(channel, FALSE);
305
306         g_io_channel_set_close_on_unref(channel, TRUE);
307
308         channel_watch = g_io_add_watch_full(channel, G_PRIORITY_DEFAULT,
309                                 G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
310                                 received_data, NULL, NULL);
311
312         g_io_channel_unref(channel);
313
314         transmit_fd = fd;
315         transmit_server = g_strdup(server);
316
317         send_packet(fd, server);
318 }
319
320 static void resolv_debug(const char *str, void *data)
321 {
322         connman_info("%s: %s\n", (const char *) data, str);
323 }
324
325 static GResolv *resolv = NULL;
326 static guint resolv_lookup = 0;
327
328 static void resolv_result(GResolvResultStatus status,
329                                         char **results, gpointer user_data)
330 {
331         int i;
332
333         resolv_lookup = 0;
334
335         if (results != NULL) {
336                 for (i = 0; results[i]; i++)
337                         DBG("result: %s", results[i]);
338
339                 if (results[0] != NULL)
340                         start_ntp(results[0]);
341         }
342 }
343
344 int __connman_ntp_start(const char *interface, const char *resolver,
345                                                         const char *server)
346 {
347         DBG("interface %s server %s", interface, server);
348
349         resolv = g_resolv_new(0);
350         if (resolv == NULL)
351                 return -ENOMEM;
352
353         if (getenv("CONNMAN_RESOLV_DEBUG"))
354                 g_resolv_set_debug(resolv, resolv_debug, "RESOLV");
355
356         if (resolver != NULL)
357                 g_resolv_add_nameserver(resolv, resolver, 53, 0);
358
359         g_resolv_lookup_hostname(resolv, server, resolv_result, NULL);
360
361         return 0;
362 }
363
364 void __connman_ntp_stop(const char *interface)
365 {
366         DBG("interface %s", interface);
367
368         if (resolv == NULL)
369                 return;
370
371         if (resolv_lookup > 0) {
372                 g_resolv_cancel_lookup(resolv, resolv_lookup);
373                 resolv_lookup = 0;
374         }
375
376         if (channel_watch > 0) {
377                 g_source_remove(channel_watch);
378                 channel_watch = 0;
379         }
380
381         g_resolv_unref(resolv);
382 }