OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [rtos/] [ecos-3.0/] [packages/] [net/] [sntp/] [current/] [src/] [sntp.c] - Blame information for rev 786

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 786 skrzyp
//=============================================================================
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 Free Software Foundation, Inc.                        
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      
16
// version.                                                                 
17
//
18
// eCos is distributed in the hope that it will be useful, but WITHOUT      
19
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or    
20
// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License    
21
// for more details.                                                        
22
//
23
// You should have received a copy of the GNU General Public License        
24
// along with eCos; if not, write to the Free Software Foundation, Inc.,    
25
// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.            
26
//
27
// As a special exception, if other files instantiate templates or use      
28
// macros or inline functions from this file, or you compile this file      
29
// and link it with other works to produce a work based on this file,       
30
// this file does not by itself cause the resulting work to be covered by   
31
// the GNU General Public License. However the source code for this file    
32
// must still be made available in accordance with section (3) of the GNU   
33
// General Public License v2.                                               
34
//
35
// This exception does not invalidate any other reasons why a work based    
36
// on this file might be covered by the GNU General Public License.         
37
// -------------------------------------------                              
38
// ####ECOSGPLCOPYRIGHTEND####                                              
39
//=============================================================================
40
//#####DESCRIPTIONBEGIN####
41
//
42
// Author(s):   andrew.lunn
43
// Contributors:
44
// Date:        2003-02-12
45
// Description: Provides a Simple Network Time Protocol Client
46
//####DESCRIPTIONEND####
47
//
48
//=============================================================================
49
 
50
#include <pkgconf/system.h>
51
#include <pkgconf/net_sntp.h>
52
#include <network.h>
53
#include <cyg/infra/cyg_type.h>
54
#include <cyg/infra/cyg_ass.h>
55
#include <cyg/infra/cyg_trac.h>
56
#include <cyg/sntp/sntp.h>
57
#include <time.h>
58
 
59
/* NTP/SNTPv4 Packet Format (RFC2030) */
60
typedef struct
61
{
62
  cyg_uint32    Seconds;    /* Since 00:00:00 Jan 01 1900 */
63
  cyg_uint32    Fraction;
64
} NTP_TIMESTAMP;
65
 
66
typedef struct
67
{
68
  /* Control combines LeapIndicator, Version, and Mode */
69
  cyg_uint8     Control;
70
  cyg_uint8     Stratum;
71
  cyg_uint8     Poll;
72
  cyg_uint8     Precision;
73
 
74
  cyg_uint32    RootDelay;
75
  cyg_uint32    RootDispersion;
76
  cyg_uint32    ReferenceIdentifier;
77
 
78
  NTP_TIMESTAMP ReferenceTimestamp;
79
  NTP_TIMESTAMP OriginateTimestamp;
80
  NTP_TIMESTAMP ReceiveTimestamp;
81
  NTP_TIMESTAMP TransmitTimestamp;
82
 
83
//  cyg_uint32    KeyIdentifier;          /* Optional */
84
//  cyg_uint8     MessageDigest[16];      /* Optional */
85
} NTP_PACKET;
86
#define NTP_PACKET_MINLEN       48      /* Packet size - optional fields */
87
 
88
/* Leap Indicator Field [Bits 7:6] */
89
#define NTP_LI_NOLEAP           0x00
90
#define NTP_LI_61SECS           0x40
91
#define NTP_LI_59SECS           0x80
92
#define NTP_LI_ALARM            0xC0
93
 
94
/* Version Field [Bits 5:3] */
95
#define NTP_VERSION_GET(pkt)    ((((pkt)->Control)>>3)&0x7)
96
#define NTP_VERSION_SET(ver)    (((ver)&0x7)<<3)
97
 
98
/* Mode Field [Bits 2:0] */
99
#define NTP_MODE_RESERVED       0
100
#define NTP_MODE_SYMACTIVE      1    /* Symmetric Active */
101
#define NTP_MODE_SYMPASSIVE     2    /* Symmetric Passive */
102
#define NTP_MODE_CLIENT         3
103
#define NTP_MODE_SERVER         4
104
#define NTP_MODE_BROADCAST      5
105
#define NTP_MODE_NTPCTRL        6    /* Reserved for NTP control message */
106
#define NTP_MODE_PRIVATE        7    /* Reserved for private use */
107
#define NTP_MODE_GET(pkt)       (((pkt)->Control)&0x7)
108
#define NTP_MODE_SET(mode)      ((mode)&0x7)
109
 
