blob: 1196312497fb3aa24ca7d47ee25d34d7f4059e75 [file] [log] [blame]
/*
#
#
# Task Subprogram
#
# SUBPROGRAM NAME: PINGPONG.C
#
# REQUIRED PARAMETERS:
# Calling Procedure: pingpong HOST SIZE PACKETS
# HOST - Current host
# SIZE - Size of each packet
# PACKETS - the number of packets across the network
#
# SETUP REQUIRED:
# o This task must be run as root.
# o TCP/IP must be configured before executing this task.
#
# DESCRIPTION:
# Purpose:To generate lan traffic with ICMP echo packet
# Command: None
# Subcommand:None
# Design:
# Create raw socket
# spawn child process to send echo ICMP packet
# the child process build echo packet and send to network
# repeat n times
# the parent goes on to receive the reply
# when finish print # of packets sent & # pf packets received
#
#
#===========================================================================
*/
#define PS2
#include <stdio.h>
#include <errno.h>
#include <stdint.h>
#include <string.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/file.h>
#include <sys/signal.h>
#include <arpa/inet.h>
#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <netdb.h>
#include "test.h"
#include "netdefs.h"
#define MAXPACKET 4096 /* max packet size */
#ifndef MAXHOSTNAMELEN
#define MAXHOSTNAMELEN 64
#endif
#if INET6
char *TCID = "perf_lan6";
#else
char *TCID = "perf_lan";
#endif
int TST_TOTAL = 1;
int pfd[2];
int fdstr[10];
int verbose;
int count;
uint8_t packet[MAXPACKET];
int options;
int s; /* Socket file descriptor */
struct hostent *hp; /* Pointer to host info */
struct timezone tz; /* leftover */
sai_t whereto; /* Who to pingpong */
int datalen; /* How much data */
char *hostname;
char hnamebuf[MAXHOSTNAMELEN];
int npackets = 1;
int ntransmitted = 0; /* sequence number for outbound
* packets => amount sent */
int ident;
int nwrite;
int nreceived = 0; /* # of packets we got back */
int timing = 0;
void finish(int);
uint16_t in_cksum(uint16_t *, int);
int ck_packet(uint8_t *, size_t, sai_t *);
int echopkt(int, int);
/*
* M A I N
*/
int main(int argc, char *argv[])
{
sai_t from;
int rc = 0;
struct protoent *proto;
tst_resm(TINFO, "Starting pingpong - to send / receive packets from "
"host");
/* Get Host net address */
tst_resm(TINFO, "Get host net address for sending packets");
memset((char *)&whereto, 0, sizeof(sa_t));
#if INET6
whereto.sin6_family = AFI;
whereto.sin6_addr.s6_addr = inet_addr(argv[1]);
if (whereto.sin6_addr.s6_addr != -1) {
#else
whereto.sin_family = AFI;
whereto.sin_addr.s_addr = inet_addr(argv[1]);
if (whereto.sin_addr.s_addr != -1) {
#endif
strcpy(hnamebuf, argv[1]);
hostname = hnamebuf;
} else {
if ((hp = gethostbyname(argv[1])) != NULL) {
#if INET6
whereto.sin6_family = hp->h_addrtype;
memcpy((caddr_t) & whereto.sin6_addr, hp->h_addr,
hp->h_length);
#else
whereto.sin_family = hp->h_addrtype;
memcpy((caddr_t) & whereto.sin_addr, hp->h_addr,
hp->h_length);
#endif
hostname = hp->h_name;
} else {
tst_brkm(TBROK, NULL, "%s: unknown host, couldn't get "
"address", argv[0]);
}
}
/* Determine Packet Size - either use what was passed in or the default */
tst_resm(TINFO, "Determining packet size");
if (argc >= 3)
datalen = atoi(argv[2]);
if (datalen < 0) {
tst_brkm(TBROK, NULL, "datalen must be an integer.");
} else
datalen = 64;
datalen -= 8;
if (datalen > MAXPACKET) {
tst_brkm(TBROK, NULL, "packet size too large");
}
if (datalen >= sizeof(struct timeval))
timing = 1;
/* Set number of packets to be sent */
tst_resm(TINFO, "Determining number of packets to send");
if (argc >= 4)
npackets = atoi(argv[3]);
/* Get PID of current process */
ident = getpid() & 0xFFFF;
/* Get network protocol to use (check /etc/protocol) */
if ((proto = getprotobyname(ICMP_PROTO)) == NULL) {
tst_resm(TINFO, "unknown protocol: %s", ICMP_PROTO);
tst_exit();
}
/* Create a socket endpoint for communications - returns a descriptor */
if ((s = socket(AFI, SOCK_RAW, proto->p_proto)) < 0) {
tst_resm(TINFO, "socket - could not create link");
tst_exit();
}
tst_resm(TINFO, "echoing %s: %d data bytes", hostname, datalen);
setlinebuf(stdout);
/* Setup traps */
signal(SIGINT, finish);
signal(SIGCLD, finish);
/* Fork a child process to continue sending packets */
tst_resm(TINFO, "Create a child process to continue to send packets");
switch (fork()) {
case -1:
tst_resm(TINFO, "ERROR when forking a new process");
tst_exit();
case 0:
/* Child's work */
ntransmitted = echopkt(datalen, npackets);
tst_resm(TINFO, "%d packets transmitted", ntransmitted);
sleep(10);
break;
default:
tst_resm(TINFO, "Parent started - to receive packets");
/* Parent's work - receive packets back from child */
size_t len;
while (1) {
len = sizeof(packet);
ssize_t cc;
socklen_t fromlen;
/* Receive packet from socket */
tst_resm(TINFO, "Receiving packet");
if ((cc =
recvfrom(s, packet, len, 0, (sa_t *) & from,
&fromlen)) < 0) {
tst_resm(TINFO, "ERROR - recvfrom");
}
/* Verify contents of packet */
if ((rc = ck_packet(packet, cc, &from)) != 0) {
tst_resm(TINFO,
"ERROR - network garbled packet");
} else {
nreceived++;
}
}
}
tst_exit();
}
int echopkt(int datalen, int npackets)
{
int count = 0;
static uint8_t outpack[MAXPACKET];
register icmp_t *icp = (icmp_t *) outpack;
int i;
ssize_t cc;
register u_char *datap = &outpack[8];
/* Setup the packet structure */
tst_resm(TINFO, "Setting up ICMP packet structure to send to host");
#if INET6
icp->icmp6_type = IERQ;
icp->icmp6_code = 0;
icp->icmp6_id = ident; /* ID */
#else
icp->icmp_type = IERQ;
icp->icmp_code = 0;
icp->icmp_id = ident; /* ID */
#endif
cc = datalen + 8; /* skips ICMP portion */
for (i = 8; i < datalen; i++) { /* skip 8 for time */
*datap++ = i;
}
/* Compute ICMP checksum here */
#if INET6
icp->icmp6_cksum = in_cksum((uint16_t *) icp, cc);
#else
icp->icmp_cksum = in_cksum((uint16_t *) icp, cc);
#endif
/* cc = sendto(s, msg, len, flags, to, tolen) */
ntransmitted = 0;
while (count < npackets) {
count++;
/* Send packet through socket created */
tst_resm(TINFO, "Sending packet through created socket");
i = sendto(s, outpack, cc, 0, (const sa_t *)&whereto,
sizeof(whereto));
if (i < 0 || i != cc) {
if (i < 0)
perror("sendto");
tst_resm(TINFO, "wrote %s %zd chars, ret=%d",
hostname, cc, i);
fflush(stdout);
}
}
/* sleep(30); */
return (count);
}
/*
* I N _ C K S U M
*
* Checksum routine for Internet Protocol family headers (C Version)
*
*/
uint16_t in_cksum(uint16_t * addr, int len)
{
register int nleft = len;
register uint16_t *w = addr, tmp;
register int sum = 0;
register uint16_t answer = 0;
/*
* Our algorithm is simple, using a 32 bit accumulator (sum), we add
* sequential 16 bit words to it, and at the end, fold back all the
* carry bits from the top 16 bits into the lower 16 bits.
*/
while (nleft > 1) {
sum += *w++;
nleft -= 2;
}
/* mop up an odd byte, if necessary */
if (nleft == 1) {
tmp = *(u_char *) w;
sum += (tmp << 8);
}
/* add back carry outs from top 16 bits to low 16 bits */
sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
sum += (sum >> 16); /* add carry */
answer = ~sum; /* truncate to 16 bits */
return answer;
}
/*
* F I N I S H
*
* Outputs packet information to confirm transmission and reception.
*/
void finish(int n)
{
tst_resm(TINFO, "%d packets received", nreceived);
exit(0);
}
/*
* C K _ P A C K E T
*
* Checks contents of packet to verify information did not get destroyed
*/
/*
* buf - pointer to start of IP header
* cc - total size of received packet
* from - address of sender
*/
int ck_packet(uint8_t * buf, size_t cc, sai_t * from)
{
u_char i;
int iphdrlen;
struct ip *ip = (struct ip *)buf; /* pointer to IP header */
register icmp_t *icp; /* ptr to ICMP */
u_char *datap;
#if INET6
from->sin6_addr.s6_addr = ntohl(from->sin6_addr.s6_addr);
#else
from->sin_addr.s_addr = ntohl(from->sin_addr.s_addr);
#endif
iphdrlen = ip->ip_hl << 2; /* Convert # 16-bit words to
* number of bytes */
cc -= iphdrlen;
icp = (icmp_t *) (buf + iphdrlen);
datap = (u_char *) icp + sizeof(struct timeval) + 8;
if (icp->icmp_type != IERP) {
return 0; /* Not your packet because it's
* not an echo */
}
if (icp->icmp_id != ident) {
return 0; /* Sent to us by someone
* else */
}
/* Verify data in packet */
tst_resm(TINFO, "Verify data in packet after returned from sender");
if (datalen > 118) {
datalen = 118;
}
tst_resm(TINFO, "Checking Data.");
for (i = 8; i < datalen; i++) { /* skip 8 for time */
if (i != (*datap)) {
tst_resm(TINFO, "Data cannot be validated.");
}
datap++;
}
return 0;
}