1 //=============================================================================
5 // Simple Network Time Protocol
7 //=============================================================================
8 //####ECOSGPLCOPYRIGHTBEGIN####
9 // -------------------------------------------
10 // This file is part of eCos, the Embedded Configurable Operating System.
11 // Copyright (C) 2003 Andrew Lunn
13 // eCos is free software; you can redistribute it and/or modify it under
14 // the terms of the GNU General Public License as published by the Free
15 // Software Foundation; either version 2 or (at your option) any later version.
17 // eCos is distributed in the hope that it will be useful, but WITHOUT ANY
18 // WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22 // You should have received a copy of the GNU General Public License along
23 // with eCos; if not, write to the Free Software Foundation, Inc.,
24 // 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
26 // As a special exception, if other files instantiate templates or use macros
27 // or inline functions from this file, or you compile this file and link it
28 // with other works to produce a work based on this file, this file does not
29 // by itself cause the resulting work to be covered by the GNU General Public
30 // License. However the source code for this file must still be made available
31 // in accordance with section (3) of the GNU General Public License.
33 // This exception does not invalidate any other reasons why a work based on
34 // this file might be covered by the GNU General Public License.
36 // Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
37 // at http://sources.redhat.com/ecos/ecos-license/
38 // -------------------------------------------
39 //####ECOSGPLCOPYRIGHTEND####
40 //=============================================================================
41 //#####DESCRIPTIONBEGIN####
43 // Author(s): andrew.lunn
46 // Description: Provides a Simple Network Time Protocol Client
47 //####DESCRIPTIONEND####
49 //=============================================================================
51 #include <pkgconf/system.h>
52 #include <pkgconf/net_sntp.h>
54 #include <cyg/infra/cyg_type.h>
55 #include <cyg/infra/cyg_ass.h>
56 #include <cyg/infra/cyg_trac.h>
57 #include <cyg/sntp/sntp.h>
60 /* NTP/SNTPv4 Packet Format (RFC2030) */
63 cyg_uint32 Seconds; /* Since 00:00:00 Jan 01 1900 */
69 /* Control combines LeapIndicator, Version, and Mode */
76 cyg_uint32 RootDispersion;
77 cyg_uint32 ReferenceIdentifier;
79 NTP_TIMESTAMP ReferenceTimestamp;
80 NTP_TIMESTAMP OriginateTimestamp;
81 NTP_TIMESTAMP ReceiveTimestamp;
82 NTP_TIMESTAMP TransmitTimestamp;
84 // cyg_uint32 KeyIdentifier; /* Optional */
85 // cyg_uint8 MessageDigest[16]; /* Optional */
87 #define NTP_PACKET_MINLEN 48 /* Packet size - optional fields */
89 /* Leap Indicator Field [Bits 7:6] */
90 #define NTP_LI_NOLEAP 0x00
91 #define NTP_LI_61SECS 0x40
92 #define NTP_LI_59SECS 0x80
93 #define NTP_LI_ALARM 0xC0
95 /* Version Field [Bits 5:3] */
96 #define NTP_VERSION_GET(pkt) ((((pkt)->Control)>>3)&0x7)
97 #define NTP_VERSION_SET(ver) (((ver)&0x7)<<3)
99 /* Mode Field [Bits 2:0] */
100 #define NTP_MODE_RESERVED 0
101 #define NTP_MODE_SYMACTIVE 1 /* Symmetric Active */
102 #define NTP_MODE_SYMPASSIVE 2 /* Symmetric Passive */
103 #define NTP_MODE_CLIENT 3
104 #define NTP_MODE_SERVER 4
105 #define NTP_MODE_BROADCAST 5
106 #define NTP_MODE_NTPCTRL 6 /* Reserved for NTP control message */
107 #define NTP_MODE_PRIVATE 7 /* Reserved for private use */
108 #define NTP_MODE_GET(pkt) (((pkt)->Control)&0x7)
109 #define NTP_MODE_SET(mode) ((mode)&0x7)
111 /* Time Base Conversion Macros
113 * The NTP timebase is 00:00 Jan 1 1900. The local
114 * time base is 00:00 Jan 1 1970. Convert between
115 * these two by added or substracting 70 years
116 * worth of time. Note that 17 of these years were
119 #define TIME_BASEDIFF ((((cyg_uint32)70*365 + 17) * 24*3600))
120 #define TIME_NTP_TO_LOCAL(t) ((t)-TIME_BASEDIFF)
121 #define TIME_LOCAL_TO_NTP(t) ((t)+TIME_BASEDIFF)
125 struct sockaddr addr;
128 cyg_uint32 timestamp;
130 static int sntp_initialized = 0;
133 #ifdef CYGPKG_NET_SNTP_UNICAST
134 /* When using SNTP unicast mode, sntp_servers
135 * points to an array of char pointers that
136 * specify NTP server addresses to send
137 * requests to. sntp_num_servers specifies
138 * the number of hostnames in the array.
140 static struct sockaddr *sntp_servers = NULL;
141 static cyg_uint32 sntp_num_servers = 0;
142 static cyg_mutex_t sntp_mutex;
143 static time_t NextTimeUpdate = 0;
147 * SNTP_WAITPERIOD is the number of seconds to wait
148 * before retransmitting unanswered NTP requests
149 * whenever we are due for an update.
151 * SNTP_UPDATEPERIOD is the number of seconds to wait
152 * after we get a good time update before we feel
153 * like we should re-synchronize again with the
156 #define SNTP_WAITPERIOD 10 /* Wait period in seconds */
157 #define SNTP_UPDATEPERIOD (30*60) /* Update period in seconds */
159 #endif /* CYKPKG_NET_SNTP_UNICAST */
161 #ifndef CYGNUM_SNTP_STACK_SIZE
162 /* Use a stack size of at least CYGNUM_SNTP_STACK_SIZE_MIN, but not less than
163 * CYGNUM_HAL_STACK_SIZE_TYPICAL.
165 #define CYGNUM_SNTP_STACK_SIZE_MIN 4096
166 #if (CYGNUM_HAL_STACK_SIZE_TYPICAL < CYGNUM_SNTP_STACK_SIZE_MIN)
167 #define CYGNUM_SNTP_STACK_SIZE CYGNUM_SNTP_STACK_SIZE_MIN
169 #define CYGNUM_SNTP_STACK_SIZE CYGNUM_HAL_STACK_SIZE_TYPICAL
171 #endif /* CYGNUM_SNTP_STACK_SIZE */
173 /* Is the new server better than the current one? If its the same as
174 the current, its always better. If the stratum is lower its better.
175 If we have not heard from the old server for more than 10 minutes,
176 the new server is better. */
178 static int is_better(struct sntp_srv_s *newer, struct sntp_srv_s *old) {
180 time_t last_time, diff;
182 if (!memcmp(&newer->addr, &old->addr, newer->addr.sa_len)) return 1;
183 if (newer->stratum < old->stratum) return 1;
185 if (old->timestamp != 0xffffffff) {
186 last_time = TIME_NTP_TO_LOCAL(old->timestamp);
188 diff = time(NULL) - last_time;
189 if (diff > 600) return 1;
196 const struct in6_addr in6addr_ntp_multicast = IN6ADDR_NTP_MULTICAST;
198 static void sntp_fn(cyg_addrword_t data)
202 struct sockaddr_in local;
203 struct servent *serv;
205 struct sntp_srv_s new_srv;
206 struct sntp_srv_s best_srv;
209 time_t new_time, current_time, diff;
212 #ifdef CYGPKG_NET_INET6
214 struct ipv6_mreq mreq;
215 struct sockaddr_in6 local6;
217 #ifdef CYGPKG_NET_SNTP_UNICAST
219 struct timeval timeout;
220 #endif /* CYGPKG_NET_SNTP_UNICAST */
221 struct timeval *ptimeout = NULL;
223 memset(&best_srv,0xff,sizeof(best_srv));
225 fd = socket(AF_INET,SOCK_DGRAM,0);
226 CYG_ASSERT(-1 != fd,"Failed to open socket");
228 serv = getservbyname("ntp","udp");
229 CYG_ASSERT(serv,"getservbyname(sntp,udp)");
231 memset(&local,0,sizeof(local));
232 local.sin_family = AF_INET;
233 local.sin_len = sizeof(local);
234 local.sin_port = serv->s_port;
235 local.sin_addr.s_addr = INADDR_ANY;
237 ret=bind(fd,(struct sockaddr *)&local,sizeof(local));
238 CYG_ASSERT(0 == ret, "Bind failed");
242 #ifdef CYGPKG_NET_INET6
243 fd6 = socket(AF_INET6, SOCK_DGRAM,0);
244 CYG_ASSERT(-1 != fd,"Failed to open socket");
245 mreq.ipv6mr_multiaddr = in6addr_ntp_multicast;
246 mreq.ipv6mr_interface = 0;
248 /* Join the well-known NTP multicast groups. We will
249 * try to join the link-local, site-local, and global
252 * Note that we skip the node-local group since it
253 * doesn't make any sense to join that group. If we
254 * had an NTP server running on the local node, we
255 * wouldn't need to have an NTP client get the time
258 #ifdef CYGHWR_NET_DRIVER_ETH0
260 mreq.ipv6mr_multiaddr.s6_addr[1]=0x02;
261 mreq.ipv6mr_interface = if_nametoindex("eth0");
262 if (mreq.ipv6mr_interface != 0 ) {
263 ret = setsockopt(fd6, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq));
264 CYG_ASSERT(0 == ret, "setsockopt(IPV6_JOIN_GROUP) Link-Local eth0");
267 #ifdef CYGHWR_NET_DRIVER_ETH1
269 mreq.ipv6mr_multiaddr.s6_addr[1]=0x02;
270 mreq.ipv6mr_interface = if_nametoindex("eth1");
271 if (mreq.ipv6mr_interface != 0 ) {
272 ret = setsockopt(fd6, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq));
273 CYG_ASSERT(0 == ret, "setsockopt(IPV6_JOIN_GROUP) Link-Local eth1");
278 mreq.ipv6mr_multiaddr.s6_addr[1]=0x05;
279 ret = setsockopt(fd6, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq));
280 CYG_ASSERT(0 == ret, "setsockopt(IPV6_JOIN_GROUP) Site-Local");
283 mreq.ipv6mr_multiaddr.s6_addr[1]=0x0e;
284 ret = setsockopt(fd6, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq));
285 CYG_ASSERT(0 == ret, "setsockopt(IPV6_JOIN_GROUP) Global");
287 memset(&local6,0,sizeof(local6));
288 local6.sin6_family = AF_INET6;
289 local6.sin6_len = sizeof(local6);
290 local6.sin6_port = serv->s_port;
291 local6.sin6_addr = in6addr_any;
293 ret = bind(fd6, (struct sockaddr *)&local6,sizeof(local6));
294 CYG_ASSERT(0 == ret, "Bind6 failed");
296 n = (n > fd6 ? n : fd6);
302 #ifdef CYGPKG_NET_INET6
303 FD_SET(fd6,&readfds);
306 #ifdef CYGPKG_NET_SNTP_UNICAST
307 /* By default, we will let select() wait
308 * for SNTP_WAITPERIOD to receive a packet. This
309 * allows us to periodically wake up and check
310 * if new servers have been configured. However,
311 * if we are waiting to send an update request,
312 * we will set ptimeout to something more
315 timeout.tv_sec = SNTP_WAITPERIOD;
319 /* If we've already set the time, then
320 * check to see if it's time to try and
323 if (NextTimeUpdate != 0)
325 current_time = time(NULL);
326 if (current_time < NextTimeUpdate)
328 /* Set the select() timeout to wake us
329 * up when it's time to send more
332 timeout.tv_sec = (SNTP_WAITPERIOD > (NextTimeUpdate - current_time)?
333 (NextTimeUpdate - current_time):SNTP_WAITPERIOD);
335 /* It's already time for us to update our time */
340 /* If we need to update our time and we have
341 * a list of NTP servers, then send out some
344 if (NextTimeUpdate == 0 && (sntp_num_servers > 0))
346 /* Send an NTP request to each NTP server
347 * in our server list. Use version 3
348 * for v3 and v4 compatibility.
350 memset(&ntp_pkt, 0, sizeof(ntp_pkt));
353 NTP_MODE_SET(NTP_MODE_CLIENT) |
356 /* Send a request packet to each of our
357 * configured servers.
359 cyg_mutex_lock(&sntp_mutex);
360 for (i = 0; i < sntp_num_servers; i++)
362 /* Send the request packet using the
363 * appropriate protocol.
365 ntp_pkt.TransmitTimestamp.Seconds =
366 htonl(TIME_LOCAL_TO_NTP(time(NULL)));
367 if (sntp_servers[i].sa_family == AF_INET)
369 sendto(fd, &ntp_pkt, sizeof(ntp_pkt), 0,
370 &sntp_servers[i], sntp_servers[i].sa_len);
371 #ifdef CYGPKG_NET_INET6
372 } else if (sntp_servers[i].sa_family == AF_INET6) {
373 sendto(fd6, &ntp_pkt, sizeof(ntp_pkt), 0,
374 &sntp_servers[i], sntp_servers[i].sa_len);
378 cyg_mutex_unlock(&sntp_mutex);
380 /* Set the NextTimeUpdate so that we don't
381 * send any more requests until the next
382 * poll period. And we've already configured
383 * the select() timeout above to wait for
386 NextTimeUpdate = time(NULL) + SNTP_WAITPERIOD;
388 #endif /* CYGPKG_NET_SNTP_UNICAST */
390 ret = select(n+1, &readfds, NULL, NULL, ptimeout);
391 CYG_ASSERT(-1 != ret, "Select");
393 #ifdef CYGPKG_NET_SNTP_UNICAST
394 /* If we timed out, then try resending requests */
397 #endif /* CYGPKG_NET_SNTP_UNICAST */
399 len = sizeof(new_srv.addr);
400 if (FD_ISSET(fd,&readfds)) {
401 ret=recvfrom(fd,&ntp_pkt,sizeof(ntp_pkt),0,(struct sockaddr *)&new_srv.addr,&len);
403 #ifdef CYGPKG_NET_INET6
404 if (FD_ISSET(fd6,&readfds)) {
405 ret=recvfrom(fd6,&ntp_pkt,sizeof(ntp_pkt),0,(struct sockaddr *)&new_srv.addr,&len);
408 CYG_ASSERT(0 < ret,"recvfrom");
410 /* We expect at least enough bytes to fill the buffer */
411 if (ret < NTP_PACKET_MINLEN)
414 new_srv.version = NTP_VERSION_GET(&ntp_pkt);
415 new_srv.stratum = ntp_pkt.Stratum;
416 new_srv.timestamp = ntohl(ntp_pkt.TransmitTimestamp.Seconds);
417 mode = NTP_MODE_GET(&ntp_pkt);
419 /* Only support protocol versions 3 or 4 */
420 if (new_srv.version < 3 || new_srv.version > 4) {
421 CYG_TRACE1(1, "Unsupported version of NTP. Version %d",new_srv.version);
425 /* Only process broadcast and server packets */
426 if (mode != NTP_MODE_BROADCAST && mode != NTP_MODE_SERVER)
429 /* Is the packet from a better server than our current one */
430 if (is_better(&new_srv,&best_srv)) {
433 /* Work out the difference between server and our time.
434 * TODO: Implement RFC2030 recommendations for
435 * calculating propagation delay between the client
438 new_time = TIME_NTP_TO_LOCAL(best_srv.timestamp);
439 current_time = time(NULL);
440 diff = current_time - new_time;
446 cyg_libc_time_settime(new_time);
448 #ifdef CYGPKG_NET_SNTP_UNICAST
449 NextTimeUpdate = time(NULL) + SNTP_UPDATEPERIOD;
454 /* Start the SNTP server */
455 void cyg_sntp_start(void) {
457 static char sntp_stack[CYGNUM_SNTP_STACK_SIZE];
458 static cyg_thread sntp_thread_data;
459 static cyg_handle_t sntp_handle;
461 /* Only initialize things once */
462 if (sntp_initialized)
464 sntp_initialized = 1;
466 #ifdef CYGPKG_NET_SNTP_UNICAST
467 /* Initialize the SNTP mutex */
468 cyg_mutex_init(&sntp_mutex);
471 cyg_thread_create(CYGPKG_NET_THREAD_PRIORITY+1,
473 0, // entry parameter
474 "SNTP Client", // Name
475 &sntp_stack, // stack
476 sizeof(sntp_stack), // stack size
477 &sntp_handle, // Handle
478 &sntp_thread_data); // Thread data structure
480 cyg_thread_resume(sntp_handle);
483 #ifdef CYGPKG_NET_SNTP_UNICAST
485 * FUNCTION cyg_sntp_set_servers
488 * Sets the list of SNTP/NTP servers to use
489 * for SNTP unicast requests. The list is
490 * specified as a list of sockaddr structures
491 * and can contain both IPv4 and IPv6
492 * addresses and UDP port numbers.
494 * The server_list array must be maintained
495 * by the caller and must not be modified after
496 * it is registered by this function. The
497 * array can be unregistered by calling this
498 * function again with different parameters.
500 * NOTE: If cyg_sntp_start() has not been called
501 * already, and this function is called with a
502 * list of 1 or more servers, then cyg_sntp_start()
503 * will be called by this function to start the client.
506 * server_list - Array of IPv4 and/or IPv6 sockaddr's
507 * num_servers - Number of sockaddr's in array (0 to disable)
512 void cyg_sntp_set_servers(struct sockaddr *server_list,
513 cyg_uint32 num_servers)
515 /* If we haven't already started the SNTP client, then
518 if (!sntp_initialized)
520 /* If we haven't started already and we don't
521 * have a list of servers, then don't start
524 if (num_servers == 0)
529 /* Get the server list mutex */
530 cyg_mutex_lock(&sntp_mutex);
532 /* Record the new server list */
533 sntp_num_servers = num_servers;
534 if (num_servers == 0) {
537 /* reset the waiting time to force a new update <= SNTP_WAITPERIOD*/
540 sntp_servers = server_list;
543 cyg_mutex_unlock(&sntp_mutex);
545 #endif /* CYGPKG_NET_SNTP_UNICAST */