├── .gitignore ├── Makefile ├── README ├── client.c └── server.c /.gitignore: -------------------------------------------------------------------------------- 1 | server 2 | client 3 | 4 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all clean tags 2 | 3 | CFLAGS:=-Wall -O2 4 | 5 | all: client server 6 | 7 | server: server.c 8 | $(CC) $(CFLAGS) $^ -o $@ 9 | strip -s $@ 10 | 11 | client: client.c 12 | $(CC) $(CFLAGS) $^ -o $@ 13 | strip -s $@ 14 | 15 | tags: 16 | ctags -R . 17 | 18 | clean: 19 | -rm *.o client server 20 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include /* gettimeofday() */ 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include /* for time() and ctime() */ 13 | 14 | #define UTC_NTP 2208988800U /* 1970 - 1900 */ 15 | 16 | /* get Timestamp for NTP in LOCAL ENDIAN */ 17 | void gettime64(uint32_t ts[]) 18 | { 19 | struct timeval tv; 20 | gettimeofday(&tv, NULL); 21 | 22 | ts[0] = tv.tv_sec + UTC_NTP; 23 | ts[1] = (4294*(tv.tv_usec)) + ((1981*(tv.tv_usec))>>11); 24 | } 25 | 26 | int die(const char *msg) 27 | { 28 | fputs(msg, stderr); 29 | exit(-1); 30 | } 31 | 32 | int useage(const char *path) 33 | { 34 | printf("Useage:\n\t%s \n", path); 35 | return 1; 36 | } 37 | 38 | 39 | int open_connect(const char* server) 40 | { 41 | int s; 42 | struct addrinfo *saddr; 43 | 44 | /* printf("Connecting to server: %s\n", server); */ 45 | s = socket(AF_INET, SOCK_DGRAM, 0); 46 | if (s == -1) { 47 | die("Can not create socket.\n"); 48 | } 49 | 50 | if (0 != getaddrinfo(server, "123", NULL, &saddr)) { 51 | die("Server address not correct.\n"); 52 | } 53 | 54 | if (connect(s, saddr->ai_addr, saddr->ai_addrlen) != 0) { 55 | die("Connect error\n"); 56 | } 57 | 58 | freeaddrinfo(saddr); 59 | 60 | return s; 61 | } 62 | 63 | void request(int fd) 64 | { 65 | unsigned char buf[48] = {0}; 66 | uint32_t tts[2]; /* Transmit Timestamp */ 67 | 68 | /* LI VN MODE = 00 100 011*/ 69 | buf[0] = 0x23; 70 | 71 | gettime64(tts); 72 | (*(uint32_t *)&buf[40]) = htonl(tts[0]); 73 | (*(uint32_t *)&buf[44])= htonl(tts[1]); 74 | if (send(fd, buf, 48, 0) !=48 ) { 75 | die("Send error\n"); 76 | } 77 | } 78 | 79 | void get_reply(int fd) 80 | { 81 | unsigned char buf[48]; 82 | uint32_t *pt; 83 | // uint32_t t_last_update[2]; /* Reference Timestamp @ Server */ 84 | uint32_t t1[2]; /* t1 = Originate Timestamp */ 85 | uint32_t t2[2]; /* t2 = Receive Timestamp @ Server */ 86 | uint32_t t3[2]; /* t3 = Transmit Timestamp @ Server */ 87 | uint32_t t4[2]; /* t4 = Receive Timestamp @ Client */ 88 | double T1, T2, T3, T4; 89 | double tfrac = 4294967296.0; 90 | time_t curr_time; 91 | time_t diff_sec; 92 | 93 | if (recv(fd, buf, 48, 0) < 48) { 94 | die("Receive error\n"); 95 | } 96 | gettime64(t4); 97 | pt = (uint32_t *)&buf[24]; 98 | 99 | t1[0] = htonl(*pt++); 100 | t1[1] = htonl(*pt++); 101 | 102 | t2[0] = htonl(*pt++); 103 | t2[1] = htonl(*pt++); 104 | 105 | t3[0] = htonl(*pt++); 106 | t3[1] = htonl(*pt++); 107 | 108 | /* TODO: überprüfen t1 = Transmit Timestamp @ Client */ 109 | /* und andere Überprüfungen 110 | * (z.B Version=4, Mode=Server, 111 | * Stratum = 0-15, etc.)*/ 112 | 113 | T1 = t1[0] + t1[1]/tfrac; 114 | T2 = t2[0] + t2[1]/tfrac; 115 | T3 = t3[0] + t3[1]/tfrac; 116 | T4 = t4[0] + t4[1]/tfrac; 117 | 118 | printf( "\ndelay = %lf\n" 119 | "offset = %lf\n\n", 120 | (T4-T1) - (T3-T2), 121 | ((T2 - T1) + (T3 - T4)) /2 122 | ); 123 | /* wenn mit Ganzzahl rechnen, kann sein, 124 | * dass die Differenz negativ ist */ 125 | diff_sec = ((int32_t)(t2[0] - t1[0]) + (int32_t)(t3[0] - t4[0])) /2; 126 | curr_time = time(NULL) - diff_sec; 127 | printf("Current Time at Server: %s\n", ctime(&curr_time)); 128 | } 129 | 130 | int client(const char* server) 131 | { 132 | int fd; 133 | 134 | fd = open_connect(server); 135 | request(fd); 136 | get_reply(fd); 137 | close(fd); 138 | 139 | return 0; 140 | } 141 | 142 | 143 | int main(int argc, char *argv[], char **env) 144 | { 145 | if (argc < 2) { 146 | return useage(argv[0]); 147 | } 148 | 149 | return client(argv[1]); 150 | } 151 | -------------------------------------------------------------------------------- /server.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include /* gettimeofday() */ 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include /* 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 | 86 | (falscher) Bezug auf der lokalen Uhr. 87 | */ 88 | /* Stratum */ 89 | send_buf[1] = 0x01; 90 | /* Reference ID = "LOCL" */ 91 | *(uint32_t*)&send_buf[12] = htonl(0x4C4F434C); 92 | 93 | /* Copy Poll */ 94 | send_buf[2] = recv_buf[2]; 95 | 96 | /* Precision in Microsecond ( from API gettimeofday() ) */ 97 | send_buf[3] = (signed char)(-6); /* 2^(-6) sec */ 98 | 99 | /* danach sind alle Werte DWORD aligned */ 100 | u32p = (uint32_t *)&send_buf[4]; 101 | /* zur Vereinfachung , Root Delay = 0, Root Dispersion = 0 */ 102 | *u32p++ = 0; 103 | *u32p++ = 0; 104 | 105 | /* Reference ID ist vorher eingetragen */ 106 | u32p++; 107 | 108 | /* falscher Reference TimeStamp, 109 | * wird immer vor eine minute synchronisiert, 110 | * damit die Überprüfung in Client zu belügen */ 111 | gettime64(u32p); 112 | *u32p = htonl(*u32p - 60); /* -1 Min.*/ 113 | u32p++; 114 | *u32p = htonl(*u32p); /* -1 Min.*/ 115 | u32p++; 116 | 117 | /* Originate Time = Transmit Time @ Client */ 118 | *u32p++ = *(uint32_t *)&recv_buf[40]; 119 | *u32p++ = *(uint32_t *)&recv_buf[44]; 120 | 121 | /* Receive Time @ Server */ 122 | *u32p++ = htonl(recv_time[0]); 123 | *u32p++ = htonl(recv_time[1]); 124 | 125 | /* zum Schluss: Transmit Time*/ 126 | gettime64(u32p); 127 | *u32p = htonl(*u32p); /* -1 Min.*/ 128 | u32p++; 129 | *u32p = htonl(*u32p); /* -1 Min.*/ 130 | 131 | if ( sendto( socket_fd, 132 | send_buf, 133 | sizeof(send_buf), 0, 134 | saddr_p, saddrlen) 135 | < 48) { 136 | perror("sendto error"); 137 | return 1; 138 | } 139 | 140 | return 0; 141 | } 142 | 143 | 144 | void request_process_loop(int fd) 145 | { 146 | struct sockaddr src_addr; 147 | socklen_t src_addrlen = sizeof(src_addr); 148 | unsigned char buf[48]; 149 | uint32_t recv_time[2]; 150 | pid_t pid; 151 | 152 | while (1) { 153 | while (recvfrom(fd, buf, 154 | 48, 0, 155 | &src_addr, 156 | &src_addrlen) 157 | < 48 ); /* invalid request */ 158 | 159 | gettime64(recv_time); 160 | /* recv_time in local endian */ 161 | log_request_arrive(recv_time); 162 | 163 | pid = fork(); 164 | if (pid == 0) { 165 | /* Child */ 166 | ntp_reply(fd, &src_addr , src_addrlen, buf, recv_time); 167 | exit(0); 168 | } else if (pid == -1) { 169 | perror("fork() error"); 170 | die(NULL); 171 | } 172 | /* return to parent */ 173 | } 174 | } 175 | 176 | 177 | void ntp_server(struct sockaddr_in bind_addr) 178 | { 179 | int s; 180 | struct sockaddr_in sinaddr; 181 | 182 | s = socket(AF_INET, SOCK_DGRAM, 0); 183 | if (s == -1) { 184 | perror("Can not create socket."); 185 | die(NULL); 186 | } 187 | 188 | memset(&sinaddr, 0, sizeof(sinaddr)); 189 | sinaddr.sin_family = AF_INET; 190 | sinaddr.sin_port = htons(123); 191 | sinaddr.sin_addr.s_addr = bind_addr.sin_addr.s_addr; 192 | 193 | if (0 != bind(s, (struct sockaddr *)&sinaddr, sizeof(sinaddr))) { 194 | perror("Bind error"); 195 | die(NULL); 196 | } 197 | 198 | log_ntp_event( "\n========================================\n" 199 | "= Server started, waiting for requests =\n" 200 | "========================================\n"); 201 | 202 | request_process_loop(s); 203 | close(s); 204 | } 205 | 206 | 207 | void wait_wrapper() 208 | { 209 | int s; 210 | wait(&s); 211 | } 212 | 213 | int main(int argc, char *argv[], char **env) 214 | { 215 | struct sockaddr_in bind_addr; 216 | memset(&bind_addr, 0, sizeof(bind_addr)); 217 | 218 | if (argc > 2) { 219 | printf("usage: %s [bind address]\n", argv[0]); 220 | return 1; 221 | } 222 | 223 | if (argc > 1) { 224 | inet_pton(AF_INET, argv[1], &(bind_addr.sin_addr)); 225 | } 226 | 227 | signal(SIGCHLD,wait_wrapper); 228 | ntp_server(bind_addr); 229 | /* nicht erreichbar: */ 230 | return 0; 231 | } 232 | --------------------------------------------------------------------------------