110
/* Time Base Conversion Macros
111
 *
112
 * The NTP timebase is 00:00 Jan 1 1900.  The local
113
 * time base is 00:00 Jan 1 1970.  Convert between
114
 * these two by added or substracting 70 years
115
 * worth of time.  Note that 17 of these years were
116
 * leap years.
117
 */
118
#define TIME_BASEDIFF           ((((cyg_uint32)70*365 + 17) * 24*3600))
119
#define TIME_NTP_TO_LOCAL(t)    ((t)-TIME_BASEDIFF)
120
#define TIME_LOCAL_TO_NTP(t)    ((t)+TIME_BASEDIFF)
121
 
122
 
123
struct sntp_srv_s {
124
  struct sockaddr addr;
125
  int stratum;
126
  int version;
127
  cyg_uint32 timestamp;
128
};
129
static int sntp_initialized = 0;
130
 
131
 
132
#ifdef CYGPKG_NET_SNTP_UNICAST
133
/* When using SNTP unicast mode, sntp_servers
134
 * points to an array of char pointers that
135
 * specify NTP server addresses to send
136
 * requests to.  sntp_num_servers specifies
137
 * the number of hostnames in the array.
138
 */
139
static struct sockaddr *sntp_servers = NULL;
140
static cyg_uint32 sntp_num_servers = 0;
141
static cyg_mutex_t sntp_mutex;
142
static time_t NextTimeUpdate = 0;
143
 
144
/* SNTP Timeouts
145
 *
146
 * SNTP_WAITPERIOD is the number of seconds to wait
147
 * before retransmitting unanswered NTP requests
148
 * whenever we are due for an update.
149
 *
150
 * SNTP_UPDATEPERIOD is the number of seconds to wait
151
 * after we get a good time update before we feel
152
 * like we should re-synchronize again with the
153
 * time server.
154
 */
155
#define SNTP_WAITPERIOD         10      /* Wait period in seconds */
156
#define SNTP_UPDATEPERIOD       (30*60) /* Update period in seconds */
157
 
158
#endif /* CYKPKG_NET_SNTP_UNICAST */
159
 
160
#ifndef CYGNUM_SNTP_STACK_SIZE
161
/* Use a stack size of at least CYGNUM_SNTP_STACK_SIZE_MIN, but not less than
162
 * CYGNUM_HAL_STACK_SIZE_TYPICAL.
163
 */
164
#define CYGNUM_SNTP_STACK_SIZE_MIN      4096
165
#if (CYGNUM_HAL_STACK_SIZE_TYPICAL < CYGNUM_SNTP_STACK_SIZE_MIN)
166
#define CYGNUM_SNTP_STACK_SIZE  CYGNUM_SNTP_STACK_SIZE_MIN
167
#else
168
#define CYGNUM_SNTP_STACK_SIZE  CYGNUM_HAL_STACK_SIZE_TYPICAL
169
#endif
170
#endif  /* CYGNUM_SNTP_STACK_SIZE */
171
 
172
/* Is the new server better than the current one? If its the same as
173
   the current, its always better. If the stratum is lower its better.
174
   If we have not heard from the old server for more than 10 minutes,
175
   the new server is better. */
176
 
177
static int is_better(struct sntp_srv_s *newer, struct sntp_srv_s *old) {
178
 
179
  time_t last_time, diff;
180
 
181
  if (!memcmp(&newer->addr, &old->addr, newer->addr.sa_len)) return 1;
182
  if (newer->stratum < old->stratum) return 1;
183
 
184
  if (old->timestamp != 0xffffffff) {
185
    last_time = TIME_NTP_TO_LOCAL(old->timestamp);
186
 
187
    diff = time(NULL) - last_time;
188
    if (diff > 600) return 1;
189
 
190
    return 0;
191
  }
192
  return 1;
193
}
194
 
195
const struct in6_addr in6addr_ntp_multicast = IN6ADDR_NTP_MULTICAST;
196
 
