home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
nisttime.carsoncity.k12.mi.us
/
nisttime.carsoncity.k12.mi.us.tar
/
nisttime.carsoncity.k12.mi.us
/
pub
/
daytime
/
t_ping.c
< prev
next >
Wrap
C/C++ Source or Header
|
2004-05-03
|
15KB
|
420 lines
#include <sys/time.h>
main (argc,argv)
int argc;
char *argv[];
{
/*
This program estimates the time difference between the
local clock and time time of the server using an icmp
ping request.
It estimates the network delay to the server and uses
this delay to correct the measured time difference.
This software was developed with US Government support
and it may not be sold, restricted or licensed. You
may duplicate this program provided that this notice
remains in all of the copies, and you may give it to
others provided they understand and agree to this
condition.
This program and the time protocol it uses are under
development and the implementation may change without
notice.
For questions or additional information, contact:
Judah Levine
Time and Frequency Division
NIST/847
325 Broadway
Boulder, Colorado 80305
(303) 492 7785
jlevine@boulder.nist.gov
*/
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/file.h>
#include <sys/param.h>
#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netdb.h>
long int address; /* holds ip address */
char *cp; /* server address in . notation */
char *sp; /* temporary for server address*/
int aindots[4]; /* numerical host address in dot notation*/
char addrbuf[20]; /* address formatted into dot notation*/
char rp[20]; /* reply address */
struct sockaddr_in sin; /* socket address structure for outbound msg*/
struct sockaddr_in from; /* socket address structure for reply*/
int fromlen; /* returned size of from socket */
struct protoent *proto; /* protocol structure */
struct icmp *icp; /* icmp packet header */
int s; /* socket number */
int length; /* size of total reply message */
int icmplen; /* size of icmp reply*/
unsigned char obuf[200]; /* holds outgoing message */
unsigned char ibuf[100]; /* holds reply datagram*/
unsigned long sendtime; /* local send time */
unsigned long recdtime; /* distant received time */
unsigned long trantime; /* distant transmit time*/
unsigned long rec2time; /* local receive time*/
int j; /* gotta have one of these */
long int cksum; /* packet check sum */
unsigned short int *intptr; /* pointer to 16-bit parts of packet*/
unsigned short int recident; /* received identifier */
unsigned long mstime(); /* time of day in msec since 0000 */
long int msdelay; /* one way delay in msec */
long int diff,diff1,diff2; /* time difference estimates, local - remote*/
int debug = 0; /* print out intermediate values if != 0*/
struct timeval tvv; /* used to adjust local time*/
char cc;
int sw(); /* parses command line switch*/
char let; /* switch letter*/
long int val; /* switch value*/
struct hostent *serv0; /*pointer to structure returned by gethost */
int ierr=0; /* consecutive error counter*/
/*
Command line switch:
-d turn on debug mode
-u<j> use server number j, where j is the index number
of the NIST server chosen from the following list.
the first server on the list is number 1, the
second is number 2, etc.
the default is to use the first server, which
is number 1.
note that the array uses C indexing, so that the
first entry in the array is number 0, etc.
thus any user response >0 is decremented before
being used.
-u0 the name of the server is given as the next
parameter on the command line. the name can
be either a name or an ip address in dot
notation. The entry will be taken as a name
if the first non-blank character is not a
digit.
*/
int use_serv= 0; /*use first server by default*/
/*
the following is a list of the servers operated by
NIST. All of them will support the daytime protocol
in the format that this program expects.
Each server may be specified either by name, as in
time.nist.gov or by ip as in 192.43.244.18. If the
first character of the specification is a digit, then
the numerical format is assumed. If a name is entered,
it is converted to the corresponding ip address using
the standard DNS query. The program will fail if the
DNS query cannot find the ip number of the server
For more information about these servers, look at the
entry for the Internet Time Service on the Time and
Frequency Division homepage at www.boulder.nist.gov/timefreq
*/
#define NUMSRV 15
char *serv_ip[NUMSRV]= {"64.236.96.53" ,/*nist1.aol-va.truetime.com*/
"128.138.140.44" ,/*utcnist.colorado.edu*/
"207.200.81.113" ,/*nist1.aol-ca.truetime.com*/
"216.200.93.8" ,/*nist1-dc.glassey.com*/
"63.149.208.50" ,/*nist1.datum.com*/
"208.184.49.9" ,/*nist1-ny.glassey.com*/
"207.126.103.204",/*nist1-sj.glassey.com*/
"129.6.15.28" ,/*time-a.nist.gov*/
"129.6.15.29" ,/*time-b.nist.gov*/
"132.163.4.101" ,/*time-a.timefreq.bldrdoc.gov*/
"132.163.4.102" ,/*time-b.timefreq.bldrdoc.gov*/
"132.163.4.103" ,/*time-c.timefreq.bldrdoc.gov*/
"192.43.244.018" ,/*time.nist.gov*/
"131.107.1.10" , /*time-nw.nist.gov*/
"utcnist.colorado.edu" /*DNS entry=128.138.140.44*/
};
/*
parse command line
*/
while( sw(argc,argv,&let,&val) != 0) /*switch is present*/
{
switch(let)
{
case 'd':
debug=1;
break;
case 'u':
if(val == 0) /*next parameter specifies server*/
{
argc--;
argv++; /*skip over the switch*/
if(argc <= 1)
{
printf("\n Expected server name is missing.\n\n");
exit();
}
cp=argv[1]; /*save the next parameter as the server name*/
use_serv=999; /*set flag*/
break;
}
/* server 1 has internal index 0 */
use_serv=val-1;
/* check if entry is out of range*/
if(use_serv < 0) use_serv=0;
if(use_serv >= NUMSRV) use_serv=NUMSRV - 1;
break;
default:
fprintf(stderr,"\nSwitch %c not recognized.",let);
break;
}
argc--; /*decrement argument counter */
argv++; /*and increment pointer */
}
/*
get internet address of selected server
if the first digit is not a digit then lookup
the host name and get its ip address. the program
will fail and exit if the name cannot be resolved.
*/
if(use_serv != 999) cp=serv_ip[use_serv];
if(!isdigit(*cp)) /*first char not a digit, must convert name*/
{
if( (serv0=gethostbyname(cp)) == NULL)
{
printf("\n Cannot resolve name %s",cp);
exit();
}
if(serv0->h_length != 4)
{
printf("\nLength of host address (= %d) is wrong, 4 expected.",
serv0->h_length);
exit();
}
sp= serv0->h_addr_list[0];
for(j=0; j<4; j++) /*store and convert address*/
{
aindots[j]= *(sp++);
aindots[j] &= 0xff; /*turn off sign extension*/
}
sprintf(addrbuf,"%d.%d.%d.%d",
aindots[0],aindots[1],aindots[2],aindots[3]);
cp= addrbuf;
}
printf("\n Using server at address %s",cp);
/*
convert address to internal format
*/
if( (address=inet_addr(cp) ) == -1)
{
perror(stderr,"\n Internet address error. \n");
exit(1);
}
bzero( (char *)&sin,sizeof(sin));
sin.sin_addr.s_addr=address;
sin.sin_family=AF_INET;
/*
get protocol number for icmp
*/
if( (proto=getprotobyname("icmp")) == NULL)
{
perror("\n can not find protocol name \n");
exit();
}
/*
create the socket and then build the packet to send
note that this is a raw socket since we are going
to connect directly using ICMP
This socket may require privilege.
*/
if( (s=socket(AF_INET,SOCK_RAW,proto->p_proto) ) < 0)
{
perror("Socket creation error.");
exit(1);
}
/*
build the packet in array obuf using
structure pointers
packet header is 8 bytes followed by
optional data. The type must be 13 to signal
time request. The checksum is calculated later
and the ident field is our process id. This field
is not checked at either end and could be anything.
The pid is used simply because it is an integer unique
to this process. Thus two copies of this program won't
interfere if they run at the same time.
The first thing in the optional
data is the local time stamp which starts at
byte 8 in the array. The time is 4 bytes long
and is in units of milliseconds since 0000 today.
no date information is included in either the
transmitted message or in the reply. Thus this idea
could have a problem if the echo process is sent
just before 0000 and received just after 0000
*/
icp = (struct icmp *)obuf;
icp->icmp_type=13; /* time req. or ICMP_ECHO */
icp->icmp_code=0;
icp->icmp_cksum=0;
icp->icmp_id=getpid() & 0xffff;
icp->icmp_seq=0;
sendtime=mstime(); /*get current time*/
/*
the local time is stored in bytes 7 - 11. The
following loop moves the time into the output buffer
and automatically puts it into network order at
the same time.
*/
for(j=11; j>7; j--) /*store it in bytes 8 - 11*/
{
obuf[j]= sendtime &0xff;
sendtime= sendtime >> 8;
}
for(j=16; j<64; j++) obuf[j]=0; /*clear out rest of packet*/
/*
compute the checksum -- add the contents of the packet
as 8 16 bit words -- the remainder of the packet is all
0 and need not be summed.
*/
cksum = 0;
intptr= (unsigned short int *) &obuf[0];
for(j=0; j<8; j++) cksum += *intptr++;
/*
add high 16 bits of checksum to low 16 bits
If this operation resulted in a carry then
add the carry as well.
*/
cksum= (cksum >> 16) + (cksum & 0xffff);
cksum += (cksum >> 16);
cksum = ~cksum; /* 1s complement*/
icp->icmp_cksum= (unsigned short int) (cksum & 0xffff);
/*
okay folks, mail it off ..
*/
j=sendto(s,obuf,64,0,(struct sockaddr *) &sin,sizeof(sin));
if(j < 0) perror("sendto error ");
if(j != 64) printf("\n sent 64 bytes, sendto return is %d",j);
/*
and see what comes back
*/
rdone: /* read another packet */
length=recvfrom(s,ibuf,sizeof(ibuf),0,&from,&fromlen);
rec2time=mstime(); /*get time as soon as read completes*/
if(length <= 0)
{
perror(" receive error ");
if(ierr++ > 5) exit();
}
if(debug != 0)
{
printf("\n received length is %d \n",length);
for(j=0; j<length; j++) printf(" %d",ibuf[j]);
/*
make sure that this packet came from the time server
take the source address from the packet and convert
it to . notation and compare it with the address
pointed to by cp above.
*/
printf("\n packet came from: %u %u %u %u",ibuf[12],
ibuf[13],ibuf[14],ibuf[15]);
}
sprintf(rp,"%d.%d.%d.%d",ibuf[12],ibuf[13],
ibuf[14],ibuf[15]);
if(strcmp(rp,cp) != 0)
{
printf("\n Packet did not come from time server.");
printf("\n received ip= %s",rp);
printf(" expected ip= %s",cp);
if(ierr++ > 5) exit();
goto rdone; /* read another one */
}
icmplen=4*(ibuf[0] & 0xf); /*length of icmp header*/
if(debug != 0)
{
printf("\n ip header length is %d bytes \n",icmplen);
for(j=icmplen; j<length; j++) printf(" %d",ibuf[j]);
}
/*
now check to be sure that the identifier matches
*/
recident= ibuf[icmplen+4];
recident= (recident << 8) + ibuf[icmplen+5];
if(ntohs(recident) != icp->icmp_id)
{
if(debug != 0) printf("\n Identifer %d not as expected.",j);
goto rdone;
}
close(s); /*this is the correct packet to parse*/
/*
our orignate time stamp is in 8-11
time stamp when it got to server is in 12 -15
time stamp when it left server is 16-19
*/
sendtime=ibuf[icmplen+8];
for(j=9; j<12; j++) sendtime=
(sendtime << 8) + ibuf[icmplen+j];
recdtime=ibuf[icmplen+12];
for(j=13; j<16; j++) recdtime=
(recdtime << 8) + ibuf[icmplen+j];
trantime=ibuf[icmplen+16];
for(j=17; j<20; j++) trantime=
(trantime << 8) + ibuf[icmplen+j];
printf("\n packet was sent at %lu",sendtime);
printf("\n received at server at %lu",recdtime);
printf("\n transmitted from server at %lu",trantime);
printf("\n echo received here at %lu",rec2time);
/*
network delay is assumed to be the same in both directions.
thus the one way delay is one-half of the local elapsed time
less the latency delay in the server
*/
msdelay= (rec2time - sendtime)/2 - (trantime - recdtime);
/*
first difference estiamte uses outbound packet corrected
for delay.
second differences estiamte uses inbound packet also corrected
for delay.
*/
diff1= (sendtime-recdtime) + msdelay;
diff2= (rec2time-trantime) - msdelay;
printf("\n Network delay is %ld msec.",msdelay);
printf(" \n Time difference 1, Local - NIST=%ld msec.",diff1);
printf(" \n Time difference 2, Local - NIST=%ld msec.",diff2);
diff=(diff1+diff2)/2;
printf(" \n Average Time Difference =%ld msec.",diff);
if(diff == 0)
{
printf("\n No adjustment is necessary.\n");
exit();
}
printf("\n Do you want to adjust the local clock ? [y/n] ");
cc=getchar();
if( (cc == 'y') || (cc == 'Y') )
{
tvv.tv_sec= -diff/1000; /*convert msec to sec*/
tvv.tv_usec = -(diff%1000);
tvv.tv_usec *= 1000; /*convert to usec */
j=adjtime(&tvv,(struct timeval *) 0 );
if(j == 0) printf("\n Adjustment was performed.\n");
else perror("Adjustment failed. ");
}
}
unsigned long int mstime()
{
/*
this subroutine returns the current system time expressed
as the number of milliseconds since 0000 of the current
day. No date information is returned
*/
struct timeval tvv;
unsigned long int j;
gettimeofday(&tvv,(struct timezone *)0 );
j=tvv.tv_sec%86400; /*get number of seconds since 0000 today*/
j= 1000*j + tvv.tv_usec/1000; /*convert to milliseconds*/
return(j);
}