1/* -*- linux-c -*- */
2/*
3 *
4 *
5 *   Copyright (c) International Business Machines  Corp., 2000
6 *
7 *   This program is free software;  you can redistribute it and/or modify
8 *   it under the terms of the GNU General Public License as published by
9 *   the Free Software Foundation; either version 2 of the License, or
10 *   (at your option) any later version.
11 *
12 *   This program is distributed in the hope that it will be useful,
13 *   but WITHOUT ANY WARRANTY;  without even the implied warranty of
14 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
15 *   the GNU General Public License for more details.
16 *
17 *   You should have received a copy of the GNU General Public License
18 *   along with this program;  if not, write to the Free Software
19 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 *
21 *
22 */
23/*
24 * ltpClient.c
25 *
26 * LTP Network Socket Test Client
27 *
28 *
29 */
30
31#include <sys/types.h>
32#include <sys/socket.h>
33#include <netinet/in.h>
34#include <netinet/ip_icmp.h>
35#include <arpa/inet.h>
36#include <netdb.h>
37#include <stdio.h>
38#include <stdlib.h>
39#include <resolv.h>
40#include <fcntl.h>
41#include <errno.h>
42#include <unistd.h>
43#include <string.h>
44#include <sys/time.h>
45
46#define LOCAL_UDP_SERVER_PORT   10000
47#define LOCAL_TCP_SERVER_PORT   10001
48#define LOCAL_MCAST_SERVER_PORT 10002
49#define MAX_MSG_LEN             256
50#define TIMETOLIVE              10
51#define PACKETSIZE	            64
52#define NET_ERROR               -1
53#define PACKET_LEN              1024	/* 1K should be plenty */
54#define TRUE                    1
55#define FALSE                   0
56
57struct protoent *protocol = NULL;
58
59struct packet {
60	struct icmphdr hdr;
61	char msg[PACKETSIZE - sizeof(struct icmphdr)];
62};
63
64/*
65*   Function Prototypes
66*/
67int ltp_run_ping_tests(char *hostName);
68int ltp_run_traceroute_tests(char *hostName);
69void ping_network(struct sockaddr_in *rawAddr, int pid);
70void output_to_display(void *netPacket, int bytes, int pid);
71unsigned short checksum(void *netPacket, int len);
72int network_listener(char *hostname, int pid);
73void ltp_traceroute(struct sockaddr_in *rawTraceAddr, char *hostName, int pid);
74
75/*******************************************************************
76*  Function: Main
77*
78*  Main will run the tests in this order.
79*       UDP, TCP and Multicast will be run first, if multicast is enabled.
80*       If multicast is not enabled, the UDP/TCP tests will continue.
81*       Once those tests complete, the ping and then traceroute tests will run.
82*
83********************************************************************/
84int main(int argc, char *argv[])
85{
86
87	int udpSocketHandle, tcpSocketHandle, mcastSocketHandle, rc, i;
88
89	struct sockaddr_in udpClientAddr,
90	    udpRemoteServerAddr,
91	    tcpClientAddr,
92	    tcpRemoteServerAddr, mcastClientAddr, mcastRemoteServerAddr;
93
94	struct hostent *hostEntry;
95
96	char hostName[MAX_MSG_LEN],
97	    progName[MAX_MSG_LEN], traceName[MAX_MSG_LEN], multiCast = TRUE;
98
99	unsigned char ttl = 1;
100
101	mcastSocketHandle = -1;
102
103	/* check command line args */
104	if (argc < 4) {
105		printf
106		    ("usage :<server-hostname> <trace-hostName> <data1> ... <dataN> \n");
107		exit(1);
108	}
109
110	strncpy(progName, argv[0], MAX_MSG_LEN);
111	strncpy(hostName, argv[1], MAX_MSG_LEN);
112	strncpy(traceName, argv[2], MAX_MSG_LEN);
113
114	/* get server IP address (no check if input is IP address or DNS name */
115	hostEntry = gethostbyname(hostName);
116
117	if (hostEntry == NULL) {
118		printf("%s: unknown host passed'%s' \n", progName, hostName);
119		exit(1);
120	}
121
122	printf("%s: sending data to '%s' (IP : %s) \n", progName,
123	       hostEntry->h_name,
124	       inet_ntoa(*(struct in_addr *)hostEntry->h_addr_list[0]));
125
126	/* Setup UDP data packets */
127
128	udpRemoteServerAddr.sin_family = hostEntry->h_addrtype;
129	memcpy((char *)&udpRemoteServerAddr.sin_addr.s_addr,
130	       hostEntry->h_addr_list[0], hostEntry->h_length);
131	udpRemoteServerAddr.sin_port = htons(LOCAL_UDP_SERVER_PORT);
132
133	/* Setup TCP data packets */
134
135	tcpRemoteServerAddr.sin_family = hostEntry->h_addrtype;
136	memcpy((char *)&tcpRemoteServerAddr.sin_addr.s_addr,
137	       hostEntry->h_addr_list[0], hostEntry->h_length);
138	tcpRemoteServerAddr.sin_port = htons(LOCAL_TCP_SERVER_PORT);
139
140	/* Setup multiCast data packets */
141
142	mcastRemoteServerAddr.sin_family = hostEntry->h_addrtype;
143	memcpy((char *)&mcastRemoteServerAddr.sin_addr.s_addr,
144	       hostEntry->h_addr_list[0], hostEntry->h_length);
145	mcastRemoteServerAddr.sin_port = htons(LOCAL_MCAST_SERVER_PORT);
146
147	/* socket creation */
148	udpSocketHandle = socket(AF_INET, SOCK_DGRAM, 0);
149	tcpSocketHandle = socket(AF_INET, SOCK_STREAM, 0);
150
151	if (udpSocketHandle < 0) {
152		printf("%s: Error: cannot open UDP socket \n", progName);
153	}
154
155	if (tcpSocketHandle < 0) {
156		printf("%s: Error: cannot open TCP socket \n", progName);
157	}
158
159	/* bind any UDP port */
160	udpClientAddr.sin_family = AF_INET;
161	udpClientAddr.sin_addr.s_addr = htonl(INADDR_ANY);
162	udpClientAddr.sin_port = htons(0);
163
164	/* bind any TCP port */
165	tcpClientAddr.sin_family = AF_INET;
166	tcpClientAddr.sin_addr.s_addr = htonl(INADDR_ANY);
167	tcpClientAddr.sin_port = htons(0);
168
169	if (udpSocketHandle > 0) {
170		rc = bind(udpSocketHandle, (struct sockaddr *)&udpClientAddr,
171			  sizeof(udpClientAddr));
172
173		if (rc < 0) {
174			printf("%s: Error: cannot bind UDP port\n", progName);
175		}
176	}
177
178	if (tcpSocketHandle > 0) {
179		rc = bind(tcpSocketHandle, (struct sockaddr *)&tcpClientAddr,
180			  sizeof(tcpClientAddr));
181
182		if (rc < 0) {
183			printf("%s: Error: cannot bind TCP port\n", progName);
184		} else {
185			/* connect to server */
186			rc = connect(tcpSocketHandle,
187				     (struct sockaddr *)&tcpRemoteServerAddr,
188				     sizeof(tcpRemoteServerAddr));
189
190			if (rc < 0) {
191				printf
192				    ("Error: cannot connect tp TCP Server \n");
193			}
194		}
195	}
196
197	/* check given address is multicast */
198	if (!IN_MULTICAST(ntohl(mcastRemoteServerAddr.sin_addr.s_addr))) {
199		printf
200		    ("%s : Hostname [%s] passed [%s] is not a multicast server\n",
201		     progName, hostName,
202		     inet_ntoa(mcastRemoteServerAddr.sin_addr));
203		printf("The multiCast Server will not be started \n");
204		multiCast = FALSE;
205	} else {
206		/* create socket */
207		mcastSocketHandle = socket(AF_INET, SOCK_DGRAM, 0);
208		if (mcastSocketHandle < 0) {
209			printf("Error: %s : cannot open mulitCast socket\n",
210			       progName);
211			multiCast = FALSE;
212		}
213
214		/* bind any port number */
215		mcastClientAddr.sin_family = AF_INET;
216		mcastClientAddr.sin_addr.s_addr = htonl(INADDR_ANY);
217		mcastClientAddr.sin_port = htons(0);
218
219		if (bind
220		    (mcastSocketHandle, (struct sockaddr *)&mcastClientAddr,
221		     sizeof(mcastClientAddr)) < 0) {
222			printf("Error: binding multiCast socket");
223			multiCast = FALSE;
224		}
225
226		if (setsockopt
227		    (mcastSocketHandle, IPPROTO_IP, IP_MULTICAST_TTL, &ttl,
228		     sizeof(ttl)) < 0) {
229			printf("Error: %s : cannot set ttl = %d \n", progName,
230			       ttl);
231			multiCast = FALSE;
232		}
233
234		printf("%s : sending data on multicast group '%s' (%s)\n",
235		       progName, hostEntry->h_name,
236		       inet_ntoa(*(struct in_addr *)hostEntry->h_addr_list[0]));
237
238	}
239
240	/* Skip over the program and hostnames and just send data */
241	for (i = 3; i < argc; i++) {
242
243		if (udpSocketHandle > 0) {
244			rc = sendto(udpSocketHandle, argv[i],
245				    strlen(argv[i]) + 1, 0,
246				    (struct sockaddr *)&udpRemoteServerAddr,
247				    sizeof(udpRemoteServerAddr));
248
249			if (rc < 0) {
250				printf("%s: cannot send UDP data %d \n",
251				       progName, i - 1);
252				close(udpSocketHandle);
253			} else {
254				printf("%s: UDP data%u sent (%s)\n", progName,
255				       i - 1, argv[i]);
256			}
257		} else {
258			printf("%s UDP Socket not open for send \n", hostName);
259		}
260
261		if (tcpSocketHandle > 0) {
262			rc = send(tcpSocketHandle, argv[i], strlen(argv[i]) + 1,
263				  0);
264
265			if (rc < 0) {
266				printf("cannot send TCP data ");
267				close(tcpSocketHandle);
268
269			} else {
270				printf("%s: TCP data%u sent (%s)\n", progName,
271				       i - 1, argv[i]);
272			}
273		} else {
274			printf("%s TCP Socket not open for send \n", hostName);
275		}
276
277		if (multiCast) {
278			rc = sendto(mcastSocketHandle, argv[i],
279				    strlen(argv[i]) + 1, 0,
280				    (struct sockaddr *)&mcastRemoteServerAddr,
281				    sizeof(mcastRemoteServerAddr));
282
283			if (rc < 0) {
284				printf("%s : cannot send multiCast data %d\n",
285				       progName, i - 1);
286				close(mcastSocketHandle);
287				multiCast = FALSE;
288			}
289		}
290	}
291
292	sleep(5);
293
294	ltp_run_traceroute_tests(traceName);
295
296	ltp_run_ping_tests(hostName);
297
298	return 0;
299
300}
301
302/*****************************************************************************
303* Function: ltp_run_traceroute_tests - host look up and start traceroute processes
304*
305******************************************************************************/
306int ltp_run_traceroute_tests(char *hostName)
307{
308
309	struct hostent *hostEntry;
310	struct sockaddr_in rawTraceAddr;
311	int pid = -1;
312
313	pid = getpid();
314
315	protocol = getprotobyname("ICMP");
316	hostEntry = gethostbyname(hostName);
317
318	memset(&rawTraceAddr, 0, sizeof(rawTraceAddr));
319
320	rawTraceAddr.sin_family = hostEntry->h_addrtype;
321	rawTraceAddr.sin_port = 0;
322	rawTraceAddr.sin_addr.s_addr = *(long *)hostEntry->h_addr;
323
324	ltp_traceroute(&rawTraceAddr, hostName, pid);
325
326	return 0;
327}
328
329/**********************************************************************
330* Function: ltp_run_ping_tests - host look up and start ping processes
331*
332***********************************************************************/
333int ltp_run_ping_tests(char *hostName)
334{
335
336	struct hostent *hostEntry;
337	struct sockaddr_in rawAddr;
338	int pid = -1;
339
340	pid = getpid();
341
342	protocol = getprotobyname("ICMP");
343	hostEntry = gethostbyname(hostName);
344
345	memset(&rawAddr, 0, sizeof(rawAddr));
346
347	rawAddr.sin_family = hostEntry->h_addrtype;
348	rawAddr.sin_port = 0;
349	rawAddr.sin_addr.s_addr = *(long *)hostEntry->h_addr;
350
351	if (fork() == 0) {
352		network_listener(hostName, pid);
353	} else {
354		ping_network(&rawAddr, pid);
355
356	}
357
358	return 0;
359}
360
361/******************************************************************************
362* Function: network_listener - separate process to listen for and collect messages
363*
364*******************************************************************************/
365int network_listener(char *hostName, int pid)
366{
367
368	int rawSocket, count, value = TIMETOLIVE;
369	struct sockaddr_in rawAddr;
370	unsigned char packet[PACKET_LEN];
371
372	rawSocket = socket(PF_INET, SOCK_RAW, protocol->p_proto);
373	count = 0;
374
375	if (rawSocket < 0) {
376		printf("%s: Error: cannot open RAW socket \n", hostName);
377		return (NET_ERROR);
378	}
379
380	while (1) {		/* loop forever */
381
382		int bytes;
383		socklen_t len = sizeof(rawAddr);
384
385		memset(packet, 0, sizeof(packet));
386
387		bytes =
388		    recvfrom(rawSocket, packet, sizeof(packet), 0,
389			     (struct sockaddr *)&rawAddr, &len);
390
391		if (bytes > 0)
392			output_to_display(packet, bytes, pid);
393		else {
394			printf("%s : cannot receive data\n", hostName);
395			break;
396		}
397		count++;
398
399		if (value == count) {
400			printf("Exiting the network_listener...\n");
401		}
402	}
403
404	close(rawSocket);
405	return (0);
406}
407
408/****************************************************************
409* Function: checksum - standard 1s complement checksum
410*
411*****************************************************************/
412unsigned short checksum(void *netPacket, int len)
413{
414
415	unsigned short *packetPtr = netPacket, result;
416
417	unsigned int sum = 0;
418
419	for (sum = 0; len > 1; len -= 2) {
420		sum += *packetPtr++;
421	}
422
423	if (len == 1) {
424		sum += *(unsigned char *)packetPtr;
425	}
426
427	sum = (sum >> 16) + (sum & 0xFFFF);
428	sum += (sum >> 16);
429
430	result = ~sum;
431
432	return result;
433}
434
435/*****************************************************************
436* Function: output_to_display - Output to display info. from the
437*                               listener
438******************************************************************/
439void output_to_display(void *netPacket, int bytes, int pid)
440{
441
442	int i;
443	struct iphdr *ip = netPacket;
444	struct icmphdr *icmpPtr = netPacket + ip->ihl * 4;
445	struct in_addr tmp_addr;
446
447	printf
448	    ("\n************** -- Ping Tests - **********************************************\n");
449
450	for (i = 0; i < bytes; i++) {
451		if (!(i & 15)) {
452			printf("\n[%d]:  ", i);
453		}
454
455		printf("[%d] ", ((unsigned char *)netPacket)[i]);
456	}
457
458	printf("\n");
459
460	tmp_addr.s_addr = ip->saddr;
461
462	printf("IPv%d: hdr-size=%d pkt-size=%d protocol=%d TTL=%d src=%s ",
463	       ip->version, ip->ihl * 4, ntohs(ip->tot_len), ip->protocol,
464	       ip->ttl, inet_ntoa(tmp_addr));
465
466	tmp_addr.s_addr = ip->daddr;
467	printf("dst=%s\n", inet_ntoa(tmp_addr));
468
469	if (icmpPtr->un.echo.id == pid) {
470
471		printf("ICMP: type[%d/%d] checksum[%d] id[%d] seq[%d]\n\n",
472		       icmpPtr->type, icmpPtr->code, ntohs(icmpPtr->checksum),
473		       icmpPtr->un.echo.id, icmpPtr->un.echo.sequence);
474
475	}
476}
477
478/***********************************************************************
479* Function: ping_network - Build a message and send it.
480*
481*
482***********************************************************************/
483void ping_network(struct sockaddr_in *rawAddr, int pid)
484{
485
486	const int value = TIMETOLIVE;
487	int i, rawSocket, count = 1;
488
489	struct packet rawPacket;
490
491	struct sockaddr_in r_addr;
492
493	rawSocket = socket(PF_INET, SOCK_RAW, protocol->p_proto);
494
495	if (rawSocket < 0) {
496		printf("Error: cannot open RAW socket %d\n", rawSocket);
497		return;
498	}
499
500	if (setsockopt(rawSocket, SOL_IP, IP_TTL, &value, sizeof(value)) != 0) {
501		printf("ERROR: Setting TimeToLive option");
502	} else {
503		printf
504		    ("The test will run for [%d] iterations -- Ctrl-C to interupt \n",
505		     value);
506		sleep(3);
507	}
508
509	if (fcntl(rawSocket, F_SETFL, O_NONBLOCK) != 0) {
510		printf("ERROR: Failed request nonblocking I/O");
511	}
512
513	while (1) {
514
515		socklen_t msgLength = sizeof(r_addr);
516
517		printf("Message ID #:%d \n", count);
518
519		if (recvfrom
520		    (rawSocket, &rawPacket, sizeof(rawPacket), 0,
521		     (struct sockaddr *)&r_addr, &msgLength) > 0) {
522			printf("*** -- Message Received -- ***\n");
523		}
524
525		memset(&rawPacket, 0, sizeof(rawPacket));
526
527		rawPacket.hdr.type = ICMP_ECHO;
528		rawPacket.hdr.un.echo.id = pid;
529
530		for (i = 0; i < sizeof(rawPacket.msg) - 1; i++) {
531			rawPacket.msg[i] = i + '0';
532		}
533
534		rawPacket.msg[i] = 0;
535		rawPacket.hdr.un.echo.sequence = count++;
536		rawPacket.hdr.checksum =
537		    checksum(&rawPacket, sizeof(rawPacket));
538
539		if (sendto
540		    (rawSocket, &rawPacket, sizeof(rawPacket), 0,
541		     (struct sockaddr *)rawAddr, sizeof(*rawAddr)) <= 0)
542			printf("ERROR: sendto failed !!");
543
544		sleep(1);
545
546		if (value == count) {
547			printf("Exiting ping test...\n");
548			break;
549		}
550	}
551
552	close(rawSocket);
553}
554
555/**********************************************************************
556*  Function: ltp_traceroute
557*                      try to reach the destination
558*                      while outputting hops along the route
559***********************************************************************/
560void ltp_traceroute(struct sockaddr_in *rawTraceAddr, char *hostName, int pid)
561{
562
563	const int flag = TRUE;
564	int TimeToLive = 0;
565	int i, rawTraceSocket, count = 1;
566	socklen_t length;
567	struct packet rawTracePacket;
568	unsigned char tracePacket[PACKET_LEN];
569	struct sockaddr_in rawReceiveAddr;
570	struct hostent *hostEntry2;
571	struct in_addr tmp_addr;
572
573	printf
574	    ("\n************** -- Trace Route Tests - **********************************************\n");
575
576	rawTraceSocket = socket(PF_INET, SOCK_RAW, protocol->p_proto);
577
578	if (rawTraceSocket < 0) {
579		printf("Error: cannot open RAW socket %d\n", rawTraceSocket);
580		return;
581	}
582
583	if (setsockopt(rawTraceSocket, SOL_IP, SO_ERROR, &flag, sizeof(flag)) !=
584	    0)
585		printf("ERROR: Setting socket options");
586
587	do {
588		struct iphdr *ip;
589		length = sizeof(rawReceiveAddr);
590
591		TimeToLive++;
592		if (setsockopt
593		    (rawTraceSocket, SOL_IP, IP_TTL, &TimeToLive,
594		     sizeof(TimeToLive)) != 0) {
595			printf("ERROR: Setting TimeToLive option");
596		}
597
598		memset(&rawTracePacket, 0, sizeof(rawTracePacket));
599
600		rawTracePacket.hdr.type = ICMP_ECHO;
601		rawTracePacket.hdr.un.echo.id = pid;
602
603		for (i = 0; i < sizeof(rawTracePacket.msg) - 1; i++) {
604			rawTracePacket.msg[i] = i + '0';
605		}
606
607		rawTracePacket.msg[i] = 0;
608		rawTracePacket.hdr.un.echo.sequence = count++;
609		rawTracePacket.hdr.checksum =
610		    checksum(&rawTracePacket, sizeof(rawTracePacket));
611
612		if (sendto
613		    (rawTraceSocket, &rawTracePacket, sizeof(rawTracePacket), 0,
614		     (struct sockaddr *)rawTraceAddr,
615		     sizeof(*rawTraceAddr)) <= 0) {
616			printf("ERROR: sendto failed !!");
617		}
618		sleep(1);
619
620		if (recvfrom
621		    (rawTraceSocket, tracePacket, sizeof(tracePacket),
622		     MSG_DONTWAIT, (struct sockaddr *)&rawReceiveAddr,
623		     &length) > 0) {
624			ip = (void *)tracePacket;
625
626			tmp_addr.s_addr = ip->saddr;
627			printf("Host IP:#%d: %s \n", count - 1,
628			       inet_ntoa(tmp_addr));
629
630			hostEntry2 =
631			    gethostbyaddr((void *)&rawReceiveAddr, length,
632					  rawReceiveAddr.sin_family);
633
634			if (hostEntry2 != NULL)
635				printf("(%s)\n", hostEntry2->h_name);
636			else
637				perror("Name: ");
638		} else {
639			printf("%s : data send complete...\n", hostName);
640			break;
641		}
642
643	}
644	while (rawReceiveAddr.sin_addr.s_addr != rawTraceAddr->sin_addr.s_addr);
645
646	printf
647	    ("\n************** -- End Trace Route Tests - ******************************************\n");
648
649	close(rawTraceSocket);
650}
651