197
static void sntp_fn(cyg_addrword_t data)
198
{
199
  int fd;
200
  int ret;
201
  struct sockaddr_in local;
202
  struct servent *serv;
203
  NTP_PACKET ntp_pkt;
204
  struct sntp_srv_s new_srv;
205
  struct sntp_srv_s best_srv;
206
  int mode;
207
  socklen_t len;
208
  time_t new_time, current_time, diff;
209
  fd_set readfds;
210
  int n;
211
#ifdef CYGPKG_NET_INET6
212
  int fd6 = -1;
213
  struct ipv6_mreq mreq;
214
  struct sockaddr_in6 local6;
215
#endif
216
#ifdef CYGPKG_NET_SNTP_UNICAST
217
  int i;
218
  struct timeval timeout;
219
#endif /* CYGPKG_NET_SNTP_UNICAST */
220
  struct timeval *ptimeout = NULL;
221
 
222
  memset(&best_srv,0xff,sizeof(best_srv));
223
 
224
  fd = socket(AF_INET,SOCK_DGRAM,0);
225
  CYG_ASSERT(-1 != fd,"Failed to open socket");
226
 
227
  serv = getservbyname("ntp","udp");
228
  CYG_ASSERT(serv,"getservbyname(sntp,udp)");
229
 
230
  memset(&local,0,sizeof(local));
231
  local.sin_family = AF_INET;
232
  local.sin_len = sizeof(local);
233
  local.sin_port = serv->s_port;
234
  local.sin_addr.s_addr = INADDR_ANY;
235
 
236
  ret=bind(fd,(struct sockaddr *)&local,sizeof(local));
237
  CYG_ASSERT(0 == ret, "Bind failed");
238
 
239
  n = fd;
240
 
241
#ifdef CYGPKG_NET_INET6
242
  fd6 = socket(AF_INET6, SOCK_DGRAM,0);
243
  CYG_ASSERT(-1 != fd,"Failed to open socket");
244
  mreq.ipv6mr_multiaddr = in6addr_ntp_multicast;
245
  mreq.ipv6mr_interface = 0;
246
 
247
  /* Join the well-known NTP multicast groups.  We will
248
   * try to join the link-local, site-local, and global
249
   * unicast groups.
250
   *
251
   * Note that we skip the node-local group since it
252
   * doesn't make any sense to join that group.  If we
253
   * had an NTP server running on the local node, we
254
   * wouldn't need to have an NTP client get the time
255
   * from it!
256
   */
257
#ifdef CYGHWR_NET_DRIVER_ETH0
258
  // Link-Local
259
  mreq.ipv6mr_multiaddr.s6_addr[1]=0x02;
260
  mreq.ipv6mr_interface = if_nametoindex("eth0");
261
  if (mreq.ipv6mr_interface != 0 ) {
262
    ret = setsockopt(fd6, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq));
263
    CYG_ASSERT(0 == ret, "setsockopt(IPV6_JOIN_GROUP) Link-Local eth0");
264
  }
265
#endif
266
#ifdef CYGHWR_NET_DRIVER_ETH1
267
  // Link-Local
268
  mreq.ipv6mr_multiaddr.s6_addr[1]=0x02;
269
  mreq.ipv6mr_interface = if_nametoindex("eth1");
270
  if (mreq.ipv6mr_interface != 0 ) {
271
    ret = setsockopt(fd6, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq));
272
    CYG_ASSERT(0 == ret, "setsockopt(IPV6_JOIN_GROUP) Link-Local eth1");
273
  }
274
#endif
275
 
276
  // Site-Local
277
  mreq.ipv6mr_multiaddr.s6_addr[1]=0x05;
278
  ret = setsockopt(fd6, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq));
279
  CYG_ASSERT(0 == ret, "setsockopt(IPV6_JOIN_GROUP) Site-Local");
280
 
281
  // Global
282
  mreq.ipv6mr_multiaddr.s6_addr[1]=0x0e;
283
  ret = setsockopt(fd6, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq));
284
  CYG_ASSERT(0 == ret, "setsockopt(IPV6_JOIN_GROUP) Global");
285
 
286
  memset(&local6,0,sizeof(local6));
287
  local6.sin6_family = AF_INET6;
288
  local6.sin6_len = sizeof(local6);
289
  local6.sin6_port = serv->s_port;
290
  local6.sin6_addr = in6addr_any;
291
 
292
  ret = bind(fd6, (struct sockaddr *)&local6,sizeof(local6));
293
  CYG_ASSERT(0 == ret, "Bind6 failed");
294
 
295
  n = (n > fd6 ? n : fd6);
296
#endif
297
 
