It has passed the test using the system clock synchronization of
Windows XP
. For testing and debugging, make sure to turn off
the clock synchronization with the host system, when the client operating
system is running in a virtual machine.
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 #include <stdint.h> 5 #include <unistd.h> 6 #include <signal.h> 7 #include <sys/time.h> /* gettimeofday() */ 8 #include <sys/types.h> 9 #include <sys/wait.h> 10 #include <sys/socket.h> 11 #include <netdb.h> 12 #include <netinet/in.h> 13 #include <arpa/inet.h> 14 15 #include <time.h> /* for time() and ctime() */ 16 17 #define UTC_NTP 2208988800U /* 1970 - 1900 */ 18 19 /* get Timestamp for NTP in LOCAL ENDIAN */ 20 void gettime64(uint32_t ts[]) 21 { 22 struct timeval tv; 23 gettimeofday(&tv, NULL); 24 25 ts[0] = tv.tv_sec + UTC_NTP; 26 ts[1] = (4294*(tv.tv_usec)) + ((1981*(tv.tv_usec))>>11); 27 } 28 29 30 int die(const char *msg) 31 { 32 if (msg) { 33 fputs(msg, stderr); 34 } 35 exit(-1); 36 } 37 38 39 void log_request_arrive(uint32_t *ntp_time) 40 { 41 time_t t; 42 43 if (ntp_time) { 44 t = *ntp_time - UTC_NTP; 45 } else { 46 t = time(NULL); 47 } 48 printf("A request comes at: %s", ctime(&t)); 49 } 50 51 52 void log_ntp_event(char *msg) 53 { 54 puts(msg); 55 } 56 57 58 int ntp_reply( 59 int socket_fd, 60 struct sockaddr *saddr_p, 61 socklen_t saddrlen, 62 unsigned char recv_buf[], 63 uint32_t recv_time[]) 64 { 65 /* Assume that recv_time is in local endian ! */ 66 unsigned char send_buf[48]; 67 uint32_t *u32p; 68 69 /* do not use 0xC7 because the LI can be `unsynchronized` */ 70 if ((recv_buf[0] & 0x07/*0xC7*/) != 0x3) { 71 /* LI VN Mode stimmt nicht */ 72 log_ntp_event("Invalid request: found error at the first byte"); 73 return 1; 74 } 75 76 /* füllt LI VN Mode aus 77 LI = 0 78 VN = Version Nummer aus dem Client 79 Mode = 4 80 */ 81 send_buf[0] = (recv_buf[0] & 0x38) + 4; 82 83 /* Stratum = 1 (primary reference) 84 Reference ID = 'LOCL" 85 (falscher) Bezug auf der lokalen Uhr. 86 */ 87 /* Stratum */ 88 send_buf[1] = 0x01; 89 /* Reference ID = "LOCL" */ 90 *(uint32_t*)&send_buf[12] = htonl(0x4C4F434C); 91 92 /* Copy Poll */ 93 send_buf[2] = recv_buf[2]; 94 95 /* Precision in Microsecond ( from API gettimeofday() ) */ 96 send_buf[3] = (signed char)(-6); /* 2^(-6) sec */ 97 98 /* danach sind alle Werte DWORD aligned */ 99 u32p = (uint32_t *)&send_buf[4]; 100 /* zur Vereinfachung , Root Delay = 0, Root Dispersion = 0 */ 101 *u32p++ = 0; 102 *u32p++ = 0; 103 104 /* Reference ID ist vorher eingetragen */ 105 u32p++; 106 107 /* falscher Reference TimeStamp, 108 * wird immer vor eine minute synchronisiert, 109 * damit die Überprüfung in Client zu belügen */ 110 gettime64(u32p); 111 *u32p = htonl(*u32p - 60); /* -1 Min.*/ 112 u32p++; 113 *u32p = htonl(*u32p); /* -1 Min.*/ 114 u32p++; 115 116 /* Originate Time = Transmit Time @ Client */ 117 *u32p++ = *(uint32_t *)&recv_buf[40]; 118 *u32p++ = *(uint32_t *)&recv_buf[44]; 119 120 /* Receive Time @ Server */ 121 *u32p++ = htonl(recv_time[0]); 122 *u32p++ = htonl(recv_time[1]); 123 124 /* zum Schluss: Transmit Time*/ 125 gettime64(u32p); 126 *u32p = htonl(*u32p); /* -1 Min.*/ 127 u32p++; 128 *u32p = htonl(*u32p); /* -1 Min.*/ 129 130 if ( 131 sendto( 132 socket_fd, 133 send_buf, 134 sizeof(send_buf), 0, 135 saddr_p, saddrlen 136 ) < 48 137 ) { 138 perror("sendto error"); 139 return 1; 140 } 141 142 return 0; 143 } 144 145 146 void request_process_loop(int fd) 147 { 148 struct sockaddr src_addr; 149 socklen_t src_addrlen = sizeof(src_addr); 150 unsigned char buf[48]; 151 uint32_t recv_time[2]; 152 pid_t pid; 153 154 while (1) { 155 while (recvfrom(fd, buf, 156 48, 0, 157 &src_addr, 158 &src_addrlen) 159 < 48 ); /* invalid request */ 160 161 gettime64(recv_time); 162 /* recv_time in local endian */ 163 log_request_arrive(recv_time); 164 165 pid = fork(); 166 if (pid == 0) { 167 /* Child */ 168 ntp_reply(fd, &src_addr , src_addrlen, buf, recv_time); 169 exit(0); 170 } else if (pid == -1) { 171 perror("fork() error"); 172 die(NULL); 173 } 174 /* return to parent */ 175 } 176 } 177 178 179 void ntp_server() 180 { 181 int s; 182 struct sockaddr_in sinaddr; 183 184 s = socket(AF_INET, SOCK_DGRAM, 0); 185 if (s == -1) { 186 perror("Can not create socket."); 187 die(NULL); 188 } 189 190 memset(&sinaddr, 0, sizeof(sinaddr)); 191 sinaddr.sin_family = AF_INET; 192 sinaddr.sin_port = htons(123); 193 sinaddr.sin_addr.s_addr = INADDR_ANY; 194 195 if (0 != bind(s, (struct sockaddr *)&sinaddr, sizeof(sinaddr))) { 196 perror("Bind error"); 197 die(NULL); 198 } 199 200 log_ntp_event( 201 "\n" 202 "========================================\n" 203 "= Server started, waiting for requests =\n" 204 "========================================\n" 205 ); 206 207 request_process_loop(s); 208 close(s); 209 } 210 211 212 void wait_wrapper() 213 { 214 int s; 215 wait(&s); 216 } 217 218 219 int main(int argc, char *argv[], char **env) 220 { 221 signal(SIGCHLD,wait_wrapper); 222 ntp_server(); 223 /* nicht erreichbar: */ 224 return 0; 225 }
A simple client for debug purpose is also available: client.c.gz.