最简单的嵌入式Linux下ntp实现

2019-07-12 15:35发布

#include #include #include #include #include #include #include #include #include #include #include #include #include #include #define JAN_1970 0x83aa7e80 /* 2208988800 1970 - 1900 in seconds */ #define NTP_PORT (123) /* How to multiply by 4294.967296 quickly (and not quite exactly) * without using floating point or greater than 32-bit integers. * If you want to fix the last 12 microseconds of error, add in * (2911*(x))>>28) */ #define NTPFRAC(x) ( 4294*(x) + ( (1981*(x))>>11 ) ) /* The reverse of the above, needed if we want to set our microsecond * clock (via settimeofday) based on the incoming time in NTP format. * Basically exact. */ #define USEC(x) ( ( (x) >> 12 ) - 759 * ( ( ( (x) >> 10 ) + 32768 ) >> 16 ) ) /* Converts NTP delay and dispersion, apparently in seconds scaled * by 65536, to microseconds. RFC1305 states this time is in seconds, * doesn't mention the scaling. * Should somehow be the same as 1000000 * x / 65536 */ #define sec2u(x) ( (x) * 15.2587890625 ) typedef u_int32_t __u32; struct ntptime { unsigned int coarse; unsigned int fine; }; void send_packet(int usd) { __u32 data[12]; struct timeval now; #define LI 0 #define VN 3 #define MODE 3 #define STRATUM 0 #define POLL 4 #define PREC -6 bzero((char *) data,sizeof(data)); data[0] = htonl ( ( LI << 30 ) | ( VN << 27 ) | ( MODE << 24 ) | ( STRATUM << 16) | ( POLL << 8 ) | ( PREC & 0xff ) ); data[1] = htonl(1<<16); /* Root Delay (seconds) */ data[2] = htonl(1<<16); /* Root Dispersion (seconds) */ gettimeofday(&now,NULL); data[10] = htonl(now.tv_sec + JAN_1970); /* Transmit Timestamp coarse */ data[11] = htonl(NTPFRAC(now.tv_usec)); /* Transmit Timestamp fine */ send(usd,data,48,0); } /* double ntpdiff( struct ntptime *start, struct ntptime *stop) { int a; unsigned int b; a = stop->coarse - start->coarse; if (stop->fine >= start->fine) { b = stop->fine - start->fine; } else { b = start->fine - stop->fine; b = ~b; a -= 1; } return a*1.e6 + b * (1.e6/4294967296.0); } */ void rfc1305(uint32_t *data) { /* struct ntptime *arrival; struct timeval udp_arrival; gettimeofday(&udp_arrival, NULL); arrival->coarse = udp_arrival.tv_sec + JAN_1970; arrival->fine = NTPFRAC(udp_arrival.tv_usec); */ /* // straight out of RFC-1305 Appendix A int li, vn, mode, stratum, poll, prec; int delay, disp, refid; struct ntptime reftime, orgtime, rectime, xmttime; #define Data(i) ntohl(((uint32_t *)data)[i]) li = Data(0) >> 30 & 0x03; vn = Data(0) >> 27 & 0x07; mode = Data(0) >> 24 & 0x07; stratum = Data(0) >> 16 & 0xff; poll = Data(0) >> 8 & 0xff; prec = Data(0) & 0xff; if (prec & 0x80) prec|=0xffffff00; delay = Data(1); disp = Data(2); refid = Data(3); reftime.coarse = Data(4); reftime.fine = Data(5); orgtime.coarse = Data(6); orgtime.fine = Data(7); rectime.coarse = Data(8); rectime.fine = Data(9); xmttime.coarse = Data(10); xmttime.fine = Data(11); #undef Data struct timeval tv_set; // it would be even better to subtract half the slop tv_set.tv_sec = xmttime.coarse - JAN_1970; // divide xmttime.fine by 4294.967296 tv_set.tv_usec = USEC(xmttime.fine); if (settimeofday(&tv_set,NULL)<0) { perror("settimeofday"); exit(1); } */ struct timeval tv_set; tv_set.tv_sec = ntohl(((uint32_t *)data)[10]) - JAN_1970; tv_set.tv_usec = USEC(ntohl(((uint32_t *)data)[11])); if (settimeofday(&tv_set, NULL) < 0) { perror("settimeofday"); exit(1); } /* double el_time,st_time; el_time=ntpdiff(&orgtime,arrival); // elapsed st_time=ntpdiff(&rectime,&xmttime); // stall return(el_time-st_time); */ } int main(int argc, char *argv[]) { int usd; struct sockaddr_in sa; struct hostent *he; struct timeval tv; fd_set fds; int i; char *srv; char buf[256]; // printf("mini ntpclient "); if (argc <= 1) { printf("Usage: %s [server [...]] ", argv[0]); return 1; } for (i = 1; i < argc; ++i) { // ntp.c passes servers as one block of string, so we need to break it down strcpy(buf, argv[i]); srv = strtok(buf, " "); while (srv) { if ((usd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) { printf("socket error"); return 1; } if ((he = gethostbyname(srv)) != NULL) { memset(&sa, 0, sizeof(sa)); memcpy(&sa.sin_addr, he->h_addr_list[0], sizeof(sa.sin_addr)); sa.sin_port = htons(NTP_PORT); sa.sin_family = AF_INET; //printf("trying %s [%s] ", argv[i], inet_ntoa(sa.sin_addr)); if (connect(usd, (struct sockaddr*)&sa, sizeof(sa)) != -1) { send_packet(usd); tv.tv_sec = 3; tv.tv_usec = 0; FD_ZERO(&fds); FD_SET(usd, &fds); if (select(usd + 1, &fds, NULL, NULL, &tv) == 1) { int len; uint32_t packet[12]; len = recv(usd, packet, sizeof(packet), 0); if (len == sizeof(packet)) { rfc1305(packet); close(usd); printf("Time_updated. "); return 0; } } } else { perror("connect"); printf("connect_error "); } } else { perror("gethostbyname"); printf("gethostbyname_error "); } close(usd); srv = strtok(NULL, " "); } } return 1; }