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 | |
---|
42 | struct ntpPacket { |
---|
43 | struct ntpPacketSmall ntp; |
---|
44 | char authenticator[96]; |
---|
45 | }; |
---|
46 | |
---|
47 | static int |
---|
48 | processPacket (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 | |
---|
74 | static int |
---|
75 | getServerTimespec(struct ntpPacketSmall *p, int state, void *usr_data) |
---|
76 | { |
---|
77 | struct timespec *ts = usr_data; |
---|
78 | unsigned 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 | |
---|
91 | int rtems_bsdnet_ntp_retry_count = 5; |
---|
92 | int rtems_bsdnet_ntp_timeout_secs = 5; |
---|
93 | int rtems_bsdnet_ntp_bcast_timeout_secs = 80; |
---|
94 | |
---|
95 | static int |
---|
96 | tryServer (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 | |
---|
152 | int rtems_bsdnet_get_ntp(int sock, rtems_bsdnet_ntp_callback_t callback, void *usr_data) |
---|
153 | { |
---|
154 | int s = -1; |
---|
155 | int i; |
---|
156 | int retry; |
---|
157 | struct sockaddr_in myAddr; |
---|
158 | int reuseFlag; |
---|
159 | int 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 | |
---|
208 | int |
---|
209 | rtems_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 | } |
---|