298
  while (1) {
299
    FD_ZERO(&readfds);
300
    FD_SET(fd,&readfds);
301
#ifdef CYGPKG_NET_INET6
302
    FD_SET(fd6,&readfds);
303
#endif
304
 
305
#ifdef CYGPKG_NET_SNTP_UNICAST
306
    /* By default, we will let select() wait
307
     * for SNTP_WAITPERIOD to receive a packet.  This
308
     * allows us to periodically wake up and check
309
     * if new servers have been configured.  However,
310
     * if we are waiting to send an update request,
311
     * we will set ptimeout to something more
312
     * reasonable below.
313
     */
314
     timeout.tv_sec = SNTP_WAITPERIOD;
315
     timeout.tv_usec = 0;
316
     ptimeout = &timeout;
317
 
318
    /* If we've already set the time, then
319
     * check to see if it's time to try and
320
     * update it.
321
     */
322
    if (NextTimeUpdate != 0)
323
    {
324
        current_time = time(NULL);
325
        if (current_time < NextTimeUpdate)
326
        {
327
            /* Set the select() timeout to wake us
328
             * up when it's time to send more
329
             * requests.
330
             */
331
            timeout.tv_sec = (SNTP_WAITPERIOD > (NextTimeUpdate - current_time)?
332
                                                         (NextTimeUpdate - current_time):SNTP_WAITPERIOD);
333
        } else {
334
            /* It's already time for us to update our time */
335
                        NextTimeUpdate = 0;
336
        }
337
    }
338
 
339
    /* If we need to update our time and we have
340
     * a list of NTP servers, then send out some
341
     * time requests.
342
     */
343
    if (NextTimeUpdate == 0 && (sntp_num_servers > 0))
344
    {
345
        /* Send an NTP request to each NTP server
346
         * in our server list.  Use version 3
347
         * for v3 and v4 compatibility.
348
         */
349
        memset(&ntp_pkt, 0, sizeof(ntp_pkt));
350
        ntp_pkt.Control =
351
                        NTP_LI_NOLEAP |
352
                        NTP_MODE_SET(NTP_MODE_CLIENT) |
353
                        NTP_VERSION_SET(3);
354
 
355
        /* Send a request packet to each of our
356
         * configured servers.
357
         */
358
        cyg_mutex_lock(&sntp_mutex);
359
        for (i = 0; i < sntp_num_servers; i++)
360
        {
361
                /* Send the request packet using the
362
                 * appropriate protocol.
363
                 */
364
            ntp_pkt.TransmitTimestamp.Seconds =
365
                                htonl(TIME_LOCAL_TO_NTP(time(NULL)));
366
            if (sntp_servers[i].sa_family == AF_INET)
367
            {
368
                sendto(fd, &ntp_pkt, sizeof(ntp_pkt), 0,
369
                    &sntp_servers[i], sntp_servers[i].sa_len);
370
#ifdef CYGPKG_NET_INET6
371
            } else if (sntp_servers[i].sa_family == AF_INET6) {
372
                sendto(fd6, &ntp_pkt, sizeof(ntp_pkt), 0,
373
                    &sntp_servers[i], sntp_servers[i].sa_len);
374
#endif
375
            }
376
        }
377
        cyg_mutex_unlock(&sntp_mutex);
378
 
379
        /* Set the NextTimeUpdate so that we don't
380
         * send any more requests until the next
381
         * poll period.  And we've already configured
382
         * the select() timeout above to wait for
383
         * replies.
384
         */
385
        NextTimeUpdate = time(NULL) + SNTP_WAITPERIOD;
386
    }
387
#endif /* CYGPKG_NET_SNTP_UNICAST */
388
 
389
    ret = select(n+1, &readfds, NULL, NULL, ptimeout);
390
    CYG_ASSERT(-1 != ret, "Select");
391
 
392
#ifdef CYGPKG_NET_SNTP_UNICAST
393
    /* If we timed out, then try resending requests */
394
    if (ret == 0)
395
        continue;
396
#endif /* CYGPKG_NET_SNTP_UNICAST */
397
 
398
    len = sizeof(new_srv.addr);
399
    if (FD_ISSET(fd,&readfds)) {
400
      ret=recvfrom(fd,&ntp_pkt,sizeof(ntp_pkt),0,(struct sockaddr *)&new_srv.addr,&len);
401
    }
402
#ifdef CYGPKG_NET_INET6
403
    if (FD_ISSET(fd6,&readfds)) {
404
      ret=recvfrom(fd6,&ntp_pkt,sizeof(ntp_pkt),0,(struct sockaddr *)&new_srv.addr,&len);
405
    }
406
#endif
407
    CYG_ASSERT(0 < ret,"recvfrom");
408
 
409
    /* We expect at least enough bytes to fill the buffer */
410
    if (ret < NTP_PACKET_MINLEN)
411
      continue;
412
 
413
    new_srv.version = NTP_VERSION_GET(&ntp_pkt);
414
    new_srv.stratum = ntp_pkt.Stratum;
415
    new_srv.timestamp = ntohl(ntp_pkt.TransmitTimestamp.Seconds);
416
    mode = NTP_MODE_GET(&ntp_pkt);
417
 
418
    /* Only support protocol versions 3 or 4 */
419
    if (new_srv.version < 3 || new_srv.version > 4) {
420
      CYG_TRACE1(1, "Unsupported version of NTP. Version %d",new_srv.version);
421
      continue;
422
    }
423
 
424
    /* Only process broadcast and server packets */
425
    if (mode != NTP_MODE_BROADCAST && mode != NTP_MODE_SERVER)
426
      continue;
427
 
428
    /* Is the packet from a better server than our current one */
429
    if (is_better(&new_srv,&best_srv)) {
430
      best_srv = new_srv;
431
 
432
      /* Work out the difference between server and our time.
433
       * TODO: Implement RFC2030 recommendations for
434
       * calculating propagation delay between the client
435
       * and server.
436
       */
437
      new_time = TIME_NTP_TO_LOCAL(best_srv.timestamp);
438
      current_time = time(NULL);
439
      diff = current_time - new_time;
440
 
441
      if (diff < 0)
442
          diff = -diff;
443
 
444
      if (diff > 2)
445
          cyg_libc_time_settime(new_time);
446
    }
447
#ifdef CYGPKG_NET_SNTP_UNICAST
448
    NextTimeUpdate = time(NULL) + SNTP_UPDATEPERIOD;
449
#endif
450
  }
