sntpcLib.c
上传用户:nvosite88
上传日期:2007-01-17
资源大小:4983k
文件大小:16k
源码类别:

VxWorks

开发平台:

C/C++

  1. /* sntpcLib.c - Simple Network Time Protocol (SNTP) client library */
  2. /* Copyright 1984-2002 Wind River Systems, Inc. */
  3. #include "copyright_wrs.h"
  4. /*
  5. modification history 
  6. --------------------
  7. 01k,07jan02,vvv  doc: added errnos for sntpcTimeGet and sntpcFetch (SPR #71557)
  8. 01j,16mar99,spm  doc: removed references to configAll.h (SPR #25663)
  9. 01e,14dec97,jdi  doc: cleanup.
  10. 01d,10dec97,kbw  making man page changes
  11. 01c,27aug97,spm  corrections for man page generation
  12. 01b,15jul97,spm  code cleanup, documentation, and integration; entered in
  13.                  source code control
  14. 01a,24may97,kyc  written
  15. */
  16. /* 
  17. DESCRIPTION
  18. This library implements the client side of the Simple Network Time 
  19. Protocol (SNTP), a protocol that allows a system to maintain the 
  20. accuracy of its internal clock based on time values reported by one 
  21. or more remote sources.  The library is included in the VxWorks image 
  22. if INCLUDE_SNTPC is defined at the time the image is built.
  23. USER INTERFACE
  24. The sntpcTimeGet() routine retrieves the time reported by a remote source and
  25. converts that value for POSIX-compliant clocks.  The routine will either send a 
  26. request and extract the time from the reply, or it will wait until a message is
  27. received from an SNTP/NTP server executing in broadcast mode.
  28. INCLUDE FILES: sntpcLib.h
  29. SEE ALSO: clockLib, RFC 1769
  30. */
  31. /* includes */
  32. #include "vxWorks.h"
  33. #include "sysLib.h"
  34. #include "ioLib.h"
  35. #include "inetLib.h"
  36. #include "hostLib.h"
  37. #include "sockLib.h"
  38. #include "errnoLib.h"
  39. #include "sntpcLib.h"
  40. /* globals */
  41. u_short sntpcPort;
  42. /* forward declarations */
  43. LOCAL STATUS sntpcListen (u_int, struct timespec *);
  44. LOCAL STATUS sntpcFetch (struct in_addr *, u_int, struct timespec *);
  45. /*******************************************************************************
  46. *
  47. * sntpcInit - set up the SNTP client
  48. *
  49. * This routine is called to link the SNTP client module into the VxWorks
  50. * image. It assigns the UDP source and destination port according to the
  51. * corresponding SNTP_PORT setting.
  52. * RETURNS: OK, always.
  53. *
  54. * ERRNO: N/A
  55. *
  56. * NOMANUAL
  57. */
  58. STATUS sntpcInit
  59.     (
  60.     u_short  port  /* UDP source/destination port */
  61.     )
  62.     {
  63.     sntpcPort = htons (port);
  64.     return (OK);
  65.     }
  66. /*******************************************************************************
  67. *
  68. * sntpcFractionToNsec - convert time from the NTP format to POSIX time format
  69. *
  70. * This routine converts the fractional part of the NTP timestamp format to a 
  71. * value in nanoseconds compliant with the POSIX clock.  While the NTP time 
  72. * format provides a precision of about 200 pico-seconds, rounding error in the 
  73. * conversion routine reduces the precision to tenths of a micro-second.
  74. * RETURNS: Value for struct timespec corresponding to NTP fractional part
  75. *
  76. * ERRNO:   N/A
  77. *
  78. * INTERNAL
  79. *
  80. * Floating-point calculations can't be used because some boards (notably
  81. * the SPARC architectures) disable software floating point by default to 
  82. * speed up context switching. These boards abort with an exception when
  83. * floating point operations are encountered.
  84. *
  85. * NOMANUAL
  86. */
  87. LOCAL ULONG sntpcFractionToNsec
  88.     (
  89.     ULONG sntpFraction      /* base 2 fractional part of the NTP timestamp */
  90.     )
  91.     {
  92.     ULONG factor = 0x8AC72305; /* Conversion factor from base 2 to base 10 */
  93.     ULONG divisor = 10;        /* Initial exponent for mantissa. */
  94.     ULONG mask = 1000000000;   /* Pulls digits of factor from left to right. */
  95.     int loop;
  96.     ULONG nsec = 0;
  97.     BOOL shift = FALSE;        /* Shifted to avoid overflow? */
  98.     /* 
  99.      * Adjust large values so that no intermediate calculation exceeds 
  100.      * 32 bits. (This test is overkill, since the fourth MSB can be set 
  101.      * sometimes, but it's fast).
  102.      */
  103.  
  104.     if (sntpFraction & 0xF0000000)
  105.         {
  106.         sntpFraction /= 10;
  107.         shift = TRUE;
  108.         }
  109.     /* 
  110.      * In order to increase portability, the following conversion avoids
  111.      * floating point operations, so it is somewhat obscure.
  112.      *
  113.      * Incrementing the NTP fractional part increases the corresponding
  114.      * decimal value by 2^(-32). By interpreting the fractional part as an
  115.      * integer representing the number of increments, the equivalent decimal
  116.      * value is equal to the product of the fractional part and 0.2328306437.
  117.      * That value is the mantissa for 2^(-32). Multiplying by 2.328306437E-10
  118.      * would convert the NTP fractional part into the equivalent in seconds.
  119.      *
  120.      * The mask variable selects each digit from the factor sequentially, and
  121.      * the divisor shifts the digit the appropriate number of decimal places. 
  122.      * The initial value of the divisor is 10 instead of 1E10 so that the 
  123.      * conversion produces results in nanoseconds, as required by POSIX clocks.
  124.      */
  125.     for (loop = 0; loop < 10; loop++)    /* Ten digits in mantissa */
  126.         {
  127. nsec += sntpFraction * (factor/mask)/divisor;  /* Use current digit. */
  128. factor %= mask;    /* Remove most significant digit from the factor. */
  129. mask /= 10;        /* Reduce length of mask by one. */
  130. divisor *= 10;     /* Increase preceding zeroes by one. */
  131.         }
  132.     /* Scale result upwards if value was adjusted before processing. */
  133.     if (shift)
  134.         nsec *= 10;
  135.     return (nsec);
  136.     }
  137. /*******************************************************************************
  138. *
  139. * sntpcTimeGet - retrieve the current time from a remote source
  140. *
  141. * This routine stores the current time as reported by an SNTP/NTP server in
  142. * the location indicated by <pCurrTime>.  The reported time is first converted
  143. * to the elapsed time since January 1, 1970, 00:00, GMT, which is the base value
  144. * used by UNIX systems.  If <pServerAddr> is NULL, the routine listens for 
  145. * messages sent by an SNTP/NTP server in broadcast mode.  Otherwise, this
  146. * routine sends a request to the specified SNTP/NTP server and extracts the
  147. * reported time from the reply.  In either case, an error is returned if no 
  148. * message is received within the interval specified by <timeout>.  Typically, 
  149. * SNTP/NTP servers operating in broadcast mode send update messages every 64 
  150. * to 1024 seconds.  An infinite timeout value is specified by WAIT_FOREVER.
  151. * RETURNS: OK, or ERROR if unsuccessful.
  152. *
  153. * ERRNO:
  154. *  S_sntpcLib_INVALID_PARAMETER, S_sntpcLib_INVALID_ADDRESS, S_sntpcLib_TIMEOUT,
  155. *  S_sntpcLib_SERVER_UNSYNC, S_sntpcLib_VERSION_UNSUPPORTED
  156. */
  157. STATUS sntpcTimeGet
  158.     (
  159.     char *  pServerAddr,  /* server IP address or hostname */
  160.     u_int  timeout, /* timeout interval in ticks */
  161.     struct timespec *  pCurrTime /* storage for retrieved time value */
  162.     )
  163.     {
  164.     STATUS result;
  165.     struct in_addr  target;
  166.     if (pCurrTime == NULL || (timeout < 0 && timeout != WAIT_FOREVER))
  167.         {
  168.         errnoSet (S_sntpcLib_INVALID_PARAMETER);
  169.         return (ERROR);
  170.         }
  171.     if (pServerAddr == NULL)
  172.         result = sntpcListen (timeout, pCurrTime);
  173.     else
  174.         {
  175.         target.s_addr = hostGetByName (pServerAddr);
  176.         if (target.s_addr == ERROR)
  177.             target.s_addr = inet_addr (pServerAddr);
  178.     
  179.         if (target.s_addr == ERROR)
  180.             {
  181.             errnoSet (S_sntpcLib_INVALID_ADDRESS);
  182.             return (ERROR);
  183.             }
  184.         result = sntpcFetch (&target, timeout, pCurrTime); 
  185.         }
  186.     return (result);
  187.     }
  188. /*******************************************************************************
  189. *
  190. * sntpcFetch - send an SNTP request and retrieve the time from the reply
  191. *
  192. * This routine sends an SNTP request to the IP address specified by
  193. * <pTargetAddr>, converts the returned NTP timestamp to the POSIX-compliant 
  194. * clock format with the UNIX base value (elapsed time since 00:00 GMT on 
  195. * Jan. 1, 1970), and stores the result in the location indicated by <pTime>.
  196. * RETURNS: OK, or ERROR if unsuccessful.
  197. *
  198. * ERRNO:
  199. *  S_sntpcLib_SERVER_UNSYNC
  200. *  S_sntpcLib_VERSION_UNSUPPORTED
  201. *  S_sntpcLib_TIMEOUT
  202. *
  203. * NOMANUAL
  204. */
  205. LOCAL STATUS sntpcFetch
  206.     (
  207.     struct in_addr *  pTargetAddr,  /* SNTP/NTP server IP address */
  208.     u_int  timeout, /* timeout in ticks */
  209.     struct timespec *  pCurrTime /* storage for retrieved time value */
  210.     )
  211.     {
  212.     SNTP_PACKET sntpRequest;     /* sntp request packet for */
  213.                                  /* transmission to server */
  214.     SNTP_PACKET sntpReply;       /* buffer for server reply */
  215.     struct sockaddr_in dstAddr;
  216.     struct sockaddr_in servAddr;
  217.     struct timeval sockTimeout;
  218.     int optval;
  219.     int clockRate;
  220.     int sntpSocket;
  221.     fd_set readFds;
  222.     int result;
  223.     int servAddrLen;
  224.   
  225.     /* Set destination for request. */
  226.   
  227.     bzero ( (char *)&dstAddr, sizeof (dstAddr));
  228.     dstAddr.sin_addr.s_addr = pTargetAddr->s_addr;
  229.     dstAddr.sin_family = AF_INET;
  230.     dstAddr.sin_port = sntpcPort;
  231.   
  232.     /* Create socket for transmission. */
  233.   
  234.     sntpSocket = socket (AF_INET, SOCK_DGRAM, 0);
  235.     if (sntpSocket == -1) 
  236.         return (ERROR);
  237.     /* 
  238.      * Enable broadcast option for socket in case that address is given. 
  239.      * This use of the SNTP client is not likely, so ignore errors. If 
  240.      * the broadcast address is used, and this call fails, the error will
  241.      * be caught by sendto() below.
  242.      */
  243.     optval = 1;
  244.     result = setsockopt (sntpSocket, SOL_SOCKET, SO_BROADCAST, 
  245.                          (char *)&optval, sizeof (optval));
  246.     /* Initialize SNTP message buffers. */
  247.   
  248.     bzero ( (char *)&sntpRequest, sizeof (sntpRequest));
  249.     bzero ( (char *)&sntpReply, sizeof (sntpReply));
  250.   
  251.     sntpRequest.leapVerMode = SNTP_CLIENT_REQUEST;
  252.   
  253.     bzero ( (char *) &servAddr, sizeof (servAddr));
  254.     servAddrLen = sizeof (servAddr);
  255.   
  256.     /* Transmit SNTP request. */
  257.   
  258.     if (sendto (sntpSocket, (caddr_t)&sntpRequest, sizeof(sntpRequest), 0,
  259.                 (struct sockaddr *)&dstAddr, sizeof (dstAddr)) == -1) 
  260.         {
  261.         close (sntpSocket);
  262.         return (ERROR);
  263.         }
  264.     
  265.     /* Convert timeout value to format needed by select() call. */
  266.     if (timeout != WAIT_FOREVER)
  267.         {
  268.         clockRate = sysClkRateGet ();
  269.         sockTimeout.tv_sec = timeout / clockRate;
  270.         sockTimeout.tv_usec = (1000000 * timeout % clockRate) / clockRate;
  271.         }
  272.     /* Wait for reply at the ephemeral port selected by the sendto () call. */
  273.     FD_ZERO (&readFds);
  274.     FD_SET (sntpSocket, &readFds);
  275.     if (timeout == WAIT_FOREVER)
  276.         result = select (FD_SETSIZE, &readFds, NULL, NULL, NULL);
  277.     else
  278.         result = select (FD_SETSIZE, &readFds, NULL, NULL, &sockTimeout);
  279.     if (result == -1) 
  280.         {
  281.         close (sntpSocket);
  282.         return (ERROR);
  283.         }
  284.     if (result == 0)    /* Timeout interval expired. */
  285.         {
  286.         close (sntpSocket); 
  287.         errnoSet (S_sntpcLib_TIMEOUT);
  288.         return (ERROR);
  289.         }
  290.     result = recvfrom (sntpSocket, (caddr_t)&sntpReply, sizeof (sntpReply),
  291.                        0, (struct sockaddr *)&servAddr, &servAddrLen);
  292.     if (result == -1) 
  293.         {
  294.         close (sntpSocket);
  295.         return (ERROR);   
  296.         }
  297.     close (sntpSocket);
  298.     /*
  299.      * Return error if the server clock is unsynchronized, or the version is 
  300.      * not supported.
  301.      */
  302.     if ( (sntpReply.leapVerMode & SNTP_LI_MASK) == SNTP_LI_3 ||
  303.         sntpReply.transmitTimestampSec == 0)
  304.         {
  305.         errnoSet (S_sntpcLib_SERVER_UNSYNC);
  306.         return (ERROR);
  307.         }
  308.     if ( (sntpReply.leapVerMode & SNTP_VN_MASK) == SNTP_VN_0 ||
  309.         (sntpReply.leapVerMode & SNTP_VN_MASK) > SNTP_VN_3)
  310.         {
  311.         errnoSet (S_sntpcLib_VERSION_UNSUPPORTED);
  312. return (ERROR);
  313.         }
  314.     /* Convert the NTP timestamp to the correct format and store in clock. */
  315.     /* Add test for 2036 base value here! */
  316.     sntpReply.transmitTimestampSec = ntohl (sntpReply.transmitTimestampSec) - 
  317.                                      SNTP_UNIX_OFFSET;
  318.     /* 
  319.      * Adjust returned value if leap seconds are present. 
  320.      * This needs work! 
  321.      */ 
  322.     /* if ( (sntpReply.leapVerMode & SNTP_LI_MASK) == SNTP_LI_1)
  323.             sntpReply.transmitTimestampSec += 1;
  324.      else if ((sntpReply.leapVerMode & SNTP_LI_MASK) == SNTP_LI_2)
  325.               sntpReply.transmitTimestampSec -= 1;
  326.     */
  327.     sntpReply.transmitTimestampFrac = ntohl (sntpReply.transmitTimestampFrac);
  328.     pCurrTime->tv_sec = sntpReply.transmitTimestampSec;
  329.     pCurrTime->tv_nsec = sntpcFractionToNsec (sntpReply.transmitTimestampFrac);
  330.     return (OK);
  331.     }
  332. /*******************************************************************************
  333. *
  334. * sntpcListen - retrieve the time from an SNTP/NTP broadcast
  335. *
  336. * This routine listens to the SNTP/NTP port for a valid message from any 
  337. * SNTP/NTP server executing in broadcast mode, converts the returned NTP 
  338. * timestamp to the POSIX-compliant clock format with the UNIX base value
  339. * (elapsed time since 00:00 GMT on January 1, 1970), and stores the result in 
  340. * the location indicated by <pTime>.
  341. * RETURNS: OK, or ERROR if unsuccessful.
  342. *
  343. * ERRNO:
  344. *  S_sntpcLib_TIMEOUT
  345. *
  346. * NOMANUAL
  347. */
  348. LOCAL STATUS sntpcListen
  349.     (
  350.     u_int  timeout, /* timeout in ticks */
  351.     struct timespec *  pCurrTime /* storage for retrieved time value */
  352.     )
  353.     {
  354.     SNTP_PACKET sntpMessage;    /* buffer for message from server */
  355.     struct sockaddr_in srcAddr;
  356.     int sntpSocket;
  357.     struct timeval sockTimeout;
  358.     int clockRate;
  359.     fd_set readFds;
  360.     int result;
  361.     int srcAddrLen;
  362.  
  363.     /* Initialize source address. */
  364.     bzero ( (char *)&srcAddr, sizeof (srcAddr));
  365.     srcAddr.sin_addr.s_addr = INADDR_ANY;
  366.     srcAddr.sin_family = AF_INET;
  367.     srcAddr.sin_port = sntpcPort;
  368.     /* Create socket for listening. */
  369.   
  370.     sntpSocket = socket (AF_INET, SOCK_DGRAM, 0);
  371.     if (sntpSocket == -1) 
  372.         return (ERROR);
  373.       
  374.     result = bind (sntpSocket, (struct sockaddr *)&srcAddr, sizeof (srcAddr));
  375.     if (result == -1) 
  376.         {
  377.         close (sntpSocket);
  378.         return (ERROR);
  379.         }
  380.     /* Convert timeout value to format needed by select() call. */
  381.     if (timeout != WAIT_FOREVER)
  382.         {
  383.         clockRate = sysClkRateGet ();
  384.         sockTimeout.tv_sec = timeout / clockRate;
  385.         sockTimeout.tv_usec = (1000000 * timeout % clockRate) / clockRate;
  386.         }
  387.     /* Wait for broadcast message from server. */
  388.     FD_ZERO (&readFds);
  389.     FD_SET (sntpSocket, &readFds);
  390.       
  391.     if (timeout == WAIT_FOREVER)
  392.         result = select (FD_SETSIZE, &readFds, NULL, NULL, NULL);
  393.     else
  394.         result = select (FD_SETSIZE, &readFds, NULL, NULL, &sockTimeout);
  395.     if (result == -1)
  396.         {
  397.         close (sntpSocket);
  398.         errnoSet (S_sntpcLib_TIMEOUT);
  399.         return (ERROR);
  400.         }
  401.     if (result == 0)    /* Timeout interval expired. */
  402.         {
  403.         close (sntpSocket);
  404.         errnoSet (S_sntpcLib_TIMEOUT);
  405.         return (ERROR);
  406.         }
  407.     result = recvfrom (sntpSocket, (caddr_t) &sntpMessage, sizeof(sntpMessage),
  408.                        0, (struct sockaddr *) &srcAddr, &srcAddrLen);
  409.     if (result == -1) 
  410.         {
  411.         close (sntpSocket);
  412.         return (ERROR);
  413.         }
  414.     close (sntpSocket);
  415.     /*
  416.      * Return error if the server clock is unsynchronized, or the version is 
  417.      * not supported.
  418.      */
  419.     if ( (sntpMessage.leapVerMode & SNTP_LI_MASK) == SNTP_LI_3 ||
  420.         sntpMessage.transmitTimestampSec == 0)
  421.         {
  422.         errnoSet (S_sntpcLib_SERVER_UNSYNC);
  423.         return (ERROR);
  424.         }
  425.     if ( (sntpMessage.leapVerMode & SNTP_VN_MASK) == SNTP_VN_0 ||
  426.         (sntpMessage.leapVerMode & SNTP_VN_MASK) > SNTP_VN_3)
  427.         {
  428.         errnoSet (S_sntpcLib_VERSION_UNSUPPORTED);
  429.         return (ERROR);
  430.         }
  431.     /* Convert the NTP timestamp to the correct format and store in clock. */
  432.     /* Add test for 2036 base value here! */
  433.     sntpMessage.transmitTimestampSec = 
  434.                                      ntohl (sntpMessage.transmitTimestampSec) -
  435.                                      SNTP_UNIX_OFFSET;
  436.     /*
  437.      * Adjust returned value if leap seconds are present.
  438.      * This needs work!
  439.      */
  440.     /* if ( (sntpReply.leapVerMode & SNTP_LI_MASK) == SNTP_LI_1)
  441.             sntpReply.transmitTimestampSec += 1;
  442.      else if ((sntpReply.leapVerMode & SNTP_LI_MASK) == SNTP_LI_2)
  443.               sntpReply.transmitTimestampSec -= 1;
  444.     */
  445.     sntpMessage.transmitTimestampFrac = 
  446.                                      ntohl (sntpMessage.transmitTimestampFrac);
  447.     pCurrTime->tv_sec = sntpMessage.transmitTimestampSec;
  448.     pCurrTime->tv_nsec =
  449.                        sntpcFractionToNsec (sntpMessage.transmitTimestampFrac);
  450.     return (OK);
  451.     }