ntp.c 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. /*
  2. *
  3. * (C) 2014 David Lettier.
  4. * (C) 2018 Armink (armink.ztl@gmail.com)
  5. *
  6. * http://www.lettier.com/
  7. *
  8. * NTP client.
  9. *
  10. * Compiled with gcc version 4.7.2 20121109 (Red Hat 4.7.2-8) (GCC).
  11. *
  12. * Tested on Linux 3.8.11-200.fc18.x86_64 #1 SMP Wed May 1 19:44:27 UTC 2013 x86_64 x86_64 x86_64 GNU/Linux.
  13. * Tested on RT-Thread 3.0.0+
  14. *
  15. * To compile: $ gcc main.c -o ntpClient.out
  16. *
  17. * Usage: $ ./ntpClient.out
  18. *
  19. */
  20. #include <rtthread.h>
  21. #ifdef PKG_NETUTILS_NTP
  22. #include <stdio.h>
  23. #include <stdlib.h>
  24. #include <string.h>
  25. #include <sys/time.h>
  26. #include <sys/types.h>
  27. #include <sys/socket.h>
  28. #include <sys/select.h>
  29. #include <netinet/in.h>
  30. #include <netdb.h>
  31. #ifdef NETUTILS_NTP_TIMEZONE
  32. #define NTP_TIMEZONE NETUTILS_NTP_TIMEZONE
  33. #endif
  34. #ifdef NETUTILS_NTP_HOSTNAME
  35. #define NTP_HOSTNAME NETUTILS_NTP_HOSTNAME
  36. #endif
  37. #define NTP_TIMESTAMP_DELTA 2208988800ull
  38. #define NTP_GET_TIMEOUT 5
  39. #ifndef NTP_TIMEZONE
  40. #define NTP_TIMEZONE 8
  41. #endif
  42. #ifndef NTP_HOSTNAME
  43. #define NTP_HOSTNAME "cn.ntp.org.cn"
  44. #endif
  45. #define LI(packet) (uint8_t) ((packet.li_vn_mode & 0xC0) >> 6) // (li & 11 000 000) >> 6
  46. #define VN(packet) (uint8_t) ((packet.li_vn_mode & 0x38) >> 3) // (vn & 00 111 000) >> 3
  47. #define MODE(packet) (uint8_t) ((packet.li_vn_mode & 0x07) >> 0) // (mode & 00 000 111) >> 0
  48. // Structure that defines the 48 byte NTP packet protocol.
  49. typedef struct {
  50. uint8_t li_vn_mode; // Eight bits. li, vn, and mode.
  51. // li. Two bits. Leap indicator.
  52. // vn. Three bits. Version number of the protocol.
  53. // mode. Three bits. Client will pick mode 3 for client.
  54. uint8_t stratum; // Eight bits. Stratum level of the local clock.
  55. uint8_t poll; // Eight bits. Maximum interval between successive messages.
  56. uint8_t precision; // Eight bits. Precision of the local clock.
  57. uint32_t rootDelay; // 32 bits. Total round trip delay time.
  58. uint32_t rootDispersion; // 32 bits. Max error aloud from primary clock source.
  59. uint32_t refId; // 32 bits. Reference clock identifier.
  60. uint32_t refTm_s; // 32 bits. Reference time-stamp seconds.
  61. uint32_t refTm_f; // 32 bits. Reference time-stamp fraction of a second.
  62. uint32_t origTm_s; // 32 bits. Originate time-stamp seconds.
  63. uint32_t origTm_f; // 32 bits. Originate time-stamp fraction of a second.
  64. uint32_t rxTm_s; // 32 bits. Received time-stamp seconds.
  65. uint32_t rxTm_f; // 32 bits. Received time-stamp fraction of a second.
  66. uint32_t txTm_s; // 32 bits and the most important field the client cares about. Transmit time-stamp seconds.
  67. uint32_t txTm_f; // 32 bits. Transmit time-stamp fraction of a second.
  68. } ntp_packet; // Total: 384 bits or 48 bytes.
  69. static ntp_packet packet = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
  70. static void ntp_error(char* msg)
  71. {
  72. rt_kprintf("\033[31;22m[E/NTP]: ERROR %s\033[0m\n", msg); // Print the error message to stderr.
  73. }
  74. /**
  75. * Get the UTC time from NTP server
  76. *
  77. * @param host_name NTP server host name, NULL: will using default host name
  78. *
  79. * @note this function is not reentrant
  80. *
  81. * @return >0: success, current UTC time
  82. * =0: get failed
  83. */
  84. time_t ntp_get_time(const char *host_name)
  85. {
  86. int sockfd, n; // Socket file descriptor and the n return result from writing/reading from the socket.
  87. int portno = 123; // NTP UDP port number.
  88. time_t new_time = 0;
  89. fd_set readset;
  90. struct timeval timeout;
  91. // Using default host name when host_name is NULL
  92. if (host_name == NULL)
  93. {
  94. host_name = NTP_HOSTNAME;
  95. }
  96. // Create and zero out the packet. All 48 bytes worth.
  97. memset(&packet, 0, sizeof(ntp_packet));
  98. // Set the first byte's bits to 00,011,011 for li = 0, vn = 3, and mode = 3. The rest will be left set to zero.
  99. *((char *) &packet + 0) = 0x1b; // Represents 27 in base 10 or 00011011 in base 2.
  100. // Create a UDP socket, convert the host-name to an IP address, set the port number,
  101. // connect to the server, send the packet, and then read in the return packet.
  102. struct sockaddr_in serv_addr; // Server address data structure.
  103. struct hostent *server; // Server data structure.
  104. sockfd = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP); // Create a UDP socket.
  105. if (sockfd < 0) {
  106. ntp_error("opening socket");
  107. return 0;
  108. }
  109. server = gethostbyname(host_name); // Convert URL to IP.
  110. if (server == NULL) {
  111. ntp_error("no such host");
  112. goto __exit;
  113. }
  114. // Zero out the server address structure.
  115. memset((char *) &serv_addr, 0, sizeof(serv_addr));
  116. serv_addr.sin_family = AF_INET;
  117. // Copy the server's IP address to the server address structure.
  118. memcpy((char *) &serv_addr.sin_addr.s_addr, (char *) server->h_addr, server->h_length);
  119. // Convert the port number integer to network big-endian style and save it to the server address structure.
  120. serv_addr.sin_port = htons(portno);
  121. // Call up the server using its IP address and port number.
  122. if (connect(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
  123. ntp_error("connecting");
  124. goto __exit;
  125. }
  126. // Send it the NTP packet it wants. If n == -1, it failed.
  127. n = send(sockfd, (char*) &packet, sizeof(ntp_packet), 0);
  128. if (n < 0) {
  129. ntp_error("writing to socket");
  130. goto __exit;
  131. }
  132. timeout.tv_sec = NTP_GET_TIMEOUT;
  133. timeout.tv_usec = 0;
  134. FD_ZERO(&readset);
  135. FD_SET(sockfd, &readset);
  136. if (select(sockfd + 1, &readset, RT_NULL, RT_NULL, &timeout) <= 0) {
  137. ntp_error("select the socket timeout(5s)");
  138. goto __exit;
  139. }
  140. // Wait and receive the packet back from the server. If n == -1, it failed.
  141. n = recv(sockfd, (char*) &packet, sizeof(ntp_packet), 0);
  142. if (n < 0) {
  143. ntp_error("reading from socket");
  144. goto __exit;
  145. }
  146. // These two fields contain the time-stamp seconds as the packet left the NTP server.
  147. // The number of seconds correspond to the seconds passed since 1900.
  148. // ntohl() converts the bit/byte order from the network's to host's "endianness".
  149. packet.txTm_s = ntohl(packet.txTm_s); // Time-stamp seconds.
  150. packet.txTm_f = ntohl(packet.txTm_f); // Time-stamp fraction of a second.
  151. // Extract the 32 bits that represent the time-stamp seconds (since NTP epoch) from when the packet left the server.
  152. // Subtract 70 years worth of seconds from the seconds since 1900.
  153. // This leaves the seconds since the UNIX epoch of 1970.
  154. // (1900)------------------(1970)**************************************(Time Packet Left the Server)
  155. new_time = (time_t) (packet.txTm_s - NTP_TIMESTAMP_DELTA);
  156. __exit:
  157. closesocket(sockfd);
  158. return new_time;
  159. }
  160. /**
  161. * Get the local time from NTP server
  162. *
  163. * @param host_name NTP server host name, NULL: will using default host name
  164. *
  165. * @return >0: success, current local time, offset timezone by NTP_TIMEZONE
  166. * =0: get failed
  167. */
  168. time_t ntp_get_local_time(const char *host_name)
  169. {
  170. time_t cur_time = ntp_get_time(host_name);
  171. if (cur_time)
  172. {
  173. /* add the timezone offset for set_time/set_date */
  174. cur_time += NTP_TIMEZONE * 3600;
  175. }
  176. return cur_time;
  177. }
  178. /**
  179. * Sync current local time to RTC by NTP
  180. *
  181. * @param host_name NTP server host name, NULL: will using default host name
  182. *
  183. * @return >0: success, current local time, offset timezone by NTP_TIMEZONE
  184. * =0: sync failed
  185. */
  186. time_t ntp_sync_to_rtc(const char *host_name)
  187. {
  188. #ifdef RT_USING_RTC
  189. struct tm *cur_tm;
  190. #endif
  191. time_t cur_time = ntp_get_local_time(host_name);
  192. if (cur_time)
  193. {
  194. #ifdef RT_USING_RTC
  195. cur_tm = localtime(&cur_time);
  196. set_time(cur_tm->tm_hour, cur_tm->tm_min, cur_tm->tm_sec);
  197. cur_tm = localtime(&cur_time);
  198. set_date(cur_tm->tm_year + 1900, cur_tm->tm_mon + 1, cur_tm->tm_mday);
  199. #endif /* RT_USING_RTC */
  200. }
  201. return cur_time;
  202. }
  203. static void ntp_sync(const char *host_name)
  204. {
  205. time_t cur_time = ntp_sync_to_rtc(host_name);
  206. if (cur_time)
  207. {
  208. rt_kprintf("Get local time from NTP server: %s", ctime((const time_t*) &cur_time));
  209. #ifdef RT_USING_RTC
  210. rt_kprintf("The system time is updated. Timezone is %d.\n", NTP_TIMEZONE);
  211. #else
  212. rt_kprintf("The system time update failed. Please enable RT_USING_RTC.\n");
  213. #endif /* RT_USING_RTC */
  214. }
  215. }
  216. static void cmd_ntp_sync(int argc, char **argv)
  217. {
  218. char *host_name = NULL;
  219. if (argc > 1)
  220. {
  221. host_name = argv[1];
  222. }
  223. ntp_sync(host_name);
  224. }
  225. #ifdef RT_USING_FINSH
  226. #include <finsh.h>
  227. FINSH_FUNCTION_EXPORT(ntp_sync, Update time by NTP(Network Time Protocol): ntp_sync(host_name));
  228. MSH_CMD_EXPORT_ALIAS(cmd_ntp_sync, ntp_sync, Update time by NTP(Network Time Protocol): ntp_sync [host_name]);
  229. #endif /* RT_USING_FINSH */
  230. #endif /* PKG_NETUTILS_NTP */