I have been trying for several months to create a simple SNTP single Client/Server based on RFC5905. Finally I manage to make it work at least I think it works correctly, but when I tried to test my code against a real NTP server (e.g. 0.se.pool.ntp.org:123) the timestamps that I am receiving need to be recalculated. I have tried several different approaches but no matter for 3 days now but no matter what I tried nothing yet.
Does anybody know how to convert the NTP timestamp to Unix epoch timestamp?
Syntax to execute the Server e.g. ./server 127.0.0.1:5000 and Client e.g. ./client 127.0.0.1:5000
Syntax to execute the Client against a real NTP server e.g. ./client 0.se.pool.ntp.org:123
Sample of working code Client:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <time.h>
#include <math.h>
#include <sys/timeb.h>
#include <inttypes.h>
#include <limits.h>
#include <assert.h>
#define UNIX_EPOCH 2208988800UL /* 1970 - 1900 in seconds */
typedef struct client_packet client_packet;
struct client_packet {
uint8_t client_li_vn_mode;
uint8_t client_stratum;
uint8_t client_poll;
uint8_t client_precision;
uint32_t client_root_delay;
uint32_t client_root_dispersion;
uint32_t client_reference_identifier;
uint32_t client_reference_timestamp_sec;
uint32_t client_reference_timestamp_microsec;
uint32_t client_originate_timestamp_sec;
uint32_t client_originate_timestamp_microsec;
uint32_t client_receive_timestamp_sec;
uint32_t client_receive_timestamp_microsec;
uint32_t client_transmit_timestamp_sec;
uint32_t client_transmit_timestamp_microsec;
}__attribute__((packed));
typedef struct server_send server_send;
struct server_send {
uint8_t server_li_vn_mode;
uint8_t server_stratum;
uint8_t server_poll;
uint8_t server_precision;
uint32_t server_root_delay;
uint32_t server_root_dispersion;
char server_reference_identifier[4];
uint32_t server_reference_timestamp_sec;
uint32_t server_reference_timestamp_microsec;
uint32_t server_originate_timestamp_sec;
uint32_t server_originate_timestamp_microsec;
uint32_t server_receive_timestamp_sec;
uint32_t server_receive_timestamp_microsec;
uint32_t server_transmit_timestamp_sec;
uint32_t server_transmit_timestamp_microsec;
}__attribute__((packed));
/* Linux man page bind() */
#define handle_error(msg) \
do {perror(msg); exit(EXIT_FAILURE);} while (0)
uint32_t ClockGetTime() {
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
return (uint32_t)ts.tv_sec * 1000000LL + (uint32_t)ts.tv_nsec / 1000LL;
}
int main(int argc, char *argv[]) {
int sockfd , numbytes;
struct addrinfo hints, *servinfo, *p;
int rv;
client_packet memsend;
server_send memrcv;
memset( &memsend , 0 , sizeof memsend );
memset( &memrcv , 0 , sizeof memrcv );
char IP[16]; /* IP = 15 digits 1 extra for \0 null terminating character string */
char PORT_STR[6]; /* Port = 5 digits MAX 1 extra for \0 null terminating character string */
memset(IP , '\0' , sizeof(IP));
memset(PORT_STR , '\0' , sizeof(PORT_STR));
strcpy(IP, strtok(argv[1], ":"));
strcpy(PORT_STR, strtok(NULL, ":"));
memset( &hints , 0 , sizeof hints );
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_DGRAM;
if ( ( rv = getaddrinfo( IP , PORT_STR , &hints , &servinfo ) ) != 0 ) {
fprintf( stderr , "getaddrinfo: %s\n" , gai_strerror(rv) );
return 1;
}
// loop through all the results and make a socket
for( p = servinfo; p != NULL; p = p->ai_next ) {
if ( ( sockfd = socket( p->ai_family , p->ai_socktype , p->ai_protocol ) ) == -1 ) {
handle_error( "socket" );
continue;
}
break;
}
if (p == NULL) {
fprintf(stderr, "Error while binding socket\n");
return 2;
}
memsend.client_li_vn_mode = 0b00100011;
memsend.client_stratum = 0;
memsend.client_poll = 0;
memsend.client_precision = 0;
memsend.client_root_delay = 0;
memsend.client_root_dispersion = 0;
memsend.client_reference_identifier = 0;
memsend.client_reference_timestamp_sec = 0;
memsend.client_reference_timestamp_microsec = 0;
memsend.client_receive_timestamp_sec = 0;
memsend.client_receive_timestamp_microsec = 0;
time_t time_originate_sec = time(NULL);
memsend.client_originate_timestamp_sec = time_originate_sec;
memsend.client_originate_timestamp_microsec = ClockGetTime();
memsend.client_transmit_timestamp_sec = memsend.client_originate_timestamp_sec;
memsend.client_transmit_timestamp_microsec = memsend.client_originate_timestamp_microsec;
if ( ( numbytes = sendto( sockfd, &memsend , sizeof memsend , 0 ,
p->ai_addr , p->ai_addrlen ) ) == -1 ) {
handle_error("sendto");
exit(1);
}
if ( ( numbytes = recvfrom( sockfd , &memrcv , sizeof memrcv , 0 ,
(struct sockaddr *) &p->ai_addr, &p->ai_addrlen ) ) == -1 ) {
handle_error( "recvfrom" );
exit(1);
}
time_t time_rcv_sec = time(NULL);
uint32_t client_rcv_timestamp_sec = time_rcv_sec;
uint32_t client_rcv_timestamp_microsec = ClockGetTime();
freeaddrinfo(servinfo);
char Identifier[5];
memset(Identifier , '\0' , sizeof Identifier);
memcpy(Identifier , memrcv.server_reference_identifier , sizeof memrcv.server_reference_identifier);
printf("\t Reference Identifier \t %"PRIu32" \t\t\t %s\n",memsend.client_reference_identifier,Identifier);
printf("\t Reference Timestamp \t %"PRIu32".%"PRIu32" \t\t\t %"PRIu32".%"PRIu32"\n",memsend.client_reference_timestamp_sec,memsend.client_reference_timestamp_microsec,memrcv.server_reference_timestamp_sec,memrcv.server_reference_timestamp_microsec);
printf("\t Originate Timestamp \t %"PRIu32".%"PRIu32" \t %"PRIu32".%"PRIu32"\n",memsend.client_originate_timestamp_sec,memsend.client_originate_timestamp_microsec,memrcv.server_originate_timestamp_sec,memrcv.server_originate_timestamp_microsec);
printf("\t Receive Timestamp \t %"PRIu32".%"PRIu32" \t %"PRIu32".%"PRIu32"\n",client_rcv_timestamp_sec,client_rcv_timestamp_microsec,memrcv.server_receive_timestamp_sec,memrcv.server_receive_timestamp_microsec);
printf("\t Transmit Timestamp \t %"PRIu32".%"PRIu32" \t %"PRIu32".%"PRIu32"\n\n",memsend.client_transmit_timestamp_sec,memsend.client_transmit_timestamp_microsec,memrcv.server_transmit_timestamp_sec,memrcv.server_transmit_timestamp_microsec);
close(sockfd);
return 0;
}
Sample of Server code:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <time.h>
#include <math.h>
#include <sys/timeb.h>
#include <inttypes.h>
#include <limits.h>
#define TRUE 1
typedef struct client_send client_send;
struct client_send {
uint8_t client_li_vn_mode;
uint8_t client_startum;
uint8_t client_poll;
uint8_t client_precision;
uint32_t client_root_delay;
uint32_t client_root_dispersion;
uint32_t client_reference_identifier;
uint32_t client_reference_timestamp_sec;
uint32_t client_reference_timestamp_microsec;
uint32_t client_originate_timestamp_sec;
uint32_t client_originate_timestamp_microsec;
uint32_t client_receive_timestamp_sec;
uint32_t client_receive_timestamp_microsec;
uint32_t client_transmit_timestamp_sec;
uint32_t client_transmit_timestamp_microsec;
}__attribute__((packed));
typedef struct server_packet server_packet;
struct server_packet {
uint8_t server_li_vn_mode;
uint8_t server_startum;
uint8_t server_poll;
uint8_t server_precision;
uint32_t server_root_delay;
uint32_t server_root_dispersion;
char server_reference_identifier[4];
uint32_t server_reference_timestamp_sec;
uint32_t server_reference_timestamp_microsec;
uint32_t server_originate_timestamp_sec;
uint32_t server_originate_timestamp_microsec;
uint32_t server_receive_timestamp_sec;
uint32_t server_receive_timestamp_microsec;
uint32_t server_transmit_timestamp_sec;
uint32_t server_transmit_timestamp_microsec;
}__attribute__((packed));
/* Linux man page bind() */
#define handle_error(msg) \
do {perror(msg); exit(EXIT_FAILURE);} while (0)
uint32_t ClockGetTime() {
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
return (uint32_t)ts.tv_sec * 1000000LL + (uint32_t)ts.tv_nsec / 1000LL;
}
unsigned long int precision() {
struct timespec res;
if ( clock_getres( CLOCK_REALTIME, &res) == -1 ) {
perror( "clock get resolution" );
return EXIT_FAILURE;
}
return res.tv_nsec / 1000;
}
void *get_in_addr(struct sockaddr *sa) {
if (sa->sa_family == AF_INET) {
return &(((struct sockaddr_in*)sa)->sin_addr);
}
return &(((struct sockaddr_in6*)sa)->sin6_addr);
}
int main(int argc, char *argv[]) {
server_packet send_mem;
client_send rcv_mem;
/* Empty structs */
memset( &send_mem , 0 , sizeof send_mem );
memset( &rcv_mem , 0 , sizeof rcv_mem );
char s[INET_ADDRSTRLEN];
struct addrinfo hints, *servinfo, *p;
struct sockaddr_storage their_addr;
socklen_t addr_len;
int get, numbytes;
int sockfd;
char IP[16];
char PORT_STR[6];
memset(IP , '\0' , sizeof(IP));
memset(PORT_STR , '\0' , sizeof(PORT_STR));
strcpy(IP, strtok(argv[1], ":"));
strcpy(PORT_STR, strtok(NULL, ":"));
memset( &hints , 0 , sizeof hints );
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_flags = AI_PASSIVE;
hints.ai_protocol = IPPROTO_UDP;
if ( ( get = getaddrinfo( NULL , PORT_STR , &hints , &servinfo ) ) != 0) {
fprintf( stderr , "getaddrinfo: %s\n" , gai_strerror(get) );
return 1;
}
for( p = servinfo; p != NULL; p = p->ai_next ) {
if ( ( sockfd = socket( p->ai_family , p->ai_socktype ,
p->ai_protocol ) ) == -1 ) {
handle_error("socket");
continue;
}
if ( bind( sockfd , p->ai_addr , p->ai_addrlen ) == -1 ) {
close(sockfd);
handle_error("bind");
continue;
}
break;
}
if (p == NULL) {
fprintf(stderr, "Not able to bind socket\n");
return 2;
}
freeaddrinfo(servinfo);
printf("\nServer is up and running: waiting to recv msg at port: %s...\n",
PORT_STR);
while(TRUE) {
time_t t_ref_sec = time(NULL);
unsigned long int Ref_epoc_sec = t_ref_sec;
send_mem.server_reference_timestamp_sec = Ref_epoc_sec;
unsigned long int t_ref_nanosec = ClockGetTime();
send_mem.server_reference_timestamp_microsec = t_ref_nanosec;
addr_len = sizeof(their_addr);
if ((numbytes = recvfrom(sockfd, &rcv_mem , sizeof rcv_mem , 0,
(struct sockaddr *)&their_addr, &addr_len)) == -1) {
handle_error("recvfrom");
exit(1);
}
time_t t_rcv_sec = time(NULL);
send_mem.server_receive_timestamp_sec = t_rcv_sec;
send_mem.server_receive_timestamp_microsec = ClockGetTime();
printf("Peer address: %s\n",
inet_ntop(their_addr.ss_family,
get_in_addr((struct sockaddr *)&their_addr),
s, sizeof(s)));
printf("Peer port: %i\n",p->ai_socktype);
send_mem.server_li_vn_mode = 0b00100100;
send_mem.server_startum = 0b00000001;
send_mem.server_poll = 0b00000110;
send_mem.server_precision = precision();
send_mem.server_root_delay = 0;
send_mem.server_root_dispersion = 0;
memcpy( send_mem.server_reference_identifier , "LOCL" ,
sizeof send_mem.server_reference_identifier );
send_mem.server_originate_timestamp_sec = rcv_mem.client_originate_timestamp_sec;
send_mem.server_originate_timestamp_microsec = rcv_mem.client_originate_timestamp_microsec;
time_t t_send_sec = time(NULL);
send_mem.server_transmit_timestamp_sec = t_send_sec;
send_mem.server_transmit_timestamp_microsec = ClockGetTime();
if ( sendto( sockfd, &send_mem , sizeof send_mem , 0 ,
(struct sockaddr *) &their_addr , addr_len ) == -1 ) {
handle_error("sendto");
exit(1);
}
}
close(sockfd);
return 0;
}
Sample of printed output when I use Server and Client.
Reference Identifier 0 LOCL
Reference Timestamp 0.0 1426637081.3564398733
Originate Timestamp 1426637087.3570333925 1426637087.3570333925
Receive Timestamp 1426637087.3570334078 1426637087.3570334003
Transmit Timestamp 1426637087.3570333925 1426637087.3570334046
Sample of printed output when I am probing a real NTP server (e.g. 0.se.pool.ntp.org:123).
Reference Identifier 0 �$�
Reference Timestamp 0.0 3879449560.3503094062
Originate Timestamp 1426637090.3573978972 1426637090.3573978972
Receive Timestamp 1426637090.3573992772 2722083800.781009125
Transmit Timestamp 1426637090.3573978972 2722083800.937312997
The expected output would be something similar to print out as I posted before.
Thank you in advance for everyones time and effort to assist me.
Update Relevant question but not close to the answer that I am looking for How to write a NTP client? [closed].
Convert from human-readable date to epochlong epoch = new java.text.SimpleDateFormat("MM/dd/yyyy HH:mm:ss").parse("01/01/1970 01:00:00").getTime() / 1000; Timestamp in seconds, remove '/1000' for milliseconds. date +%s -d"Jan 1, 1980 00:00:01" Replace '-d' with '-ud' to input in GMT/UTC time.
Epoch Time Difference FormulaMultiply the two dates' absolute difference by 86400 to get the Epoch Time in seconds – using the example dates above, is 319080600.
In computing, Unix time (also known as Epoch time, Posix time, seconds since the Epoch, Unix timestamp or UNIX Epoch time) is a system for describing a point in time. It is the number of seconds that have elapsed since the Unix epoch, excluding leap seconds. The Unix epoch is 00:00:00 UTC on 1 January 1970.
The Unix epoch is at the beginning of 1970 meaning that any timestamp prior to 1970 needs to be represented as a negative number representing the number of seconds until January 1st, 1970 00:00:00 UTC.
converting NTP timestamps to Unix timestamps (struct timeval) involves two problems to be solved.
One is the offset between the two epochs. Unix uses an epoch located at 1/1/1970-00:00h (UTC) and NTP uses 1/1/1900-00:00h. This leads to an offset equivalent to 70 years in seconds (there are 17 leap years between the two dates so the offset is
(70*365 + 17)*86400 = 2208988800
to be substracted from NTP time to get Unix struct timeval
.
The second is that struct timeval
uses 1/1000000 sec
as unit of subsecond fractions and NTP uses 1/2^32 sec
as its unit of fractional time. To convert from NTP to struct timeval
one might divide the fractional part by 2^32
(this is easy, it's a right shift) and then multiply by 1000000
. To cope with this, we have to use 64 bit arithmetic, as numbers range between 0
and 2^32
so, the best is:
to convert from NTP to struct timeval
copy the fractional part field (the right 32 bits of a NTP timestamp) to a uint64_t
variable and multiply it by 1000000
, then right shift it by 32 bit positions to get the proper value. You must take into account that NTP timestamps are in network byte order, so perhaps you'll have to make some adjustments to be able to operate with the numbers.
To convert from struct timeval
copy the tv_usec
field of the unix time to a uint64_t
and left shift it 32 bit positions, then divide it by 1000000
and convert to network byte order (most significative byte first)
The following code sample illustrates this.
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <stdint.h>
#include <getopt.h>
#define OFFSET 2208988800ULL
void ntp2tv(uint8_t ntp[8], struct timeval *tv)
{
uint64_t aux = 0;
uint8_t *p = ntp;
int i;
/* we get the ntp in network byte order, so we must
* convert it to host byte order. */
for (i = 0; i < sizeof ntp / 2; i++) {
aux <<= 8;
aux |= *p++;
} /* for */
/* now we have in aux the NTP seconds offset */
aux -= OFFSET;
tv->tv_sec = aux;
/* let's go with the fraction of second */
aux = 0;
for (; i < sizeof ntp; i++) {
aux <<= 8;
aux |= *p++;
} /* for */
/* now we have in aux the NTP fraction (0..2^32-1) */
aux *= 1000000; /* multiply by 1e6 */
aux >>= 32; /* and divide by 2^32 */
tv->tv_usec = aux;
} /* ntp2tv */
void tv2ntp(struct timeval *tv, uint8_t ntp[8])
{
uint64_t aux = 0;
uint8_t *p = ntp + sizeof ntp;
int i;
aux = tv->tv_usec;
aux <<= 32;
aux /= 1000000;
/* we set the ntp in network byte order */
for (i = 0; i < sizeof ntp/2; i++) {
*--p = aux & 0xff;
aux >>= 8;
} /* for */
aux = tv->tv_sec;
aux += OFFSET;
/* let's go with the fraction of second */
for (; i < sizeof ntp; i++) {
*--p = aux & 0xff;
aux >>= 8;
} /* for */
} /* ntp2tv */
size_t print_tv(struct timeval *t)
{
return printf("%ld.%06ld\n", t->tv_sec, t->tv_usec);
}
size_t print_ntp(uint8_t ntp[8])
{
int i;
int res = 0;
for (i = 0; i < sizeof ntp; i++) {
if (i == sizeof ntp / 2)
res += printf(".");
res += printf("%02x", ntp[i]);
} /* for */
res += printf("\n");
return res;
} /* print_ntp */
int main(int argc, char *argv[])
{
struct timeval t;
uint8_t ntp[8];
gettimeofday(&t, NULL);
printf("tv2ntp\n");
tv2ntp(&t, ntp);
printf("tv : "); print_tv(&t);
printf("ntp: "); print_ntp(ntp);
printf("ntp2tv\n");
ntp2tv(ntp, &t);
printf("tv : "); print_tv(&t);
printf("ntp: "); print_ntp(ntp);
}
The accepted answer is quite old and no longer compiles on GCC 9.3.0 but it has inspired me to write a C++ equivalent, which makes the conversions quite simple thanks to the chrono library:
#include <chrono>
#include <cstddef>
#include <iomanip>
#include <iostream>
using namespace std::chrono;
using namespace std::chrono_literals;
using fractions = duration<std::int64_t, std::ratio<1, 0x100000000>>; // 1/(2^32)
using ntp_t = std::uint64_t;
auto tp2ntp(system_clock::time_point tp)
{
// "shift" epoch from unix 1/1/1970 to ntp 1/1/1900 (70 years + 17 leap days)
tp += (70 * 365 + 17) * 24h;
auto total = tp.time_since_epoch();
auto secs = duration_cast<seconds>(total);
auto fracs = duration_cast<fractions>(total - secs);
return static_cast<ntp_t>( (secs.count() << 32) | fracs.count() );
}
auto ntp2tp(ntp_t ntp)
{
auto tp = system_clock::time_point(); // epoch
// "shift" epoch from ntp 1/1/1900 to unix 1/1/1970 (70 years + 17 leap days)
tp -= (70 * 365 + 17) * 24h;
tp += seconds(ntp >> 32);
tp += duration_cast<system_clock::duration>( fractions(ntp & 0xffffffff) );
return tp;
}
void print_tp(system_clock::time_point tp)
{
auto total = tp.time_since_epoch();
auto secs = duration_cast<seconds>(total);
auto usecs = duration_cast<microseconds>(total - secs);
std::cout << "tp : " << secs.count() << '.' << usecs.count() << std::endl;
}
void print_ntp(ntp_t ntp)
{
std::cout << "ntp: " << std::hex << (ntp >> 32) << '.' << (ntp & 0xffffffff) << std::dec << std::endl;
}
int main(int argc, char *argv[])
{
auto tp = system_clock::now();
std::cout << "tp2ntp" << std::endl;
auto ntp = tp2ntp(tp);
print_tp(tp);
print_ntp(ntp);
std::cout << "ntp2tp" << std::endl;
auto tp2 = ntp2tp(ntp);
print_tp(tp2);
print_ntp(ntp);
return 0;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With