451
}
452
 
453
/* Start the SNTP server */
454
void cyg_sntp_start(void) {
455
 
456
  static char sntp_stack[CYGNUM_SNTP_STACK_SIZE];
457
  static cyg_thread sntp_thread_data;
458
  static cyg_handle_t sntp_handle;
459
 
460
  /* Only initialize things once */
461
  if (sntp_initialized)
462
      return;
463
  sntp_initialized = 1;
464
 
465
#ifdef CYGPKG_NET_SNTP_UNICAST
466
  /* Initialize the SNTP mutex */
467
  cyg_mutex_init(&sntp_mutex);
468
#endif
469
 
470
  cyg_thread_create(CYGPKG_NET_THREAD_PRIORITY+1,
471
                    sntp_fn,               // entry
472
                    0,                     // entry parameter
473
                    "SNTP Client",         // Name
474
                    &sntp_stack,           // stack
475
                    sizeof(sntp_stack),    // stack size
476
                    &sntp_handle,          // Handle
477
                    &sntp_thread_data);    // Thread data structure
478
 
479
  cyg_thread_resume(sntp_handle);
480
}
481
 
482
#ifdef CYGPKG_NET_SNTP_UNICAST
483
/*
484
 *      FUNCTION cyg_sntp_set_servers
485
 *
486
 *      DESCRIPTION
487
 *              Sets the list of SNTP/NTP servers to use
488
 *              for SNTP unicast requests.  The list is
489
 *              specified as a list of sockaddr structures
490
 *              and can contain both IPv4 and IPv6
491
 *              addresses and UDP port numbers.
492
 *
493
 *              The server_list array must be maintained
494
 *              by the caller and must not be modified after
495
 *              it is registered by this function.  The
496
 *              array can be unregistered by calling this
497
 *              function again with different parameters.
498
 *
499
 *      NOTE: If cyg_sntp_start() has not been called
500
 *              already, and this function is called with a
501
 *              list of 1 or more servers, then cyg_sntp_start()
502
 *              will be called by this function to start the client.
503
 *
504
 *      PARAMETERS
505
 *              server_list - Array of IPv4 and/or IPv6 sockaddr's
506
 *              num_servers - Number of sockaddr's in array (0 to disable)
507
 *
508
 *      RETURN VALUES
509
 *              None
510
 */
511
void cyg_sntp_set_servers(struct sockaddr *server_list,
512
        cyg_uint32 num_servers)
513
{
514
        /* If we haven't already started the SNTP client, then
515
         * start it now.
516
         */
517
    if (!sntp_initialized)
518
        {
519
                /* If we haven't started already and we don't
520
                 * have a list of servers, then don't start
521
                 * anything up.
522
                 */
523
                if (num_servers == 0)
524
                        return;
525
                cyg_sntp_start();
526
        }
527
 
528
    /* Get the server list mutex */
529
    cyg_mutex_lock(&sntp_mutex);
530
 
531
        /* Record the new server list */
532
        sntp_num_servers = num_servers;
533
        if (num_servers == 0) {
534
                server_list = NULL;
535
        } else {
536
                /* reset the waiting time to force a new update <= SNTP_WAITPERIOD*/
537
                NextTimeUpdate = 0;
538
        }
539
        sntp_servers = server_list;
540
 
541
        /* Free the mutex */
542
    cyg_mutex_unlock(&sntp_mutex);
543
}
544
#endif /* CYGPKG_NET_SNTP_UNICAST */
545
 
546
 
547
 
548
 

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.