]> git.karo-electronics.de Git - karo-tx-redboot.git/blob - packages/net/sntp/v2_0/src/sntp.c
unified MX27, MX25, MX37 trees
[karo-tx-redboot.git] / packages / net / sntp / v2_0 / src / sntp.c
1 //=============================================================================
2 //
3 //      sntp.c
4 //
5 //      Simple Network Time Protocol
6 //
7 //=============================================================================
8 //####ECOSGPLCOPYRIGHTBEGIN####
9 // -------------------------------------------
10 // This file is part of eCos, the Embedded Configurable Operating System.
11 // Copyright (C) 2003 Andrew Lunn
12 //
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.
16 //
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
20 // for more details.
21 //
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.
25 //
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.
32 //
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.
35 //
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####
42 //
43 // Author(s):   andrew.lunn
44 // Contributors:
45 // Date:        2003-02-12
46 // Description: Provides a Simple Network Time Protocol Client
47 //####DESCRIPTIONEND####
48 //
49 //=============================================================================
50
51 #include <pkgconf/system.h>
52 #include <pkgconf/net_sntp.h>
53 #include <network.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>
58 #include <time.h>
59
60 /* NTP/SNTPv4 Packet Format (RFC2030) */
61 typedef struct
62 {
63   cyg_uint32    Seconds;    /* Since 00:00:00 Jan 01 1900 */
64   cyg_uint32    Fraction;
65 } NTP_TIMESTAMP;
66
67 typedef struct
68 {
69   /* Control combines LeapIndicator, Version, and Mode */
70   cyg_uint8     Control;
71   cyg_uint8     Stratum;
72   cyg_uint8     Poll;
73   cyg_uint8     Precision;
74
75   cyg_uint32    RootDelay;
76   cyg_uint32    RootDispersion;
77   cyg_uint32    ReferenceIdentifier;
78
79   NTP_TIMESTAMP ReferenceTimestamp;
80   NTP_TIMESTAMP OriginateTimestamp;
81   NTP_TIMESTAMP ReceiveTimestamp;
82   NTP_TIMESTAMP TransmitTimestamp;
83
84 //  cyg_uint32    KeyIdentifier;          /* Optional */
85 //  cyg_uint8     MessageDigest[16];      /* Optional */
86 } NTP_PACKET;
87 #define NTP_PACKET_MINLEN       48      /* Packet size - optional fields */
88
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
94
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)
98
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)
110
111 /* Time Base Conversion Macros
112  *
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
117  * leap years.
118  */
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)
122
123
124 struct sntp_srv_s {
125   struct sockaddr addr;
126   int stratum;
127   int version;
128   cyg_uint32 timestamp;
129 };
130 static int sntp_initialized = 0;
131
132
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.
139  */
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;
144
145 /* SNTP Timeouts
146  *
147  * SNTP_WAITPERIOD is the number of seconds to wait
148  * before retransmitting unanswered NTP requests
149  * whenever we are due for an update.
150  *
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
154  * time server.
155  */
156 #define SNTP_WAITPERIOD         10      /* Wait period in seconds */
157 #define SNTP_UPDATEPERIOD       (30*60) /* Update period in seconds */
158
159 #endif /* CYKPKG_NET_SNTP_UNICAST */
160
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.
164  */
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
168 #else
169 #define CYGNUM_SNTP_STACK_SIZE  CYGNUM_HAL_STACK_SIZE_TYPICAL
170 #endif
171 #endif  /* CYGNUM_SNTP_STACK_SIZE */
172
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. */
177
178 static int is_better(struct sntp_srv_s *newer, struct sntp_srv_s *old) {
179    
180   time_t last_time, diff;
181   
182   if (!memcmp(&newer->addr, &old->addr, newer->addr.sa_len)) return 1;
183   if (newer->stratum < old->stratum) return 1;
184
185   if (old->timestamp != 0xffffffff) {
186     last_time = TIME_NTP_TO_LOCAL(old->timestamp);
187   
188     diff = time(NULL) - last_time;
189     if (diff > 600) return 1;
190     
191     return 0;
192   }
193   return 1;
194 }
195
196 const struct in6_addr in6addr_ntp_multicast = IN6ADDR_NTP_MULTICAST;
197
198 static void sntp_fn(cyg_addrword_t data)
199 {
200   int fd;
201   int ret;
202   struct sockaddr_in local;
203   struct servent *serv;
204   NTP_PACKET ntp_pkt;
205   struct sntp_srv_s new_srv;
206   struct sntp_srv_s best_srv;
207   int mode;
208   socklen_t len;
209   time_t new_time, current_time, diff;
210   fd_set readfds;
211   int n;
212 #ifdef CYGPKG_NET_INET6
213   int fd6 = -1;
214   struct ipv6_mreq mreq;
215   struct sockaddr_in6 local6;
216 #endif
217 #ifdef CYGPKG_NET_SNTP_UNICAST
218   int i;
219   struct timeval timeout;
220 #endif /* CYGPKG_NET_SNTP_UNICAST */
221   struct timeval *ptimeout = NULL;
222
223   memset(&best_srv,0xff,sizeof(best_srv));
224
225   fd = socket(AF_INET,SOCK_DGRAM,0);
226   CYG_ASSERT(-1 != fd,"Failed to open socket");
227
228   serv = getservbyname("ntp","udp");
229   CYG_ASSERT(serv,"getservbyname(sntp,udp)");
230
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;
236
237   ret=bind(fd,(struct sockaddr *)&local,sizeof(local));
238   CYG_ASSERT(0 == ret, "Bind failed");
239
240   n = fd;
241
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;
247
248   /* Join the well-known NTP multicast groups.  We will
249    * try to join the link-local, site-local, and global
250    * unicast groups.
251    *
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
256    * from it!
257    */
258 #ifdef CYGHWR_NET_DRIVER_ETH0
259   // Link-Local
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");
265   }
266 #endif
267 #ifdef CYGHWR_NET_DRIVER_ETH1
268   // Link-Local
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");
274   }
275 #endif
276
277   // Site-Local
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");
281
282   // Global
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");
286   
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;
292   
293   ret = bind(fd6, (struct sockaddr *)&local6,sizeof(local6));
294   CYG_ASSERT(0 == ret, "Bind6 failed");
295   
296   n = (n > fd6 ? n : fd6);
297 #endif
298
299   while (1) {
300     FD_ZERO(&readfds);
301     FD_SET(fd,&readfds);
302 #ifdef CYGPKG_NET_INET6
303     FD_SET(fd6,&readfds);
304 #endif
305     
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
313      * reasonable below.
314      */
315      timeout.tv_sec = SNTP_WAITPERIOD;
316      timeout.tv_usec = 0;
317      ptimeout = &timeout;
318
319     /* If we've already set the time, then
320      * check to see if it's time to try and
321      * update it.
322      */
323     if (NextTimeUpdate != 0)
324     {
325         current_time = time(NULL);
326         if (current_time < NextTimeUpdate)
327         {
328             /* Set the select() timeout to wake us
329              * up when it's time to send more
330              * requests.
331              */
332             timeout.tv_sec = (SNTP_WAITPERIOD > (NextTimeUpdate - current_time)?
333                                                          (NextTimeUpdate - current_time):SNTP_WAITPERIOD);
334         } else {
335             /* It's already time for us to update our time */
336                         NextTimeUpdate = 0;
337         }
338     }
339
340     /* If we need to update our time and we have
341      * a list of NTP servers, then send out some
342      * time requests.
343      */
344     if (NextTimeUpdate == 0 && (sntp_num_servers > 0))
345     {
346         /* Send an NTP request to each NTP server
347          * in our server list.  Use version 3
348          * for v3 and v4 compatibility.
349          */
350         memset(&ntp_pkt, 0, sizeof(ntp_pkt));
351         ntp_pkt.Control =
352                         NTP_LI_NOLEAP |
353                         NTP_MODE_SET(NTP_MODE_CLIENT) |
354                         NTP_VERSION_SET(3);
355
356         /* Send a request packet to each of our
357          * configured servers.
358          */
359         cyg_mutex_lock(&sntp_mutex);
360         for (i = 0; i < sntp_num_servers; i++)
361         {
362                 /* Send the request packet using the
363                  * appropriate protocol.
364                  */
365             ntp_pkt.TransmitTimestamp.Seconds =
366                                 htonl(TIME_LOCAL_TO_NTP(time(NULL)));
367             if (sntp_servers[i].sa_family == AF_INET)
368             {
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);
375 #endif
376             }
377         }
378         cyg_mutex_unlock(&sntp_mutex);
379
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
384          * replies.
385          */
386         NextTimeUpdate = time(NULL) + SNTP_WAITPERIOD;
387     }
388 #endif /* CYGPKG_NET_SNTP_UNICAST */
389
390     ret = select(n+1, &readfds, NULL, NULL, ptimeout);
391     CYG_ASSERT(-1 != ret, "Select");
392
393 #ifdef CYGPKG_NET_SNTP_UNICAST
394     /* If we timed out, then try resending requests */
395     if (ret == 0)
396         continue;
397 #endif /* CYGPKG_NET_SNTP_UNICAST */
398
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);
402     }
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);
406     }
407 #endif
408     CYG_ASSERT(0 < ret,"recvfrom");
409
410     /* We expect at least enough bytes to fill the buffer */
411     if (ret < NTP_PACKET_MINLEN)
412       continue;
413     
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);
418     
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);
422       continue;
423     }
424     
425     /* Only process broadcast and server packets */
426     if (mode != NTP_MODE_BROADCAST && mode != NTP_MODE_SERVER) 
427       continue;
428     
429     /* Is the packet from a better server than our current one */
430     if (is_better(&new_srv,&best_srv)) {
431       best_srv = new_srv;
432
433       /* Work out the difference between server and our time.
434        * TODO: Implement RFC2030 recommendations for
435        * calculating propagation delay between the client
436        * and server.
437        */
438       new_time = TIME_NTP_TO_LOCAL(best_srv.timestamp);
439       current_time = time(NULL);
440       diff = current_time - new_time;
441       
442       if (diff < 0) 
443           diff = -diff;
444       
445       if (diff > 2) 
446           cyg_libc_time_settime(new_time);
447     }
448 #ifdef CYGPKG_NET_SNTP_UNICAST
449     NextTimeUpdate = time(NULL) + SNTP_UPDATEPERIOD;
450 #endif
451   }
452 }
453
454 /* Start the SNTP server */
455 void cyg_sntp_start(void) {
456   
457   static char sntp_stack[CYGNUM_SNTP_STACK_SIZE];
458   static cyg_thread sntp_thread_data;
459   static cyg_handle_t sntp_handle;
460
461   /* Only initialize things once */
462   if (sntp_initialized)
463       return;
464   sntp_initialized = 1;
465
466 #ifdef CYGPKG_NET_SNTP_UNICAST
467   /* Initialize the SNTP mutex */
468   cyg_mutex_init(&sntp_mutex);
469 #endif
470
471   cyg_thread_create(CYGPKG_NET_THREAD_PRIORITY+1, 
472                     sntp_fn,               // entry
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
479
480   cyg_thread_resume(sntp_handle);
481 }
482
483 #ifdef CYGPKG_NET_SNTP_UNICAST
484 /*
485  *      FUNCTION cyg_sntp_set_servers
486  *
487  *      DESCRIPTION
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.
493  *
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.
499  *
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.
504  *
505  *      PARAMETERS
506  *              server_list - Array of IPv4 and/or IPv6 sockaddr's
507  *              num_servers - Number of sockaddr's in array (0 to disable)
508  *
509  *      RETURN VALUES
510  *              None
511  */
512 void cyg_sntp_set_servers(struct sockaddr *server_list,
513         cyg_uint32 num_servers)
514 {
515         /* If we haven't already started the SNTP client, then
516          * start it now.
517          */
518     if (!sntp_initialized)
519         {
520                 /* If we haven't started already and we don't
521                  * have a list of servers, then don't start
522                  * anything up.
523                  */
524                 if (num_servers == 0)
525                         return;
526                 cyg_sntp_start();
527         }
528
529     /* Get the server list mutex */
530     cyg_mutex_lock(&sntp_mutex);
531
532         /* Record the new server list */
533         sntp_num_servers = num_servers;
534         if (num_servers == 0) {
535                 server_list = NULL;
536         } else {
537                 /* reset the waiting time to force a new update <= SNTP_WAITPERIOD*/
538                 NextTimeUpdate = 0;
539         }
540         sntp_servers = server_list;
541
542         /* Free the mutex */
543     cyg_mutex_unlock(&sntp_mutex);
544 }
545 #endif /* CYGPKG_NET_SNTP_UNICAST */
546
547
548
549