source: trunk/Xalert/stevens/tcpipi/icmptime/icmptime.c

Last change on this file was 41, checked in by wallis, 12 years ago

Importing personal source code.

  • Property svn:keywords set to Id Date Revision Author HeadURL
File size: 7.3 KB
Line 
1/*
2 * Fetch the current time from another host using the ICMP timestamp
3 * request/reply.
4 *
5 * You must be superuser to run this program (or it must be suid to root)
6 * since it requires a raw socket.
7 */
8
9#include <sys/types.h>
10#include <sys/param.h>
11#include <sys/socket.h>
12#include <sys/file.h>
13#include <sys/time.h>
14#include <sys/signal.h>
15
16#include <netinet/in_systm.h>
17#include <netinet/in.h>
18#include <netinet/ip.h>
19#include <netinet/ip_icmp.h>
20#include <netinet/ip_var.h>
21#include <netdb.h>
22#include <unistd.h>
23#include <stdio.h>
24#include <ctype.h>
25#include <errno.h>
26#include <string.h>
27
28#define DEFDATALEN      (12)    /* default data length */
29#define MAXIPLEN        60
30#define MAXICMPLEN      76
31#define MAXPACKET       (65536 - 60 - 8)/* max packet size */
32
33struct sockaddr whereto;        /* who to ping */
34int             datalen = DEFDATALEN;
35int             s;
36u_char          outpack[MAXPACKET];
37char            *hostname;
38
39struct timeval  tvorig, tvrecv; /* originate & receive timeval structs */
40long            tsorig, tsrecv; /* originate & receive timestamps */
41                        /* timestamp = #millisec since midnight UTC */
42                        /* both host byte ordered */
43long            tsdiff;         /* adjustment must also be signed */
44
45u_long          inet_addr();
46char            *inet_ntoa();
47void            sig_alrm(int);
48
49main(argc, argv)
50int     argc;
51char    **argv;
52{
53        int                     i, ch, fdmask, hold, packlen, preload;
54        extern int              errno;
55        struct hostent          *hp;
56        struct sockaddr_in      *to;
57        struct protoent         *proto;
58        u_char                  *packet;
59        char                    *target, hnamebuf[MAXHOSTNAMELEN], *malloc();
60
61        if (argc != 2)
62                exit(1);
63
64        target = argv[1];
65
66        bzero((char *)&whereto, sizeof(struct sockaddr));
67        to = (struct sockaddr_in *)&whereto;
68        to->sin_family = AF_INET;
69
70                /* try to convert as dotted decimal address,
71                   else if that fails assume it's a hostname */
72        to->sin_addr.s_addr = inet_addr(target);
73        if (to->sin_addr.s_addr != (u_int)-1)
74                hostname = target;
75        else {
76                hp = gethostbyname(target);
77                if (!hp) {
78                        fprintf(stderr, "unknown host %s\n", target);
79                        exit(1);
80                }
81                to->sin_family = hp->h_addrtype;
82                bcopy(hp->h_addr, (caddr_t)&to->sin_addr, hp->h_length);
83                strncpy(hnamebuf, hp->h_name, sizeof(hnamebuf) - 1);
84                hostname = hnamebuf;
85        }
86
87        packlen = datalen + MAXIPLEN + MAXICMPLEN;
88        if ( (packet = (u_char *)malloc((u_int)packlen)) == NULL) {
89                fprintf(stderr, "malloc error\n");
90                exit(1);
91        }
92
93        if ( (proto = getprotobyname("icmp")) == NULL) {
94                fprintf(stderr, "unknown protocol icmp\n");
95                exit(1);
96        }
97
98        if ( (s = socket(AF_INET, SOCK_RAW, proto->p_proto)) < 0) {
99                perror("socket");
100                exit(1);
101        }
102
103        /*
104         * We send one request and wait up to 30 seconds for a reply.
105         */
106
107        signal(SIGALRM, sig_alrm);
108        alarm(30);      /* 30 second time limit */
109
110        sender();       /* send the request */
111
112        for (;;) {
113                struct sockaddr_in from;
114                register int cc;
115                int fromlen;
116
117                fromlen = sizeof(from);
118                if ( (cc = recvfrom(s, (char *)packet, packlen, 0,
119                    (struct sockaddr *)&from, &fromlen)) < 0) {
120                        if (errno == EINTR)
121                                continue;
122                        perror("recvfrom error");
123                        continue;
124                }
125
126                        /* process received ICMP message */
127                if (procpack((char *)packet, cc, &from) == 0)
128                        exit(0);        /* terminate if reply received */
129        }
130}
131
132/*
133 * Send the request.
134 */
135
136sender()
137{
138        int             i, cc;
139        struct icmp     *icp;
140
141        icp = (struct icmp *)outpack;
142        icp->icmp_type = ICMP_TSTAMP;
143        icp->icmp_code = 0;
144        icp->icmp_cksum = 0;    /* compute checksum below */
145        icp->icmp_seq = 12345;  /* seq and id must be reflected */
146        icp->icmp_id = getpid();
147
148                /* fill in originate timestamp: have to convert tv_sec
149                   from seconds since the Epoch to milliseconds since
150                   midnight, then add in microseconds */
151
152        gettimeofday(&tvorig, (struct timezone *)NULL);
153        tsorig = (tvorig.tv_sec % (24*60*60)) * 1000 + tvorig.tv_usec / 1000;
154        icp->icmp_otime = htonl(tsorig);
155        icp->icmp_rtime = 0;            /* filled in by receiver */
156        icp->icmp_ttime = 0;            /* filled in by receiver */
157
158        cc = datalen + 8;       /* 8 bytes of header, 12 bytes of data */
159
160                                        /* compute ICMP checksum */
161        icp->icmp_cksum = in_cksum((u_short *)icp, cc);
162
163        i = sendto(s, (char *)outpack, cc, 0, &whereto,
164                                        sizeof(struct sockaddr));
165        if (i < 0 || i != cc)  {
166                if (i < 0)
167                        perror("sendto error");
168                printf("wrote %s %d chars, ret=%d\n", hostname, cc, i);
169        }
170}
171
172/*
173 * Process a received ICMP message.  Since we receive *all* ICMP messages
174 * that the kernel receives, we may receive other than the timestamp
175 * reply we're waiting for.
176 */
177
178int
179procpack(buf, cc, from)
180char                    *buf;
181int                     cc;
182struct sockaddr_in      *from;
183{
184        int             i, hlen;
185        struct icmp     *icp;
186        struct ip       *ip;
187        struct timeval  tvdelta;
188
189        /* We could call gettimeofday() again to measure the RTT and use
190         * that in computing the offset, but some (most?) systems only
191         * have 100 Hz resoultion for this function and the RTT on a
192         * LAN is usually less than this (i.e., it wouldn't buy us
193         * anything). */
194
195#ifdef  notdef
196        gettimeofday(&tvrecv, (struct timezone *)NULL);
197#endif
198
199                /* Check the IP header */
200        ip = (struct ip *)buf;
201        hlen = ip->ip_hl << 2;
202        if (cc < hlen + ICMP_MINLEN) {
203                fprintf(stderr, "packet too short (%d bytes) from %s\n", cc,
204                          inet_ntoa(*(struct in_addr *)&from->sin_addr.s_addr));
205                return;
206        }
207
208                /* Now the ICMP part */
209        cc -= hlen;
210        icp = (struct icmp *)(buf + hlen);
211
212                /* With a raw ICMP socket we get all ICMP packets that
213                   come into the kernel. */
214
215        if (icp->icmp_type == ICMP_TSTAMPREPLY) {
216                if (ntohl(icp->icmp_otime) != tsorig)
217                        printf("originate timestamp not echoed: sent $lu, received %lu\n",
218                                                tsorig, ntohl(icp->icmp_otime));
219                if (cc != 20)
220                        printf("cc = %d, expected cc = 20\n", cc);
221                if (icp->icmp_seq != 12345)
222                        printf("received sequence # %d\n", icp->icmp_seq);
223                if (icp->icmp_id != getpid())
224                        printf("received id %d\n", icp->icmp_id);
225
226                /* tsorig and tsrecv are both signed longs.  The icmp_[ort]time
227                 * members in the structure are unsigned, but the max value
228                 * for the #millisec since midnight is (24*60*60*1000 - 1)
229                 * or 85,399,999, which easily fits into a signed long.
230                 * We want them signed to compute a signed difference. */
231
232                tsrecv = ntohl(icp->icmp_rtime);
233                tsdiff = tsrecv - tsorig;       /* difference in millisec */
234
235                printf("orig = %ld, recv = %ld\n",
236                                ntohl(icp->icmp_otime), ntohl(icp->icmp_rtime));
237                printf("adjustment = %ld ms\n", tsdiff);
238                tvdelta.tv_sec  = tsdiff / 1000;        /* normally 0 */
239                tvdelta.tv_usec = (tsdiff % 1000) * 1000;
240                printf("correction = %ld sec, %ld usec\n",
241                                tvdelta.tv_sec, tvdelta.tv_usec);
242                if (adjtime(&tvdelta, (struct timeval *) 0) < 0) {
243                        perror("adjtime error");
244                        exit(1);
245                }
246                return(0);      /* done */
247        } else
248                return(-1);     /* some other type of ICMP message */
249}
250
251/*
252 * in_cksum --
253 *      Checksum routine for Internet Protocol family headers (C Version)
254 */
255in_cksum(addr, len)
256        u_short *addr;
257        int len;
258{
259        register int nleft = len;
260        register u_short *w = addr;
261        register int sum = 0;
262        u_short answer = 0;
263
264        /*
265         * Our algorithm is simple, using a 32 bit accumulator (sum), we add
266         * sequential 16 bit words to it, and at the end, fold back all the
267         * carry bits from the top 16 bits into the lower 16 bits.
268         */
269        while (nleft > 1)  {
270                sum += *w++;
271                nleft -= 2;
272        }
273
274        /* mop up an odd byte, if necessary */
275        if (nleft == 1) {
276                *(u_char *)(&answer) = *(u_char *)w ;
277                sum += answer;
278        }
279
280        /* add back carry outs from top 16 bits to low 16 bits */
281        sum = (sum >> 16) + (sum & 0xffff);     /* add hi 16 to low 16 */
282        sum += (sum >> 16);                     /* add carry */
283        answer = ~sum;                          /* truncate to 16 bits */
284        return(answer);
285}
286
287void
288sig_alrm(int signo)
289{
290        printf("timeout\n");
291        exit(1);
292}
Note: See TracBrowser for help on using the repository browser.