test_client.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 1993-2003 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <ctype.h>
#include <string.h>
#include <syslog.h>
#include <signal.h>
#include <time.h>
#include <limits.h>
#include <sys/resource.h>
#include <sys/fcntl.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/systeminfo.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <net/if.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <sys/stropts.h>
#include <netinet/dhcp.h>
#include <dhcp_impl.h>
#include <synch.h>
#include <netdb.h>
#include <locale.h>
#include <mtmalloc.h>
#include <tnf/probe.h>
#include <libinetutil.h>
struct client {
thread_t id;
PKT_LIST *pktlistp;
cond_t cv;
cond_t acv;
mutex_t mtx;
int proto;
uchar_t chaddr[20];
char chost[40];
int hlen;
uint_t xid;
int flags;
time_t ltime;
int state;
};
#define CLIENT_BUSY 0x1
#define CLIENT_FIRSTTIME 0x2
ushort_t port_offset = 0; /* offset to port for multiple server */
int fast = 0; /* higher load */
int bound = 0; /* only broadcast on given interface */
int lrecv = 1; /* only receive on given interface */
static struct in_addr relay; /* spoof being a relay agent */
static struct sockaddr_in from, relfrom;
static int clients, s, srelay = -1;
static struct client *clientsp;
static PKT request;
static char ifname[IFNAMSIZ];
static int startindex;
static mutex_t go_mtx;
static cond_t go_cv;
static boolean_t time_to_go;
static int release_time = 0;
static int desynch = 1;
static double avg = 0;
static timespec_t avgslp;
static volatile ulong_t tops, otops;
static volatile ulong_t minops[6];
static volatile time_t mintim[6];
static volatile int minind;
long sample_time = 10L;
long nsamples = 2;
static volatile ulong_t ops_outstanding;
static time_t start, ostart;
int verbose = 0;
int dohost = 0;
int randcl = 0;
int randhlen = 0;
int randerr = 0;
int dos = 0;
int dofork = 0;
int printid = 0;
static time_t ltime;
static struct lifreq lifr;
static void corrupt(char *, int);
static void
dhcpmsgtype(uchar_t pkt, char *buf)
{
char *p;
switch (pkt) {
case DISCOVER:
p = "DISCOVER";
break;
case OFFER:
p = "OFFER";
break;
case REQUEST:
p = "REQUEST";
break;
case DECLINE:
p = "DECLINE";
break;
case ACK:
p = "ACK";
break;
case NAK:
p = "NAK";
break;
case RELEASE:
p = "RELEASE";
break;
case INFORM:
p = "INFORM";
break;
default:
p = "UNKNOWN";
break;
}
(void) strcpy(buf, p);
}
static int
closeif(int ms, char *cifname, struct sockaddr_in *myip, thread_t myself)
{
struct ifreq ifr;
int error = 0;
(void) strcpy(ifr.ifr_name, cifname);
if (ioctl(ms, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) {
(void) fprintf(stderr,
"Client %04d - can't get interface flags on %s\n", myself,
cifname);
error = 7;
}
ifr.ifr_flags &= ~IFF_UP;
if (ioctl(ms, SIOCSIFFLAGS, (caddr_t)&ifr) < 0) {
(void) fprintf(stderr,
"Client %04d - can't set interface flags on %s\n", myself,
cifname);
error = 7;
}
myip->sin_addr.s_addr = htonl(INADDR_ANY);
ifr.ifr_addr = *(struct sockaddr *)myip;
if (ioctl(ms, SIOCSIFADDR, (caddr_t)&ifr)) {
(void) fprintf(stderr,
"Client %04d - Can't unset address on %s\n", myself,
cifname);
error = 8;
}
(void) close(ms);
return (error);
}
static void *
client(void *args)
{
PKT crequest, *irequestp;
PKT_LIST *bp = NULL, *wbp, *twbp;
struct client *mep = (struct client *)args;
time_t retry_time = 2, lease, sleep_time = 0;
uchar_t *endp;
boolean_t done, config, timeout;
int nstate, ms = -1;
DHCP_OPT *optp, *unused_optp;
timespec_t ts, tr;
int error = 0;
thread_t myself = thr_self();
struct sockaddr_in to, myip, maskip;
struct in_addr serverip;
time_t start_time, expired = 0;
char cid[BUFSIZ];
char cifname[IFNAMSIZ];
char host[40];
char domain[40];
char p[30], np[30];
struct ifreq ifr;
int moldy;
int i;
uint_t cidlen;
char *domainp;
forever:
if (bp) {
(void) free(bp->pkt);
(void) free(bp);
bp = NULL;
}
if (!time_to_go) {
(void) mutex_lock(&mep->mtx);
mep->flags &= ~CLIENT_BUSY;
if (mep->flags & CLIENT_FIRSTTIME) {
mep->flags &= ~CLIENT_FIRSTTIME;
} else {
tops++;
ops_outstanding--;
}
if (avg)
(void) cond_wait(&mep->acv, &mep->mtx);
mep->flags |= CLIENT_BUSY;
mep->ltime = time(NULL);
ops_outstanding++;
(void) mutex_unlock(&mep->mtx);
}
if (desynch)
(void) sleep((desynch & myself) + 3); /* desynch clients */
if (verbose == 1)
(void) fprintf(stdout, "Client %04d - started.\n", myself);
start_time = time(NULL);
(void) sprintf(cifname, "%s:%d", ifname, startindex + myself);
/* reset client addr each time */
if (relay.s_addr != INADDR_ANY) {
to.sin_addr.s_addr = relay.s_addr;
} else {
to.sin_addr.s_addr = INADDR_BROADCAST;
}
to.sin_port = htons(IPPORT_BOOTPS + port_offset);
to.sin_family = AF_INET;
domain[0] = host[0] = NULL;
if (randcl) {
/* Further randomize. */
if (randhlen > 0) {
mep->hlen = randhlen;
}
for (i = 3; i < mep->hlen; i++) {
mep->chaddr[i] = random() & 0xff;
}
}
(void) memcpy(&crequest, &request, sizeof (request));
(void) memcpy(crequest.chaddr, mep->chaddr, mep->hlen);
crequest.hlen = mep->hlen;
if (mep->proto) {
mep->state = DISCOVER;
optp = (DHCP_OPT *) & crequest.options[3]; /* skip TYPE */
optp->code = CD_CLIENT_ID;
optp->len = mep->hlen + 1;
optp->value[0] = 0x01;
(void) memcpy(&optp->value[1], mep->chaddr, mep->hlen);
cidlen = sizeof (cid);
(void) octet_to_hexascii(optp->value, mep->hlen + 1, cid,
&cidlen);
unused_optp = (DHCP_OPT *) & optp->value[mep->hlen + 1];
} else {
mep->state = 0;
cidlen = sizeof (cid);
(void) octet_to_hexascii(mep->chaddr, mep->hlen, cid, &cidlen);
unused_optp = (DHCP_OPT *)&crequest.options[3]; /* skip TYPE */
}
/* Use global descriptor at first */
ms = s;
myip.sin_addr.s_addr = htonl(INADDR_ANY);
done = B_FALSE;
config = B_FALSE;
do {
timeout = B_FALSE;
TNF_PROBE_2(client,
"client",
"client%debug 'in func client'",
tnf_ulong, state, mep->state,
tnf_string, cid, (char *)cid);
if (time_to_go) {
if (mep->state == ACK) {
mep->state = RELEASE;
if (verbose == 1)
(void) fprintf(stderr,
"Client %04d - RELEASEing %s\n",
myself, inet_ntoa(myip.sin_addr));
else if (verbose == 2)
fprintf(stderr, "[%d %s]",
clientsp[i].id,
inet_ntoa(myip.sin_addr));
optp = (DHCP_OPT *) crequest.options;
(void) memset((char *)unused_optp, 0,
(int)((char *)&crequest.options[
sizeof (crequest.options)] -
(char *)unused_optp));
optp->value[0] = RELEASE;
} else {
done = B_TRUE;
if (verbose == 1)
(void) fprintf(stderr,
"Client %04d - terminated.\n",
myself);
break;
}
} else if (release_time || avg) {
if (mep->state == ACK) {
/* lru testing: don't release lease */
if (randcl & 0x2) {
done = B_FALSE;
mep->state = nstate = 0;
sleep_time = 0;
if (bp) {
(void) free(bp->pkt);
(void) free(bp);
bp = NULL;
}
goto forever;
}
mep->state = RELEASE;
if (verbose == 1)
(void) fprintf(stderr,
"Client %04d - RELEASEing %s\n",
myself, inet_ntoa(myip.sin_addr));
else if (verbose == 2)
fprintf(stderr, "[%d %s]",
clientsp[i].id,
inet_ntoa(myip.sin_addr));
optp = (DHCP_OPT *) crequest.options;
(void) memset((char *)unused_optp, 0,
(int)((char *)&crequest.options[
sizeof (crequest.options)] -
(char *)unused_optp));
optp->value[0] = RELEASE;
}
}
if (mep->state == REQUEST && expired < time(NULL)) {
/* drop back to INIT state. */
if (verbose == 1)
(void) fprintf(stderr,
"Client %04d - Dropping back to INIT.\n",
myself);
done = B_FALSE;
mep->state = nstate = 0;
sleep_time = 0;
if (bp) {
(void) free(bp->pkt);
(void) free(bp);
bp = NULL;
}
goto forever;
}
if (mep->state == RELEASE && !time_to_go) {
(void) mutex_lock(&mep->mtx);
tops++;
ops_outstanding--;
mep->flags &= ~CLIENT_BUSY;
if (avg)
(void) cond_wait(&mep->acv, &mep->mtx);
mep->ltime = time(NULL);
ops_outstanding++;
mep->flags |= CLIENT_BUSY;
(void) mutex_unlock(&mep->mtx);
}
/* Send request... */
crequest.secs = htons((ushort_t)(time(NULL) - start_time));
crequest.xid = htonl((myself << 2) + mep->xid++);
/* Randomly corrupt packets of a certain type. */
if ((randerr & 0xF) == mep->state || (randerr & 0xF) == 0xF) {
if (randerr & 0x10) {
/* Randomly corrupt entire request. */
corrupt((char *)&crequest, sizeof (crequest));
} else {
/* Randomly corrupt options. */
corrupt((char *)&crequest.options[3],
sizeof (crequest.options) - 3);
}
}
if (sendto(ms, (char *)&crequest, sizeof (PKT), 0,
(struct sockaddr *)&to, sizeof (struct sockaddr)) < 0) {
perror("Sendto");
error = 4;
thr_exit(&error);
}
if (mep->state == RELEASE) {
done = B_TRUE;
if (!avg) {
(void) strcpy(ifr.ifr_name, cifname);
if (ioctl(ms, SIOCGIFFLAGS,
(caddr_t)&ifr) < 0) {
(void) fprintf(stderr, "Client %04d - "
"can't get interface flags on %s\n",
myself, cifname);
error = 7;
}
ifr.ifr_flags &= ~IFF_UP;
if (ioctl(ms, SIOCSIFFLAGS,
(caddr_t)&ifr) < 0) {
(void) fprintf(stderr, "Client %04d - "
"can't set interface flags on %s\n",
myself, cifname);
error = 7;
}
myip.sin_addr.s_addr = htonl(INADDR_ANY);
ifr.ifr_addr = *(struct sockaddr *)&myip;
if (ioctl(ms, SIOCSIFADDR, (caddr_t)&ifr)) {
(void) fprintf(stderr, "Client %04d - "
"Can't unset address on %s\n",
myself, cifname);
error = 8;
}
(void) close(ms);
}
if (release_time || avg) {
done = B_FALSE;
mep->state = nstate = 0;
sleep_time = 0;
if (bp) {
(void) free(bp->pkt);
(void) free(bp);
bp = NULL;
}
goto forever;
}
break;
}
/* await reply */
moldy:
(void) mutex_lock(&mep->mtx);
ts.tv_sec = time(NULL) + retry_time;
ts.tv_nsec = 0;
while (mep->pktlistp == NULL)
if (cond_timedwait(&mep->cv, &mep->mtx, &ts) == ETIME) {
timeout = B_TRUE;
if (retry_time > 64)
retry_time = 2;
else if (fast)
retry_time += 2;
else
retry_time *= 2;
break;
} else {
if (time_to_go)
break;
}
(void) mutex_unlock(&mep->mtx);
if (time_to_go || timeout)
continue;
(void) mutex_lock(&mep->mtx);
moldy = 0;
if (bp) {
(void) free(bp->pkt);
(void) free(bp);
}
bp = NULL;
wbp = mep->pktlistp;
while (wbp != NULL) {
irequestp = wbp->pkt;
if (bp == NULL && irequestp->op == BOOTREPLY &&
memcmp(&crequest.xid, &irequestp->xid,
sizeof (crequest.xid)) == 0) {
bp = wbp;
wbp = wbp->next;
continue;
}
(void) free(wbp->pkt);
twbp = wbp;
wbp = wbp->next;
(void) free(twbp);
if (verbose == 1)
(void) fprintf(stderr,
"Client %04d - Moldy xid\n", myself);
moldy++;
}
mep->pktlistp = NULL;
(void) mutex_unlock(&mep->mtx);
if (bp == NULL) {
if (moldy > 0)
goto moldy;
continue;
}
irequestp = bp->pkt;
if (mep->proto) {
/*
* Scan for CD_DHCP_TYPE, CD_SERVER_ID, and
* CD_LEASE_TIME if proto.
*/
nstate = 0;
maskip.sin_addr.s_addr = serverip.s_addr = INADDR_ANY;
maskip.sin_family = AF_INET;
lease = (time_t)0;
optp = (DHCP_OPT *) irequestp->options;
endp = (uchar_t *)irequestp + bp->len;
host[0] = NULL;
while ((uchar_t *)optp < (uchar_t *)endp) {
switch (optp->code) {
case CD_HOSTNAME:
(void) strncpy(host,
(const char *)optp->value,
optp->len);
host[optp->len] = '\0';
break;
case CD_DNSDOMAIN:
(void) strncpy(domain,
(const char *)optp->value,
optp->len);
domain[optp->len] = '\0';
break;
case CD_DHCP_TYPE:
nstate = optp->value[0];
break;
case CD_SUBNETMASK:
(void) memcpy(&maskip.sin_addr,
optp->value,
sizeof (struct in_addr));
break;
case CD_SERVER_ID:
(void) memcpy(&serverip, optp->value,
sizeof (struct in_addr));
break;
case CD_LEASE_TIME:
(void) memcpy(&lease, optp->value,
sizeof (time_t));
lease = htonl(lease);
break;
}
optp = (DHCP_OPT *) & optp->value[optp->len];
}
if (mep->state == DISCOVER && nstate == OFFER) {
mep->state = REQUEST;
expired = time(NULL) + 60;
/*
* Add in the requested IP address option and
* server ID.
*/
optp = (DHCP_OPT *) crequest.options;
optp->value[0] = REQUEST;
optp = unused_optp; /* step over CD_DHCP_TYPE */
optp->code = CD_REQUESTED_IP_ADDR;
optp->len = sizeof (struct in_addr);
(void) memcpy(optp->value, &irequestp->yiaddr,
sizeof (struct in_addr));
optp = (DHCP_OPT *) & optp->value[
sizeof (struct in_addr)];
optp->code = CD_SERVER_ID;
optp->len = sizeof (struct in_addr);
(void) memcpy(optp->value, &serverip,
sizeof (struct in_addr));
optp = (DHCP_OPT *) & optp->value[
sizeof (struct in_addr)];
if (dohost == 0) {
if (bp) {
(void) free(bp->pkt);
(void) free(bp);
bp = NULL;
}
continue;
}
if (domain[0] == '\0' && host[0] != '\0' &&
(domainp = strchr(host, '.')) != NULL) {
(void) snprintf(domain, sizeof (domain),
"%s", domainp);
}
if (dohost & 0x2) {
cidlen = sizeof (cid);
(void) octet_to_hexascii(mep->chaddr,
mep->hlen, host, &cidlen);
if (domain[0])
(void) snprintf(host,
sizeof (host), "%s.%s",
cid, domain);
else
(void) snprintf(host,
sizeof (host), "%s", cid);
}
optp->code = CD_HOSTNAME;
optp->len = strlen(host);
(void) memcpy(optp->value, host, strlen(host));
optp->value[strlen(host)] = '\0';
if (randcl && (random() & 0x1)) {
/* create a random name */
for (i = 0; i < optp->len &&
optp->value[i] != '.'; i++)
if (i & 1)
optp->value[i] = '0' +
(mep->chaddr[i] & 0x7);
else
optp->value[i] = 'a' +
(mep->chaddr[i] & 0x7);
strcpy((char *)mep->chost,
(const char *)optp->value);
} else if (randcl && mep->chost[0]) {
/* use the previous one */
optp->len = strlen(mep->chost);
(void) memcpy(optp->value, mep->chost,
strlen(mep->chost));
optp->value[strlen(mep->chost)] = '\0';
}
if (bp) {
(void) free(bp->pkt);
(void) free(bp);
bp = NULL;
}
continue;
} else if ((mep->state == REQUEST ||
mep->state == ACK) && nstate == ACK) {
/*
* we're bound. defend the lease. Add the
* address to our interface. Due to the
* service architecture of this program, we
* can't unset the broadcast bit..
*/
mep->state = ACK;
nstate = 0;
retry_time = 2;
myip.sin_family = AF_INET;
myip.sin_addr.s_addr = irequestp->yiaddr.s_addr;
crequest.ciaddr.s_addr = myip.sin_addr.s_addr;
optp = unused_optp;
optp->code = CD_LEASE_TIME;
optp->len = sizeof (time_t);
(void) memcpy(optp->value, &lease,
sizeof (time_t));
optp = (DHCP_OPT *)
& optp->value[sizeof (time_t)];
(void) memset((char *)optp, 0, (int)((char *)
&crequest.options[
sizeof (crequest.options)] -
(char *)optp));
to.sin_addr.s_addr = serverip.s_addr;
if (lease == -1) {
done = B_TRUE; /* permanent lease */
sleep_time = 0;
} else {
sleep_time = lease / 2;
lease = time(NULL) + lease;
}
if (release_time || avg) {
sleep_time = release_time;
done = B_FALSE;
}
if (verbose == 1)
(void) fprintf(stdout,
"Client %04d(%s) - DHCP: %s == %s",
myself, cid,
inet_ntoa(myip.sin_addr),
(lease == -1) ? "Forever\n" :
ctime(&lease));
else if (verbose == 2)
fprintf(stderr, "(%d %s)", mep->id,
cid);
if (!config && !avg) {
/* Add mask and address */
if ((ms = socket(AF_INET, SOCK_DGRAM,
0)) < 0) {
(void) fprintf(stderr,
"Client %04d - can't open "
"DGRAM socket.\n", myself);
error = 7;
break;
}
(void) strcpy(ifr.ifr_name, cifname);
ifr.ifr_addr =
*(struct sockaddr *)&myip;
/*
* XXXX: needed in on81
* for initial
* interface creation
*/
(void) (ioctl(ms, SIOCLIFADDIF,
(caddr_t)&ifr));
(void) strcpy(ifr.ifr_name, cifname);
ifr.ifr_addr =
*(struct sockaddr *)&maskip;
if (ioctl(ms, SIOCSIFNETMASK,
(caddr_t)&ifr)) {
(void) fprintf(stderr,
"Client %04d - Can't set "
"netmask: %s on %s\n",
myself,
inet_ntoa(maskip.sin_addr),
cifname);
error = 7;
(void) close(ms);
break;
}
if (ioctl(ms, SIOCGIFFLAGS,
(caddr_t)&ifr) < 0) {
(void) fprintf(stderr,
"Client %04d - can't get "
"interface flags on %s\n",
myself, cifname);
error = 7;
(void) close(ms);
break;
}
ifr.ifr_flags |= IFF_UP;
if (ioctl(ms, SIOCSIFFLAGS,
(caddr_t)&ifr) < 0) {
(void) fprintf(stderr,
"Client %04d - can't set "
"interface flags on %s\n",
myself, cifname);
error = 7;
(void) close(ms);
break;
}
ifr.ifr_addr =
*(struct sockaddr *)&myip;
if (ioctl(ms, SIOCSIFADDR,
(caddr_t)&ifr)) {
(void) fprintf(stderr,
"Client %04d - Can't set "
"address on %s\n",
myself, cifname);
error = 8;
(void) close(ms);
break;
}
config = B_TRUE;
}
if (sleep_time != 0) {
/* Go to sleep for 50% of lease time. */
tr.tv_sec = time(NULL) + sleep_time;
if (verbose == 1)
(void) fprintf(stderr,
"Client %04d - sleeping "
"until %s", myself,
ctime(&tr.tv_sec));
tr.tv_nsec = 0;
(void) mutex_lock(&go_mtx);
while (!time_to_go) {
if (cond_timedwait(&go_cv,
&go_mtx, &tr) == ETIME)
break;
}
(void) mutex_unlock(&go_mtx);
if (verbose == 1)
(void) fprintf(stderr,
"Client %04d - awake\n",
myself);
}
} else if (mep->state == ACK && nstate == NAK) {
/* drop back to INIT state. */
if (verbose == 1) {
(void) fprintf(stdout, "Client %04d - "
"DHCP: we got NAKed.\n", myself);
(void) fprintf(stderr, "Client %04d - "
"Dropping back to INIT.\n", myself);
}
if (!avg)
(void) closeif(ms, cifname,
&myip, myself);
done = B_FALSE;
mep->state = nstate = 0;
sleep_time = 0;
if (bp) {
(void) free(bp->pkt);
(void) free(bp);
bp = NULL;
}
goto forever;
} else {
dhcpmsgtype(nstate, np);
dhcpmsgtype(mep->state, p);
(void) fprintf(stderr, "Client %04d - "
"unexpected mesg: %s, when I'm in state: "
"%s.\n", myself, np, p);
error = 9;
break;
}
} else {
done = B_TRUE; /* BOOTP is done */
if (verbose == 1)
(void) fprintf(stdout,
"Client %04d(%s) - BOOTP: %s\n", myself,
cid, inet_ntoa(irequestp->yiaddr));
if (release_time || avg) {
done = B_FALSE;
mep->state = nstate = 0;
sleep_time = 0;
if (bp) {
(void) free(bp->pkt);
(void) free(bp);
bp = NULL;
}
goto forever;
}
}
if (bp) {
(void) free(bp->pkt);
(void) free(bp);
bp = NULL;
}
} while (!done);
if (!done) {
(void) fprintf(stderr,
"Client %04d - %s: configuration failed.\n",
myself, (mep->proto) ? "DHCP" : "BOOTP");
}
wbp = mep->pktlistp;
while (wbp != NULL) {
twbp = wbp->next;
if (wbp->pkt != NULL)
(void) free(wbp->pkt);
(void) free(wbp);
wbp = twbp;
}
thr_exit(&error);
return (NULL); /* NOTREACHED */
}
/*
* Never returns. Just loads client lists.
*/
static void *
service(void *args)
{
struct client *clientp = (struct client *)args;
PKT_LIST *bp, *wbp;
PKT *irequestp;
int error = 0;
struct pollfd pfd[2];
ulong_t *bufp; /* ulong_t to force alignment */
int len, i;
pfd[0].fd = s;
pfd[0].events = POLLIN | POLLPRI;
if (relay.s_addr != INADDR_ANY) {
pfd[1].fd = srelay;
pfd[1].events = POLLIN | POLLPRI;
} else {
pfd[1].fd = -1;
pfd[1].events = 0;
}
for (;;) {
pfd[0].revents = 0;
pfd[1].revents = 0;
if (poll(pfd, (nfds_t)2, INFTIM) < 0) {
(void) fprintf(stderr, "Service - can't poll...\n");
error = 5;
break;
}
(void) mutex_lock(&go_mtx);
if (time_to_go) {
(void) fprintf(stderr, "Service - exiting...\n");
error = 0;
break;
}
(void) mutex_unlock(&go_mtx);
len = BUFSIZ * 2;
bufp = malloc(len);
if (pfd[0].revents)
len = recv(s, (char *)bufp, len, 0);
else {
len = recv(srelay, (char *)bufp, len, 0);
}
if (len < 0) {
(void) fprintf(stderr,
"Service - can't receive - %s\n", strerror(errno));
error = 6;
break;
} else {
irequestp = (PKT *) bufp;
for (i = 0; i < clients; i++) {
if (memcmp(clientp[i].chaddr, irequestp->chaddr,
clientp[i].hlen) == 0) {
(void) mutex_lock(&clientp[i].mtx);
bp = malloc(sizeof (PKT_LIST));
bp->pkt = irequestp;
bp->len = len;
if (verbose == 1)
(void) fprintf(stderr,
"Service - received packet "
"for thread %04d...\n",
clientp[i].id);
if (clientp[i].pktlistp == NULL) {
clientp[i].pktlistp = bp;
bp->prev = NULL;
} else {
for (wbp = clientp[i].pktlistp;
wbp->next != NULL;
wbp = wbp->next)
/* null */;
wbp->next = bp;
bp->prev = wbp;
}
bp->next = NULL;
(void) cond_signal(&clientp[i].cv);
(void) mutex_unlock(&clientp[i].mtx);
break;
}
}
if (i >= clients)
free(bufp);
}
}
thr_exit(&error);
return (NULL); /* NOTREACHED */
}
/* ARGSUSED */
static void *
sig_handle(void *arg)
{
boolean_t leave = B_FALSE;
int sig;
sigset_t set;
char buf[SIG2STR_MAX];
int old, new, unstarted;
int i;
int oldi;
uint_t cidlen;
char cid[BUFSIZ];
int discover, offer, req, decline, ack, nak;
int release, inform, unknown;
int kicked;
ulong_t minavg;
time_t minstime;
(void) sigfillset(&set);
if (avg == 0) {
avgslp.tv_sec = sample_time;
avgslp.tv_nsec = 0L;
}
while (!leave) {
discover = offer = req = decline = ack = nak = 0;
release = inform = unknown = 0;
switch (sig = sigtimedwait(&set, NULL, &avgslp)) {
case SIGHUP:
case -1:
old = time(NULL);
new = unstarted = 0;
kicked = 0;
for (i = 0; i < clients; i++) {
/* Start next client at avgslp offset */
if (avg && kicked == 0 &&
(clientsp[i].flags &
(CLIENT_FIRSTTIME | CLIENT_BUSY)) == 0) {
(void) mutex_lock(&clientsp[i].mtx);
(void) cond_signal(&clientsp[i].acv);
(void) mutex_unlock(&clientsp[i].mtx);
kicked++;
}
switch (clientsp[i].state) {
case DISCOVER:
discover++;
break;
case OFFER:
offer++;
break;
case REQUEST:
req++;
break;
case DECLINE:
decline++;
break;
case ACK:
ack++;
break;
case NAK:
nak++;
break;
case RELEASE:
release++;
break;
case INFORM:
inform++;
break;
default:
unknown++;
break;
}
if (clientsp[i].ltime == NULL ||
(clientsp[i].flags & CLIENT_BUSY) == 0)
unstarted++;
if (clientsp[i].ltime &&
clientsp[i].ltime < old) {
old = clientsp[i].ltime;
oldi = i;
}
if (clientsp[i].ltime &&
clientsp[i].ltime > new) {
new = clientsp[i].ltime;
}
}
if (time(NULL) < ltime + sample_time)
continue;
ltime = time(NULL);
if (start == 0) {
/* toss initial sample */
ostart = start = time(NULL);
otops = tops = 0;
minind = 0;
} else {
minops[minind] = tops - otops;
mintim[minind] = ostart;
otops = tops;
ostart = time(NULL);
minind = minind + 1 > nsamples - 1 ? 0 :
minind + 1;
minstime = 0;
minavg = 0;
for (i = 0; i < nsamples; i++) {
if (mintim[i])
minavg += minops[i];
if (minstime == 0)
minstime = mintim[i];
else if (mintim[i] &&
mintim[i] < minstime)
minstime = mintim[i];
}
cidlen = sizeof (cid);
(void) octet_to_hexascii(clientsp[oldi].chaddr,
clientsp[oldi].hlen, cid, &cidlen);
fprintf(stderr, "%9.9d: Totops %d Curr %d "
"Persec %4.2f (%4.2f) Oldest %d (%d) "
"Gap %d Free %d\n", time(NULL), tops,
ops_outstanding,
(double)tops / (double)(time(NULL) - start),
(double)minavg / (double)(time(NULL)
- minstime),
time(NULL) - old, clientsp[oldi].id, cid,
new - old, unstarted);
fprintf(stderr, "\tdiscov %d off %d req %d "
"decl %d ack %d nak %d rel %d inf %d "
"free/unknown %d\n", discover, offer, req,
decline, ack, nak, release, inform,
unknown);
}
break;
case SIGINT:
/* FALLTHRU */
case SIGTERM:
(void) sig2str(sig, buf);
(void) fprintf(stderr,
"Signal: %s received...Exiting\n", buf);
(void) mutex_lock(&go_mtx);
time_to_go = B_TRUE;
(void) cond_broadcast(&go_cv);
(void) mutex_unlock(&go_mtx);
for (i = 0; i < clients; i++) {
(void) mutex_lock(&clientsp[i].mtx);
(void) cond_signal(&clientsp[i].acv);
(void) mutex_unlock(&clientsp[i].mtx);
}
leave = B_TRUE;
break;
default:
(void) sig2str(sig, buf);
(void) fprintf(stderr,
"Signal: %s received...Ignoring\n", buf);
leave = B_FALSE;
break;
}
}
thr_exit((void *) NULL);
return (NULL); /* NOTREACHED */
}
int
main(int argc, char *argv[])
{
boolean_t proto;
int i, j, threrror = 0, *threrrorp;
int sockoptbuf = 1;
register char *endp, *octet;
thread_t service_id, sig_id;
sigset_t set;
uint_t buf;
int slen;
socklen_t sslen = sizeof (slen);
unsigned int ifceno;
char cifname[IFNAMSIZ];
struct rlimit rl;
if (randcl)
srandom(time(NULL));
if (dofork) {
if (fork() != 0)
exit(0);
}
if (getrlimit(RLIMIT_NOFILE, &rl) < 0) {
(void) fprintf(stderr, "Cannot get open file limit: %s\n",
strerror(errno));
}
/* handle cases where limit is infinity */
if (rl.rlim_cur == RLIM_INFINITY) {
rl.rlim_cur = (rl.rlim_max == RLIM_INFINITY) ?
OPEN_MAX : rl.rlim_max;
}
/* set NOFILE to unlimited */
rl.rlim_cur = rl.rlim_max = RLIM_INFINITY;
if (setrlimit(RLIMIT_NOFILE, &rl) < 0) {
(void) fprintf(stderr, "Cannot set open file limit: %s\n",
strerror(errno));
}
if (argc < 5) {
(void) fprintf(stderr, "%s <interface> <ether_addr> <protocol> "
"<clients> [time] [desynch] [avg] [relayaddr]\n", argv[0]);
return (1);
}
(void) strcpy(ifname, argv[1]);
(void) strcpy(cifname, argv[1]);
if ((endp = strchr(ifname, ':')) != NULL) {
*endp = '\0';
startindex = strtol(endp + 1, 0L, 0L);
}
if (strcasecmp(argv[3], "dhcp") == 0)
proto = B_TRUE;
else
proto = B_FALSE;
clients = atoi(argv[4]);
if (argc >= 6) {
release_time = atoi(argv[5]);
}
if (argc >= 7)
desynch = atoi(argv[6]);
if (argc >= 8) {
avg = atof(argv[7]);
if (avg > 0.0) {
avgslp.tv_sec = avg;
avgslp.tv_nsec = (avg -
(double)((int)avg)) * 1000000000.0;
} else if (avg < 0.0) {
avgslp.tv_sec = abs((int)avg);
avgslp.tv_nsec = (avg + abs((double)((int)avg))) *
1000000000.0;
}
}
if (argc >= 9)
relay.s_addr = inet_addr(argv[8]);
if (argc >= 10)
slen = strtol(argv[0], 0L, 0L);
else
slen = 1024 * 64;
if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
perror("Socket");
return (1);
}
(void) setsockopt(s, SOL_SOCKET, SO_SNDBUF, &slen, sslen);
(void) setsockopt(s, SOL_SOCKET, SO_RCVBUF, &slen, sslen);
if (relay.s_addr == INADDR_ANY)
if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, (char *)&sockoptbuf,
(int)sizeof (sockoptbuf)) < 0) {
perror("Setsockopt");
return (2);
}
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
(char *)&sockoptbuf, (int)sizeof (sockoptbuf)) < 0) {
perror("Setsockopt: REUSEADDR");
return (2);
}
if (relay.s_addr != INADDR_ANY) {
relfrom.sin_port = htons(IPPORT_BOOTPS + port_offset);
relfrom.sin_family = AF_INET;
relfrom.sin_addr.s_addr = INADDR_ANY;
relfrom.sin_port = htons(IPPORT_BOOTPS + port_offset);
(void) strncpy(lifr.lifr_name, cifname,
sizeof (lifr.lifr_name));
if (lrecv) {
if (ioctl(s, SIOCGLIFADDR, (char *)&lifr) < 0) {
(void) fprintf(stderr, "Warning: SIOCGLIFADDR: %s",
strerror(errno));
} else {
relfrom.sin_addr.s_addr =
((struct sockaddr_in *)
&lifr.lifr_addr)->sin_addr.s_addr;
}
}
if ((srelay = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
perror("Socket");
return (1);
}
(void) setsockopt(srelay, SOL_SOCKET, SO_SNDBUF, &slen, sslen);
(void) setsockopt(srelay, SOL_SOCKET, SO_RCVBUF, &slen, sslen);
if (setsockopt(srelay, SOL_SOCKET, SO_REUSEADDR,
(char *)&sockoptbuf, (int)sizeof (sockoptbuf)) < 0) {
perror("Setsockopt: REUSEADDR");
return (2);
}
if (bind(srelay, (struct sockaddr *)&relfrom,
sizeof (relfrom)) < 0) {
perror("Bind");
return (3);
}
ifceno = if_nametoindex(cifname);
if (bound) {
if (setsockopt(s, IPPROTO_IP, IP_BOUND_IF,
(char *)&ifceno, (int)sizeof (char *)) < 0) {
perror("Setsockopt bind");
return (3);
}
}
}
from.sin_family = AF_INET;
if (relay.s_addr != INADDR_ANY) {
from.sin_addr.s_addr =
((struct sockaddr_in *)&lifr.lifr_addr)->sin_addr.s_addr;
} else {
from.sin_addr.s_addr = INADDR_ANY;
}
from.sin_port = htons(IPPORT_BOOTPC + port_offset);
if (bind(s, (struct sockaddr *)&from, sizeof (from)) < 0) {
perror("Bind");
return (3);
}
ifceno = if_nametoindex(cifname);
if (bound) {
if (setsockopt(s, IPPROTO_IP, IP_BOUND_IF,
(char *)&ifceno, (int)sizeof (char *)) < 0) {
perror("Setsockopt bind");
return (3);
}
}
request.op = 1; /* BOOTP request */
request.htype = 1; /* Ethernet */
request.hlen = 6; /* Ethernet addr len */
endp = octet = argv[2];
for (i = 0; i < (int)request.hlen && octet != NULL; i++) {
if ((endp = (char *)strchr(endp, ':')) != NULL)
*endp++ = '\0';
(void) sscanf(octet, "%x", &buf);
request.chaddr[i] = (uchar_t)buf;
octet = endp;
}
/* broadcast bit */
if (relay.s_addr == INADDR_ANY)
request.flags = htons(0x8000);
/* magic cookie */
request.cookie[0] = 99;
request.cookie[1] = 130;
request.cookie[2] = 83;
request.cookie[3] = 99;
if (proto) {
/* Pretend to be a discover packet */
request.options[0] = CD_DHCP_TYPE;
request.options[1] = 1;
request.options[2] = DISCOVER;
request.options[3] = 0xff;
(void) cond_init(&go_cv, USYNC_THREAD, NULL);
(void) mutex_init(&go_mtx, USYNC_THREAD, NULL);
}
if (relay.s_addr != INADDR_ANY)
request.giaddr.s_addr = from.sin_addr.s_addr;
(void) sigfillset(&set);
(void) sigdelset(&set, SIGABRT); /* allow for user abort */
(void) thr_sigsetmask(SIG_SETMASK, &set, NULL);
/*
* Create the client threads
*/
clientsp = malloc(sizeof (struct client) * clients);
(void) memset(clientsp, 0, sizeof (struct client) * clients);
if (clientsp == NULL)
return (1);
for (i = 0; i < clients; i++) {
(void) memcpy(clientsp[i].chaddr, request.chaddr, request.hlen);
clientsp[i].hlen = request.hlen;
if (i > 100)
j = 3;
else if (i > 50)
j = 2;
else
j = 1;
if (i) {
clientsp[i].chaddr[j] = (unsigned char) i;
clientsp[i].chaddr[3] += (unsigned char) j;
clientsp[i].chaddr[4] = (unsigned char) (i * j);
}
if (printid)
fprintf(stderr, "ID %x:%x:%x:%x:%x:%x\n",
clientsp[i].chaddr[0],
clientsp[i].chaddr[1],
clientsp[i].chaddr[2],
clientsp[i].chaddr[3],
clientsp[i].chaddr[4],
clientsp[i].chaddr[5]);
(void) cond_init(&clientsp[i].cv, USYNC_THREAD, 0);
(void) mutex_init(&clientsp[i].mtx, USYNC_THREAD, 0);
clientsp[i].proto = proto;
clientsp[i].flags = CLIENT_FIRSTTIME;
if (thr_create(NULL, NULL, client, (void *) &clientsp[i],
THR_BOUND | THR_SUSPENDED, &clientsp[i].id) != 0) {
(void) fprintf(stderr, "Error starting Client %04d\n",
clientsp[i].id);
}
}
/*
* Create signal handling thread.
*/
if (thr_create(NULL, 0, sig_handle, NULL,
THR_BOUND | THR_DAEMON | THR_DETACHED, &sig_id) != 0) {
(void) fprintf(stderr, "Error starting signal handler.\n");
return (1);
} else
(void) fprintf(stderr, "Started Signal handler: %04d...\n",
sig_id);
/*
* Create/start the service thread.
*/
if (thr_create(NULL, NULL, service, (void *) clientsp, THR_BOUND,
&service_id) != 0) {
(void) fprintf(stderr, "Error starting Service %d\n",
service_id);
exit(1);
} else
(void) fprintf(stderr, "Started Service %04d...\n",
service_id);
/*
* Continue the client threads.
*/
for (i = 0; i < clients; i++) {
(void) thr_continue(clientsp[i].id);
}
/*
* join them
*/
threrrorp = &threrror;
for (i = 0; i < clients; i++) {
if (thr_join(clientsp[i].id, NULL, (void **) &threrrorp) == 0) {
if (threrror != 0) {
(void) fprintf(stdout,
"Client %04d - exited with %d\n",
clientsp[i].id, threrror);
}
(void) cond_destroy(&clientsp[i].cv);
(void) mutex_destroy(&clientsp[i].mtx);
}
}
(void) close(s); /* force service out of poll */
if (thr_join(service_id, NULL, (void **) &threrrorp) == 0) {
if (threrror != 0) {
(void) fprintf(stdout, "Service - exited with %d\n",
threrror);
}
}
(void) free((char *)clientsp);
(void) fprintf(stdout, "Exiting...\n");
return (0);
}
/*
* corrupt: simulate packet corruption for debugging server
*/
static void
corrupt(char *pktp, int size)
{
int c;
int i;
int p;
char *pp;
char *pe = pktp + size;
int li = rand() % (size - 1) + 1;
for (pp = pktp; pp < pe; pp += li) {
c = ((pe - pp) < li ? pe - pp : li);
i = (rand() % c)>>1;
while (--i > 0) {
p = (rand() % c);
pp[p] = (unsigned char)(rand() & 0xFF);
}
}
}