1 | /* |
---|
2 | * Synchronize with an NTP server |
---|
3 | * |
---|
4 | * This program may be distributed and used for any purpose. |
---|
5 | * I ask only that you: |
---|
6 | * 1. Leave this author information intact. |
---|
7 | * 2. Document any changes you make. |
---|
8 | * |
---|
9 | * W. Eric Norum |
---|
10 | * Canadian Light Source |
---|
11 | * University of Saskatchewan |
---|
12 | * Saskatoon, Saskatchewan, CANADA |
---|
13 | * eric@cls.usask.ca |
---|
14 | * |
---|
15 | * $Id$ |
---|
16 | */ |
---|
17 | |
---|
18 | #include <stdio.h> |
---|
19 | #include <string.h> |
---|
20 | #include <errno.h> |
---|
21 | #include <time.h> |
---|
22 | #include <limits.h> |
---|
23 | #include <rtems.h> |
---|
24 | #include <rtems/rtems_bsdnet.h> |
---|
25 | #include <rtems/error.h> |
---|
26 | #include <sys/types.h> |
---|
27 | #include <sys/socket.h> |
---|
28 | #include <netinet/in.h> |
---|
29 | |
---|
30 | /* |
---|
31 | * RTEMS base: 1988, January 1 |
---|
32 | * UNIX base: 1970, January 1 |
---|
33 | * NTP base: 1900, January 1 |
---|
34 | */ |
---|
35 | #define UNIX_BASE_TO_NTP_BASE (((70UL*365UL)+17UL) * (24*60*60)) |
---|
36 | |
---|
37 | struct timestamp { |
---|
38 | rtems_unsigned32 integer; |
---|
39 | rtems_unsigned32 fraction; |
---|
40 | }; |
---|
41 | |
---|
42 | struct ntpPacketSmall { |
---|
43 | rtems_unsigned8 li_vn_mode; |
---|
44 | rtems_unsigned8 stratum; |
---|
45 | rtems_signed8 poll_interval; |
---|
46 | rtems_signed8 precision; |
---|
47 | rtems_signed32 root_delay; |
---|
48 | rtems_signed32 root_dispersion; |
---|
49 | char reference_identifier[4]; |
---|
50 | struct timestamp reference_timestamp; |
---|
51 | struct timestamp originate_timestamp; |
---|
52 | struct timestamp receive_timestamp; |
---|
53 | struct timestamp transmit_timestamp; |
---|
54 | }; |
---|
55 | |
---|
56 | struct ntpPacket { |
---|
57 | struct ntpPacketSmall ntp; |
---|
58 | char authenticator[96]; |
---|
59 | }; |
---|
60 | |
---|
61 | static int |
---|
62 | processPacket (struct ntpPacketSmall *p) |
---|
63 | { |
---|
64 | time_t tbuf; |
---|
65 | struct tm *lt; |
---|
66 | rtems_time_of_day rt; |
---|
67 | rtems_interval ticks_per_second; |
---|
68 | |
---|
69 | if (((p->li_vn_mode & (0x7 << 3)) != (3 << 3)) |
---|
70 | || ((p->transmit_timestamp.integer == 0) && (p->transmit_timestamp.fraction == 0))) |
---|
71 | return 0; |
---|
72 | rtems_clock_get (RTEMS_CLOCK_GET_TICKS_PER_SECOND, &ticks_per_second); |
---|
73 | tbuf = ntohl (p->transmit_timestamp.integer) - UNIX_BASE_TO_NTP_BASE - rtems_bsdnet_timeoffset; |
---|
74 | lt = gmtime (&tbuf); |
---|
75 | rt.year = lt->tm_year + 1900; |
---|
76 | rt.month = lt->tm_mon + 1; |
---|
77 | rt.day = lt->tm_mday; |
---|
78 | rt.hour = lt->tm_hour; |
---|
79 | rt.minute = lt->tm_min; |
---|
80 | rt.second = lt->tm_sec; |
---|
81 | rt.ticks = ntohl (p->transmit_timestamp.fraction) / (ULONG_MAX / ticks_per_second); |
---|
82 | if (rt.ticks >= ticks_per_second) |
---|
83 | rt.ticks = ticks_per_second - 1; |
---|
84 | rtems_clock_set (&rt); |
---|
85 | return 1; |
---|
86 | } |
---|
87 | |
---|
88 | int |
---|
89 | rtems_bsdnet_synchronize_ntp (int interval, rtems_task_priority priority) |
---|
90 | { |
---|
91 | int s; |
---|
92 | int i; |
---|
93 | static struct sockaddr_in myAddr, farAddr; |
---|
94 | int fromlen; |
---|
95 | struct ntpPacketSmall packet; |
---|
96 | struct timeval tv; |
---|
97 | |
---|
98 | if (interval != 0) { |
---|
99 | printf ("Daemon-mode note yet supported.\n"); |
---|
100 | errno = EINVAL; |
---|
101 | return -1; |
---|
102 | } |
---|
103 | s = socket (AF_INET, SOCK_DGRAM, 0); |
---|
104 | if (s < 0) { |
---|
105 | printf ("Can't create socket: %s\n", strerror (errno)); |
---|
106 | return -1; |
---|
107 | } |
---|
108 | tv.tv_sec = 5; |
---|
109 | tv.tv_usec = 0; |
---|
110 | if (setsockopt (s, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof tv) < 0) { |
---|
111 | printf ("Can't set socket receive timeout: %s", strerror (errno)); |
---|
112 | return -1; |
---|
113 | } |
---|
114 | memset (&myAddr, 0, sizeof myAddr); |
---|
115 | myAddr.sin_family = AF_INET; |
---|
116 | myAddr.sin_port = htons (123); |
---|
117 | myAddr.sin_addr.s_addr = htonl (INADDR_ANY); |
---|
118 | if (bind (s, (struct sockaddr *)&myAddr, sizeof myAddr) < 0) { |
---|
119 | printf ("Can't bind socket: %s\n", strerror (errno)); |
---|
120 | return -1; |
---|
121 | } |
---|
122 | for (;;) { |
---|
123 | /* |
---|
124 | * If there's no server we just have to wait |
---|
125 | * and hope that there's an NTP broadcast |
---|
126 | * server out there somewhere. |
---|
127 | */ |
---|
128 | if (rtems_bsdnet_ntpserver_count > 0) { |
---|
129 | memset (&farAddr, 0, sizeof farAddr); |
---|
130 | farAddr.sin_family = AF_INET; |
---|
131 | farAddr.sin_port = htons (123); |
---|
132 | /* |
---|
133 | * For now, try only the first server. |
---|
134 | */ |
---|
135 | farAddr.sin_addr = rtems_bsdnet_ntpserver[0]; |
---|
136 | memset (&packet, 0, sizeof packet); |
---|
137 | packet.li_vn_mode = (3 << 3) | 3; /* NTP version 3, client */ |
---|
138 | i = sendto (s, &packet, sizeof packet, 0, (struct sockaddr *)&farAddr, sizeof farAddr); |
---|
139 | if (i != sizeof packet) { |
---|
140 | printf ("Can't send: %s\n", strerror (errno)); |
---|
141 | return -1; |
---|
142 | } |
---|
143 | } |
---|
144 | fromlen = sizeof farAddr; |
---|
145 | i = recvfrom (s, &packet, sizeof packet, 0, (struct sockaddr *)&farAddr, &fromlen); |
---|
146 | if (i == 0) |
---|
147 | rtems_panic ("EOF"); |
---|
148 | if (i < 0) { |
---|
149 | if ((errno == EWOULDBLOCK) || (errno == EAGAIN)) |
---|
150 | continue; |
---|
151 | rtems_panic ("Can't receive: %s", strerror (errno)); |
---|
152 | } |
---|
153 | if (i >= sizeof packet) { |
---|
154 | if (processPacket (&packet)) |
---|
155 | return 0; |
---|
156 | } |
---|
157 | } |
---|
158 | } |
---|