source: rtems/cpukit/libnetworking/lib/rtems_bsdnet_ntp.c @ 8d2733f

4.104.114.84.95
Last change on this file since 8d2733f was 8d2733f, checked in by Joel Sherrill <joel.sherrill@…>, on 07/25/04 at 15:04:13

2004-07-25 Till Straumann <strauman@…>

PR 620/networking

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