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.