source: rtems/cpukit/libnetworking/lib/rtems_bsdnet_ntp.c @ 2b03a62

4.115
Last change on this file since 2b03a62 was 2b03a62, checked in by Jim Panetta <panetta@…>, on 11/01/13 at 13:59:41

NTP: Sync time correctly when receiving broadcast updates

1) The value of rtems_bsdnet_ntpserver_count is equal to 0 when no

server is set, so the check for (rtems_bsdnet_ntpserver_count < 0)
in rtems_bsdnet_get_ntp() is wrong. The check should be "<= 0".

2) Binding the listening socket port to 0 does not work. Packets

appear on the interface, but the recvfrom in tryServer() never
returns. Changing this to the well known NTP socket 123 allows
the packets to be seen.

3) In tryServer(), an explicit check for NTP version 3 packets is made.

If the NTP server is version 4, this check fails even though the
packets seem to be the right shape.

  • Property mode set to 100644
File size: 5.7 KB
Line 
1/*
2 * Synchronize with an NTP server
3 *
4 * This program may be distributed and used for any purpose.
5 * I ask only that you:
6 *      1. Leave this author information intact.
7 *      2. Document any changes you make.
8 *
9 * W. Eric Norum
10 * Canadian Light Source
11 * University of Saskatchewan
12 * Saskatoon, Saskatchewan, CANADA
13 * eric@cls.usask.ca
14 */
15
16#ifdef HAVE_CONFIG_H
17#include "config.h"
18#endif
19
20#include <unistd.h> /* close */
21#include <stdio.h>
22#include <string.h>
23#include <errno.h>
24#include <time.h>
25#include <limits.h>
26#include <rtems.h>
27#include <rtems/rtems_bsdnet.h>
28#include <rtems/error.h>
29#include <sys/types.h>
30#include <sys/socket.h>
31#include <netinet/in.h>
32
33#include <rtems/bsdnet/servers.h>
34
35/*
36 * RTEMS base: 1988, January 1
37 *  UNIX base: 1970, January 1
38 *   NTP base: 1900, January 1
39 */
40#define UNIX_BASE_TO_NTP_BASE (uint32_t)(((70UL*365UL)+17UL) * (24UL*60UL*60UL))
41
42struct ntpPacket {
43        struct ntpPacketSmall   ntp;
44        char                    authenticator[96];
45};
46
47static int
48processPacket (struct ntpPacketSmall *p, int state, void *unused)
49{
50        time_t tbuf;
51        struct tm *lt;
52        rtems_time_of_day rt;
53        rtems_interval ticks_per_second;
54
55        if ( state )
56                return 0;
57
58        ticks_per_second = rtems_clock_get_ticks_per_second();
59        tbuf = ntohl (p->transmit_timestamp.integer) - UNIX_BASE_TO_NTP_BASE - rtems_bsdnet_timeoffset;
60        lt = gmtime (&tbuf);
61        rt.year = lt->tm_year + 1900;
62        rt.month = lt->tm_mon + 1;
63        rt.day = lt->tm_mday;
64        rt.hour = lt->tm_hour;
65        rt.minute = lt->tm_min;
66        rt.second = lt->tm_sec;
67        rt.ticks = ntohl (p->transmit_timestamp.fraction) / (ULONG_MAX / ticks_per_second);
68        if (rt.ticks >= ticks_per_second)
69                rt.ticks = ticks_per_second - 1;
70        rtems_clock_set (&rt);
71        return 0;
72}
73
74static int
75getServerTimespec(struct ntpPacketSmall *p, int state, void *usr_data)
76{
77struct timespec *ts = usr_data;
78unsigned long long tmp;
79
80        if ( 0 == state ) {
81                ts->tv_sec  = ntohl( p->transmit_timestamp.integer );
82                ts->tv_sec -= rtems_bsdnet_timeoffset + UNIX_BASE_TO_NTP_BASE;
83
84                tmp  = 1000000000 * (unsigned long long)ntohl(p->transmit_timestamp.fraction);
85
86                ts->tv_nsec = (unsigned long) (tmp>>32);
87        }
88        return 0;
89}
90
91int rtems_bsdnet_ntp_retry_count  = 5;
92int rtems_bsdnet_ntp_timeout_secs = 5;
93int rtems_bsdnet_ntp_bcast_timeout_secs = 80;
94
95static int
96tryServer (int i, int s, rtems_bsdnet_ntp_callback_t callback, void *usr_data)
97{
98        int l = 0;
99        struct timeval tv;
100        socklen_t farlen;
101        struct sockaddr_in farAddr;
102        struct ntpPacketSmall packet;
103
104        if (i < 0)
105                tv.tv_sec = rtems_bsdnet_ntp_bcast_timeout_secs;
106        else
107                tv.tv_sec = rtems_bsdnet_ntp_timeout_secs;
108        tv.tv_usec = 0;
109        if (setsockopt (s, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof tv) < 0) {
110                fprintf (stderr, "rtems_bsdnet_get_ntp() Can't set socket receive timeout: %s\n", strerror (errno));
111                close (s);
112                return -1;
113        }
114        if (i >= 0) {
115                memset (&farAddr, 0, sizeof farAddr);
116                farAddr.sin_family = AF_INET;
117                farAddr.sin_port = htons (123);
118                farAddr.sin_addr = rtems_bsdnet_ntpserver[i];
119                memset (&packet, 0, sizeof packet);
120                packet.li_vn_mode = (3 << 3) | 3; /* NTP version 3, client */
121                if ( callback( &packet, 1, usr_data ) )
122                        return -1;
123                l = sendto (s, &packet, sizeof packet, 0, (struct sockaddr *)&farAddr, sizeof farAddr);
124                if (l != sizeof packet) {
125                        fprintf (stderr, "rtems_bsdnet_get_ntp() Can't send: %s\n", strerror (errno));
126                        return -1;
127                }
128        } else {
129                if ( callback( &packet, -1, usr_data ) )
130                        return -1;
131        }
132        farlen = sizeof farAddr;
133        i = recvfrom (s, &packet, sizeof packet, 0, (struct sockaddr *)&farAddr, &farlen);
134        if (i == 0)
135                fprintf (stderr, "rtems_bsdnet_get_ntp() Unexpected EOF");
136        if (i < 0) {
137                if ((errno == EWOULDBLOCK) || (errno == EAGAIN))
138                        return -1;
139                fprintf (stderr, "rtems_bsdnet_get_ntp() Can't receive: %s\n", strerror (errno));
140        }
141
142        if ( i >= sizeof packet &&
143    (((packet.li_vn_mode & (0x7 << 3)) == (3 << 3)) ||
144     ((packet.li_vn_mode & (0x7 << 3)) == (4 << 3))) &&
145            ((packet.transmit_timestamp.integer != 0) || (packet.transmit_timestamp.fraction != 0)) &&
146                0 == callback( &packet, 0 , usr_data) )
147                return 0;
148
149        return -1;
150}
151
152int rtems_bsdnet_get_ntp(int sock, rtems_bsdnet_ntp_callback_t callback, void *usr_data)
153{
154int s = -1;
155int i;
156int retry;
157struct sockaddr_in myAddr;
158int reuseFlag;
159int ret;
160
161        if ( !callback )
162                callback = getServerTimespec;
163
164        if ( sock < 0 ) {
165        s = socket (AF_INET, SOCK_DGRAM, 0);
166        if (s < 0) {
167                fprintf (stderr, "rtems_bsdnet_get_ntp() Can't create socket: %s\n", strerror (errno));
168                return -1;
169        }
170        reuseFlag = 1;
171        if (setsockopt (s, SOL_SOCKET, SO_REUSEADDR, (char *)&reuseFlag, sizeof reuseFlag) < 0) {
172                fprintf (stderr, "rtems_bsdnet_get_ntp() Can't set socket reuse: %s\n", strerror (errno));
173                close (s);
174                return -1;
175        }
176        memset (&myAddr, 0, sizeof myAddr);
177        myAddr.sin_family = AF_INET;
178        myAddr.sin_port = htons (123);
179        myAddr.sin_addr.s_addr = htonl (INADDR_ANY);
180        if (bind (s, (struct sockaddr *)&myAddr, sizeof myAddr) < 0) {
181                fprintf (stderr, "rtems_bsdnet_get_ntp() Can't bind socket: %s\n", strerror (errno));
182                close (s);
183                return -1;
184        }
185        sock = s;
186        }
187        ret = -1;
188        for (retry = 0 ; (ret == -1) && (retry < rtems_bsdnet_ntp_retry_count) ; retry++) {
189                /*
190                 * If there's no server we just have to wait
191                 * and hope that there's an NTP broadcast
192                 * server out there somewhere.
193                 */
194                if (rtems_bsdnet_ntpserver_count <= 0) {
195                        ret = tryServer (-1, sock, callback, usr_data);
196                }
197                else {
198                        for (i = 0 ; (ret == -1) && (i < rtems_bsdnet_ntpserver_count) ; i++) {
199                                ret = tryServer (i, sock, callback, usr_data);
200                        }
201                }
202        }
203        if ( s >= 0 )
204                close (s);
205        return ret;
206}
207
208int
209rtems_bsdnet_synchronize_ntp (int interval, rtems_task_priority priority)
210{
211        if (interval != 0) {
212                fprintf (stderr, "Daemon-mode note yet supported.\n");
213                errno = EINVAL;
214                return -1;
215        }
216        return rtems_bsdnet_get_ntp( -1, processPacket, 0);
217}
Note: See TracBrowser for help on using the repository browser.