Mercurial > ecos
changeset 1266:26196bf9b414
Add unicast support - from Dan Jakubiec
| author | gthomas |
|---|---|
| date | Thu, 02 Oct 2003 22:43:41 +0000 |
| parents | 734edfe6c859 |
| children | b3b9cc536753 |
| files | packages/net/sntp/current/ChangeLog packages/net/sntp/current/cdl/sntp.cdl packages/net/sntp/current/include/sntp.h packages/net/sntp/current/src/sntp.c |
| diffstat | 4 files changed, 320 insertions(+), 46 deletions(-) [+] |
line wrap: on
line diff
--- a/packages/net/sntp/current/ChangeLog +++ b/packages/net/sntp/current/ChangeLog @@ -1,3 +1,9 @@ +2003-09-29 Dan Jakubiec <firstname.lastname@systech.com> + + * src/sntp.c: Added support for SNTP unicast mode. + * src/sntp.h: Added support for SNTP unicast mode. + * src/sntp.cdl: Added support for SNTP unicast mode. + 2003-05-19 Andrew Lunn <andrew.lunn@ascom.ch> * src/sntp.c: Added support for IPv6 multicast NTP packets.
--- a/packages/net/sntp/current/cdl/sntp.cdl +++ b/packages/net/sntp/current/cdl/sntp.cdl @@ -67,6 +67,17 @@ cdl_package CYGPKG_NET_SNTP { flavor none no_define + cdl_option CYGPKG_NET_SNTP_UNICAST { + display "Enable SNTP client unicast support" + flavor bool + default_value 0 + description " + This option enables SNTP unicast mode in + for the SNTP client. This mode will send + SNTP requests to NTP/SNTP servers in + addition to listening for SNTP broadcasts." + } + cdl_option CYGPKG_NET_SNTP_CFLAGS_ADD { display "Additional compiler flags" flavor data
--- a/packages/net/sntp/current/include/sntp.h +++ b/packages/net/sntp/current/include/sntp.h @@ -50,8 +50,12 @@ // //============================================================================= +#include <pkgconf/net_sntp.h> #include <cyg/infra/cyg_type.h> +/* General Definitions */ +#define NTP_UDP_PORT 123 + // Multicast address used by IPv6 #define IN6ADDR_NTP_MULTICAST \ {{{ 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ @@ -61,4 +65,8 @@ __externC void cyg_sntp_start(void); +#ifdef CYGPKG_NET_SNTP_UNICAST +__externC void cyg_sntp_set_servers(struct sockaddr *server_list, cyg_uint32 num_servers); #endif + +#endif
--- a/packages/net/sntp/current/src/sntp.c +++ b/packages/net/sntp/current/src/sntp.c @@ -48,6 +48,8 @@ // //============================================================================= +#include <pkgconf/system.h> +#include <pkgconf/net_sntp.h> #include <network.h> #include <cyg/infra/cyg_type.h> #include <cyg/infra/cyg_ass.h> @@ -55,14 +57,69 @@ #include <cyg/sntp/sntp.h> #include <time.h> -#define VERSION_MASK 0x38000000 -#define VERSION_SHIFT 27 -#define MODE_MASK 0x07000000 -#define MODE_SHIFT 24 -#define STRATUM_MASK 0x00ff0000 -#define STRATUM_SHIFT 16 +/* NTP/SNTPv4 Packet Format (RFC2030) */ +typedef struct +{ + cyg_uint32 Seconds; /* Since 00:00:00 Jan 01 1900 */ + cyg_uint32 Fraction; +} NTP_TIMESTAMP; + +typedef struct +{ + /* Control combines LeapIndicator, Version, and Mode */ + cyg_uint8 Control; + cyg_uint8 Stratum; + cyg_uint8 Poll; + cyg_uint8 Precision; + + cyg_uint32 RootDelay; + cyg_uint32 RootDispersion; + cyg_uint32 ReferenceIdentifier; + + NTP_TIMESTAMP ReferenceTimestamp; + NTP_TIMESTAMP OriginateTimestamp; + NTP_TIMESTAMP ReceiveTimestamp; + NTP_TIMESTAMP TransmitTimestamp; + + cyg_uint32 KeyIdentifier; /* Optional */ + cyg_uint8 MessageDigest[16]; /* Optional */ +} NTP_PACKET; +#define NTP_PACKET_MINLEN 48 /* Packet size - optional fields */ -#define MODE_BROADCAST 5 +/* Leap Indicator Field [Bits 7:6] */ +#define NTP_LI_NOLEAP 0x00 +#define NTP_LI_61SECS 0x40 +#define NTP_LI_59SECS 0x80 +#define NTP_LI_ALARM 0xC0 + +/* Version Field [Bits 5:3] */ +#define NTP_VERSION_GET(pkt) ((((pkt)->Control)>>3)&0x7) +#define NTP_VERSION_SET(ver) (((ver)&0x7)<<3) + +/* Mode Field [Bits 2:0] */ +#define NTP_MODE_RESERVED 0 +#define NTP_MODE_SYMACTIVE 1 /* Symmetric Active */ +#define NTP_MODE_SYMPASSIVE 2 /* Symmetric Passive */ +#define NTP_MODE_CLIENT 3 +#define NTP_MODE_SERVER 4 +#define NTP_MODE_BROADCAST 5 +#define NTP_MODE_NTPCTRL 6 /* Reserved for NTP control message */ +#define NTP_MODE_PRIVATE 7 /* Reserved for private use */ +#define NTP_MODE_GET(pkt) (((pkt)->Control)&0x7) +#define NTP_MODE_SET(mode) ((mode)&0x7) + +/* Time Base Conversion Macros + * + * The NTP timebase is 00:00 Jan 1 1900. The local + * time base is 00:00 Jan 1 1970. Convert between + * these two by added or substracting 70 years + * worth of time. Note that 17 of these years were + * leap years. + */ +#define TIME_BASEDIFF ((((cyg_uint32)70*365 + 17) * 24*3600)) +#define TIME_NTP_TO_LOCAL(t) ((t)-TIME_BASEDIFF) +#define TIME_LOCAL_TO_NTP(t) ((t)+TIME_BASEDIFF) + struct sntp_srv_s { struct sockaddr addr; @@ -70,21 +127,47 @@ struct sntp_srv_s { int version; cyg_uint32 timestamp; }; +static int sntp_initialized = 0; -#define SECONDSPERMINUTE (cyg_uint32)60 -#define SECONDSPERHOUR (cyg_uint32)(SECONDSPERMINUTE * 60) -#define SECONDSPERDAY (cyg_uint32)(SECONDSPERHOUR * 24) -#define SECONDSPERYEAR (cyg_uint32)(SECONDSPERDAY * 365) + +#ifdef CYGPKG_NET_SNTP_UNICAST +/* When using SNTP unicast mode, sntp_servers + * points to an array of char pointers that + * specify NTP server addresses to send + * requests to. sntp_num_servers specifies + * the number of hostnames in the array. + */ +static struct sockaddr *sntp_servers = NULL; +static cyg_uint32 sntp_num_servers = 0; +static cyg_mutex_t sntp_mutex; -/* Convert a SNTP timestamp to a time_t. timestamps are seconds from - 1/1/1900. time_t is 1/1/1970. That equates to 70 years, or which - 17 were leap years.*/ -static time_t timestamp2time(cyg_uint32 timestamp) { - - return timestamp - - (SECONDSPERYEAR * (cyg_uint32)(70)) - - (SECONDSPERDAY * (cyg_uint32)(17)); -} +/* SNTP Timeouts + * + * SNTP_WAITPERIOD is the number of seconds to wait + * before retransmitting unanswered NTP requests + * whenever we are due for an update. + * + * SNTP_UPDATEPERIOD is the number of seconds to wait + * after we get a good time update before we feel + * like we should re-synchronize again with the + * time server. + */ +#define SNTP_WAITPERIOD 10 /* Wait period in seconds */ +#define SNTP_UPDATEPERIOD (30*60) /* Update period in seconds */ + +#endif /* CYKPKG_NET_SNTP_UNICAST */ + +#ifndef CYGNUM_SNTP_STACK_SIZE +/* Use a stack size of at least CYGNUM_SNTP_STACK_SIZE_MIN, but not less than + * CYGNUM_HAL_STACK_SIZE_TYPICAL. + */ +#define CYGNUM_SNTP_STACK_SIZE_MIN 4096 +#if (CYGNUM_HAL_STACK_SIZE_TYPICAL < CYGNUM_SNTP_STACK_SIZE_MIN) +#define CYGNUM_SNTP_STACK_SIZE CYGNUM_SNTP_STACK_SIZE_MIN +#else +#define CYGNUM_SNTP_STACK_SIZE CYGNUM_HAL_STACK_SIZE_TYPICAL +#endif +#endif /* CYGNUM_SNTP_STACK_SIZE */ /* Is the new server better than the current one? If its the same as the current, its always better. If the stratum is lower its better. @@ -99,7 +182,7 @@ static int is_better(struct sntp_srv_s * if (newer->stratum < old->stratum) return 1; if (old->timestamp != 0xffffffff) { - last_time = timestamp2time(old->timestamp); + last_time = TIME_NTP_TO_LOCAL(old->timestamp); diff = time(NULL) - last_time; if (diff > 600) return 1; @@ -111,12 +194,13 @@ static int is_better(struct sntp_srv_s * const struct in6_addr in6addr_ntp_multicast = IN6ADDR_NTP_MULTICAST; -static void sntp_fn(cyg_addrword_t data) { +static void sntp_fn(cyg_addrword_t data) +{ int fd; int ret; struct sockaddr_in local; struct servent *serv; - cyg_uint32 buf[12]; + NTP_PACKET ntp_pkt; struct sntp_srv_s new_srv; struct sntp_srv_s best_srv; int mode; @@ -129,6 +213,12 @@ static void sntp_fn(cyg_addrword_t data) struct ipv6_mreq mreq; struct sockaddr_in6 local6; #endif +#ifdef CYGPKG_NET_SNTP_UNICAST + int i; + struct timeval timeout; + time_t NextTimeUpdate = 0; +#endif /* CYGPKG_NET_SNTP_UNICAST */ + struct timeval *ptimeout = NULL; memset(&best_srv,0xff,sizeof(best_srv)); @@ -155,10 +245,16 @@ static void sntp_fn(cyg_addrword_t data) mreq.ipv6mr_multiaddr = in6addr_ntp_multicast; mreq.ipv6mr_interface = 0; - // Node-Local - ret = setsockopt(fd6, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)); - CYG_ASSERT(0 == ret, "setsockopt(IPV6_JOIN_GROUP) Node-Local"); - + /* Join the well-known NTP multicast groups. We will + * try to join the link-local, site-local, and global + * unicast groups. + * + * Note that we skip the node-local group since it + * doesn't make any sense to join that group. If we + * had an NTP server running on the local node, we + * wouldn't need to have an NTP client get the time + * from it! + */ #ifdef CYGHWR_NET_DRIVER_ETH0 // Link-Local mreq.ipv6mr_multiaddr.s6_addr[1]=0x02; @@ -207,29 +303,117 @@ static void sntp_fn(cyg_addrword_t data) FD_SET(fd6,&readfds); #endif - ret = select(n+1, &readfds,NULL,NULL,NULL); +#ifdef CYGPKG_NET_SNTP_UNICAST + /* By default, we will let select() wait + * for SNTP_WAITPERIOD to receive a packet. This + * allows us to periodically wake up and check + * if new servers have been configured. However, + * if we are waiting to send an update request, + * we will set ptimeout to something more + * reasonable below. + */ + timeout.tv_sec = SNTP_WAITPERIOD; + timeout.tv_usec = 0; + ptimeout = &timeout; + + /* If we've already set the time, then + * check to see if it's time to try and + * update it. + */ + if (NextTimeUpdate != 0) + { + current_time = time(NULL); + if (current_time < NextTimeUpdate) + { + /* Set the select() timeout to wake us + * up when it's time to send more + * requests. + */ + timeout.tv_sec = NextTimeUpdate - current_time; + } else { + /* It's already time for us to update our time */ + NextTimeUpdate = 0; + } + } + + /* If we need to update our time and we have + * a list of NTP servers, then send out some + * time requests. + */ + if (NextTimeUpdate == 0 && (sntp_num_servers > 0)) + { + /* Send an NTP request to each NTP server + * in our server list. Use version 3 + * for v3 and v4 compatibility. + */ + memset(&ntp_pkt, 0, sizeof(ntp_pkt)); + ntp_pkt.Control = + NTP_LI_NOLEAP | + NTP_MODE_SET(NTP_MODE_CLIENT) | + NTP_VERSION_SET(3); + + /* Send a request packet to each of our + * configured servers. + */ + cyg_mutex_lock(&sntp_mutex); + for (i = 0; i < sntp_num_servers; i++) + { + /* Send the request packet using the + * appropriate protocol. + */ + ntp_pkt.TransmitTimestamp.Seconds = + htonl(TIME_LOCAL_TO_NTP(time(NULL))); + if (sntp_servers[i].sa_family == AF_INET) + { + sendto(fd, &ntp_pkt, sizeof(ntp_pkt), 0, + &sntp_servers[i], sntp_servers[i].sa_len); +#ifdef CYGPKG_NET_INET6 + } else if (sntp_servers[i].sa_family == AF_INET6) { + sendto(fd6, &ntp_pkt, sizeof(ntp_pkt), 0, + &sntp_servers[i], sntp_servers[i].sa_len); +#endif + } + } + cyg_mutex_unlock(&sntp_mutex); + + /* Set the NextTimeUpdate so that we don't + * send any more requests until the next + * poll period. And we've already configured + * the select() timeout above to wait for + * replies. + */ + NextTimeUpdate = time(NULL) + SNTP_WAITPERIOD; + } +#endif /* CYGPKG_NET_SNTP_UNICAST */ + + ret = select(n+1, &readfds, NULL, NULL, ptimeout); CYG_ASSERT(-1 != ret, "Select"); +#ifdef CYGPKG_NET_SNTP_UNICAST + /* If we timed out, then try resending requests */ + if (ret == 0) + continue; +#endif /* CYGPKG_NET_SNTP_UNICAST */ + len = sizeof(new_srv.addr); if (FD_ISSET(fd,&readfds)) { - ret=recvfrom(fd,buf,sizeof(buf),0,(struct sockaddr *)&new_srv.addr,&len); + ret=recvfrom(fd,&ntp_pkt,sizeof(ntp_pkt),0,(struct sockaddr *)&new_srv.addr,&len); } #ifdef CYGPKG_NET_INET6 if (FD_ISSET(fd6,&readfds)) { - ret=recvfrom(fd6,buf,sizeof(buf),0,(struct sockaddr *)&new_srv.addr,&len); + ret=recvfrom(fd6,&ntp_pkt,sizeof(ntp_pkt),0,(struct sockaddr *)&new_srv.addr,&len); } #endif CYG_ASSERT(0 < ret,"recvfrom"); - - /* We expect at least enough bytes to fill the buffer. There could - be more if there is a digest, but we ignore that. */ - if (ret < sizeof(buf)) + + /* We expect at least enough bytes to fill the buffer */ + if (ret < NTP_PACKET_MINLEN) continue; - new_srv.version = (htonl(buf[0]) & VERSION_MASK) >> VERSION_SHIFT; - new_srv.stratum = (htonl(buf[0]) & STRATUM_MASK) >> STRATUM_SHIFT; - new_srv.timestamp = htonl(buf[10]); - mode = (htonl(buf[0]) & MODE_MASK) >> MODE_SHIFT; + new_srv.version = NTP_VERSION_GET(&ntp_pkt); + new_srv.stratum = ntp_pkt.Stratum; + new_srv.timestamp = ntohl(ntp_pkt.TransmitTimestamp.Seconds); + mode = NTP_MODE_GET(&ntp_pkt); /* Only support protocol versions 3 or 4 */ if (new_srv.version < 3 || new_srv.version > 4) { @@ -237,39 +421,56 @@ static void sntp_fn(cyg_addrword_t data) continue; } - /* Only process broadcast packet */ - if (mode != MODE_BROADCAST) + /* Only process broadcast and server packets */ + if (mode != NTP_MODE_BROADCAST && mode != NTP_MODE_SERVER) continue; /* Is the packet from a better server than our current one */ if (is_better(&new_srv,&best_srv)) { best_srv = new_srv; - /* Work out the difference between server and our time */ - new_time = timestamp2time(best_srv.timestamp); + /* Work out the difference between server and our time. + * TODO: Implement RFC2030 recommendations for + * calculating propagation delay between the client + * and server. + */ + new_time = TIME_NTP_TO_LOCAL(best_srv.timestamp); current_time = time(NULL); diff = current_time - new_time; if (diff < 0) - diff = -diff; + diff = -diff; if (diff > 2) - cyg_libc_time_settime(new_time); + cyg_libc_time_settime(new_time); } +#ifdef CYGPKG_NET_SNTP_UNICAST + NextTimeUpdate = time(NULL) + SNTP_UPDATEPERIOD; +#endif } } /* Start the SNTP server */ void cyg_sntp_start(void) { - static char sntp_stack[CYGNUM_HAL_STACK_SIZE_TYPICAL]; + static char sntp_stack[CYGNUM_SNTP_STACK_SIZE]; static cyg_thread sntp_thread_data; static cyg_handle_t sntp_handle; + /* Only initialize things once */ + if (sntp_initialized) + return; + sntp_initialized = 1; + +#ifdef CYGPKG_NET_SNTP_UNICAST + /* Initialize the SNTP mutex */ + cyg_mutex_init(&sntp_mutex); +#endif + cyg_thread_create(CYGPKG_NET_THREAD_PRIORITY+1, sntp_fn, // entry 0, // entry parameter - "sntp", // Name + "SNTP Client", // Name &sntp_stack, // stack sizeof(sntp_stack), // stack size &sntp_handle, // Handle @@ -277,3 +478,51 @@ void cyg_sntp_start(void) { cyg_thread_resume(sntp_handle); } + +#ifdef CYGPKG_NET_SNTP_UNICAST + +/* + * FUNCTION cyg_sntp_set_servers + * + * DESCRIPTION + * Sets the list of SNTP/NTP servers to use + * for SNTP unicast requests. The list is + * specified as a list of sockaddr structures + * and can contain both IPv4 and IPv6 + * addresses and UDP port numbers. + * + * The server_list array must be maintained + * by the caller and must not be modified after + * it is registered by this function. The + * array can be unregistered by calling this + * function again with different parameters. + * + * NOTE: This function must be called AFTER + * cyg_sntp_start(). + * + * PARAMETERS + * server_list - Array of IPv4 and/or IPv6 sockaddr's + * num_servers - Number of sockaddr's in array (0 to disable) + * + * RETURN VALUES + * None + */ +void cyg_sntp_set_servers(struct sockaddr *server_list, + cyg_uint32 num_servers) +{ + /* Get the server list mutex */ + if (!sntp_initialized) + return; + cyg_mutex_lock(&sntp_mutex); + + /* Record the new server list */ + sntp_num_servers = num_servers; + if (num_servers == 0) + server_list = NULL; + sntp_servers = server_list; + + /* Free the mutex */ + cyg_mutex_unlock(&sntp_mutex); +} +#endif /* CYGPKG_NET_SNTP_UNICAST */ +
