blob: cd7654b7afd52eabd4176c2b791695348ee83ec1 [file] [log] [blame]
/* SCTP kernel Implementation
* (C) Copyright IBM Corp. 2001, 2003
* Copyright (c) 1999 Cisco
* Copyright (c) 1999, 2000, 2001 Motorola
* Copyright (c) 2001-2002 Nokia
* Copyright (c) 2001 La Monte H.P. Yarroll
*
* This is a userspace test application for the SCTP kernel
* implementation.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it
* will be useful, but WITHOUT ANY WARRANTY; without even the implied
* ^^^^^^^^^^^^^^^^^^^^^^^^
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU CC; see the file COPYING. If not, write to
* the Free Software Foundation, 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*
* Please send any bug reports or fixes you make to the
* email address(es):
* lksctp developers <lksctp-developers@lists.sourceforge.net>
*
* Or submit a bug report through the following website:
* http://www.sf.net/projects/lksctp
*
* Written or modified by:
* Hui Huang <hui.huang@nokia.com>
* Sridhar Samudrala <samudrala@us.ibm.com>
* Jon Grimm <jgrimm@us.ibm.com>
* Daisy Chang <daisyc@us.ibm.com>
* Ryan Layer <rmlayer@us.ibm.com>
*/
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/uio.h>
#include <netinet/in.h>
#include <errno.h>
#include <netinet/sctp.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <sys/param.h>
#include <fcntl.h>
#include <netdb.h>
#include <net/if.h>
#include <sys/resource.h>
#define REALLY_BIG 65536
#define SERVER 0
#define CLIENT 1
#define MIXED 2
#define NOT_DEFINED 666
#define REPEAT 10
#define BIG_REPEAT 1000000
#define MAX_BIND_RETRYS 10
#define BODYSIZE 10
#define MSG_CNT 10 /* If this is changed the msg_sizes array
needs to be modified accordingly. */
#define DEFAULT_MAX_WINDOW 32768
#define DEFAULT_MIN_WINDOW 1500
#define DEBUG_NONE 0
#define DEBUG_MIN 1
#define DEBUG_MAX 2
#define STREAM_PATTERN_SEQUENTIAL 0
#define STREAM_PATTERN_RANDOM 1
#define ORDER_PATTERN_UNORDERED 0
#define ORDER_PATTERN_ORDERED 1
#define ORDER_PATTERN_ALTERNATE 2
#define ORDER_PATTERN_RANDOM 3
#define ASSOC_PATTERN_SEQUENTIAL 0
#define ASSOC_PATTERN_RANDOM 1
#define NCASES 6
#define MAX_POLL_SKS 256
#define DEBUG_PRINT(level, print_this...) \
{ \
if (debug_level >= level) { \
fprintf(stdout, print_this); \
fflush(stdout); \
} \
} /* DEBUG_PRINT */
/* Convenience structure to determine space needed for cmsg. */
typedef union {
struct sctp_initmsg init;
struct sctp_sndrcvinfo sndrcvinfo;
} _sctp_cmsg_data_t;
#ifdef __FreeBSD__
typedef union {
int raw;
struct sctp_initmsg init;
struct sctp_sndrcvinfo sndrcv;
} sctp_cmsg_data_t;
#endif
#define CMSG_SPACE_INITMSG (CMSG_SPACE(sizeof(struct sctp_initmsg)))
#define CMSG_SPACE_SNDRCV (CMSG_SPACE(sizeof(struct sctp_sndrcvinfo)))
typedef struct {
int rem_port;
int order_state;
int stream_state;
int msg_cnt;
int msg_sent;
int cycle;
} _assoc_state;
typedef struct {
int sk;
int assoc_i;
_assoc_state *assoc_state;
} _poll_sks;
char *local_host = NULL;
int local_port = 0;
char *remote_host = NULL;
int remote_port = 0;
/* struct sockaddr_in s_rem, s_loc; */
struct sockaddr_storage s_rem, s_loc;
int r_len, l_len;
int test_case = 0;
int size_arg = 0;
int xflag = 0;
int debug_level = DEBUG_MAX;
int do_exit = 1;
int stream_pattern = STREAM_PATTERN_SEQUENTIAL;
int stream_state = 0;
int order_pattern = ORDER_PATTERN_UNORDERED;
int order_state = 0;
int max_stream = 0;
int seed = 0;
int max_msgsize = DEFAULT_MAX_WINDOW;
int timetolive = 0;
int assoc_pattern = ASSOC_PATTERN_SEQUENTIAL;
int socket_type = SOCK_SEQPACKET;
int repeat_count = 0;
int listeners = 0;
int tosend = 0;
_poll_sks poll_sks[MAX_POLL_SKS];
int repeat = REPEAT;
int msg_cnt = MSG_CNT;
int drain = 0;
int role = NOT_DEFINED;
struct sockaddr *bindx_add_addrs = NULL;
int bindx_add_count = 0;
struct sockaddr *connectx_addrs = NULL;
int connectx_count = 0;
int if_index = 0;
unsigned char msg[] = "012345678901234567890123456789012345678901234567890";
static int msg_sizes[NCASES][MSG_CNT] =
{{1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
{1452, 2904, 4356, 1452, 2904, 4356, 1452, 2904, 4356, 1452},
{1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453},
{1, 1453, 32768, 1, 1453, 32768, 1, 1453, 32768, 1},
{1, 1000, 2000, 3000, 5000, 10000, 15000, 20000, 25000, 32768},
{32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768},
};
static const char *sac_state_tbl[] = {
"COMMUNICATION_UP",
"COMMUNICATION_LOST",
"RESTART",
"SHUTDOWN_COMPLETE",
"CANT_START_ASSOCICATION"
};
void usage(char *argv0)
{
fprintf(stderr, "\nusage:\n");
fprintf(stderr, " server:\n");
fprintf(stderr, " %8s -H local-addr -P local-port -l [-d level] [-x]\n"
"\t [-L num-ports] [-S num-ports]\n"
"\t [-a assoc-pattern]\n"
"\t [-i interface]\n",
argv0);
fprintf(stderr, "\n");
fprintf(stderr, " client:\n");
fprintf(stderr, " %8s -H local-addr -P local-port -h remote-addr\n"
"\t -p remote-port -s [-c case ] [-d level]\n"
"\t [-x repeat] [-o order-pattern] [-t stream-pattern]\n"
"\t [-M max-stream] [-r rand-seed]\n"
"\t [-m max-msgsize]\n"
"\t [-L num-ports] [-S num-ports]\n"
"\t [-a assoc-pattern]\n"
"\t [-i interface]\n",
argv0);
fprintf(stderr, "\n");
fprintf(stderr, "\t-a assoc_pattern in the mixed mode\n");
fprintf(stderr, "\t 0 = sequential ascending(default)\n");
fprintf(stderr, "\t 1 = random\n");
fprintf(stderr, "\t-d debug\n");
fprintf(stderr, "\t 0 = none\n");
fprintf(stderr, "\t 1 = min(default)\n");
fprintf(stderr, "\t 2 = max\n");
fprintf(stderr, "\t-c testcase\n");
fprintf(stderr, "\t 0 = 1 byte packets.\n");
fprintf(stderr, "\t 1 = Sequence of multiples of 1452 byte packets.\n");
fprintf(stderr, "\t (1452 is fragmentation point for an i/f with ");
fprintf(stderr, "1500 as mtu.)\n");
fprintf(stderr, "\t 2 = 1453 byte packets.\n");
fprintf(stderr, "\t (min. size at which fragmentation occurs\n");
fprintf(stderr, "\t for an i/f with 1500 as mtu.)\n");
fprintf(stderr, "\t 3 = Sequence of 1, 1453, 32768 byte packets.\n");
fprintf(stderr, "\t 4 = Sequence of following size packets.\n");
fprintf(stderr, "\t (1, 1000, 2000, 3000, 5000, 10000,");
fprintf(stderr, "15000, 20000, 25000, 32768)\n");
fprintf(stderr, "\t 5 = 32768 byte packets.\n");
fprintf(stderr, "\t (default max receive window size.)\n");
fprintf(stderr, "\t 6 = random size packets.\n");
fprintf(stderr, "\t -ve value = Packets of specifed size.\n");
fprintf(stderr, "\t-m max msgsize for option -c 6 (1500-65515, default value 32768)\n");
fprintf(stderr, "\t-x number of repeats\n");
fprintf(stderr, "\t-o order-pattern\n");
fprintf(stderr, "\t 0 = all unordered(default) \n");
fprintf(stderr, "\t 1 = all ordered \n");
fprintf(stderr, "\t 2 = alternating \n");
fprintf(stderr, "\t 3 = random\n");
fprintf(stderr, "\t-t stream-pattern\n");
fprintf(stderr, "\t 0 = sequential ascending(default)\n");
fprintf(stderr, "\t 1 = random\n");
fprintf(stderr, "\t-M max-stream (default value 0)\n");
fprintf(stderr, "\t-r seed (default 0, use time())\n");
fprintf(stderr, "\t-L num-ports (default value 0). Run the mixed mode\n");
fprintf(stderr, "\t-S num-ports (default value 0). Run the mixed mode\n");
fprintf(stderr, "\t-D drain. If in client mode do a read following send.\n");
fprintf(stderr, "\t-T use SOCK_STREAM tcp-style sockets.\n");
fprintf(stderr, "\t-B add the specified address(es) as additional bind\n");
fprintf(stderr, "\t addresses of the local socket. Multiple addresses can\n");
fprintf(stderr, "\t be specified by using this argument multiple times.\n");
fprintf(stderr, "\t For example, '-B 10.0.0.1 -B 20.0.0.2'.\n");
fprintf(stderr, "\t In case of IPv6 linklocal address, interface name can be set in following way \n");
fprintf(stderr, "\t For example, '-B fe80::f8c3:b77f:698e:4506%%eth2'.\n");
fprintf(stderr, "\t-C use the specified address(es) for connection to the\n");
fprintf(stderr, "\t peer socket. Multiple addresses can be specified by\n");
fprintf(stderr, "\t using this argument multiple times.\n");
fprintf(stderr, "\t For example, '-C 10.0.0.1 -C 20.0.0.2'.\n");
fprintf(stderr, "\t This option is incompatible with the -h option.\n");
fprintf(stderr, "\t-O time to live (default value 0)\n");
fprintf(stderr, "\n");
fflush(stderr);
} /* usage() */
void *
build_msg(int len)
{
int i = len - 1;
int n;
char *msg_buf, *p;
msg_buf = malloc(len);
if (NULL == msg_buf) {
fprintf(stderr, "\n\t\t*** malloc not enough memory!!! ***\n");
exit(1);
}
p = msg_buf;
do {
n = ((i > 50)?50:i);
memcpy(p, msg, ((i > 50)?50:i));
p += n;
i -= n;
} while (i > 0);
msg_buf[len-1] = '\0';
return(msg_buf);
} /* build_msg() */
static int
print_cmsg(int type, sctp_cmsg_data_t *data)
{
switch(type) {
case SCTP_INIT:
DEBUG_PRINT(DEBUG_MAX, "\tINIT\n");
if (DEBUG_MAX == debug_level) {
printf("\t\tsinit_num_ostreams=%d ",
data->init.sinit_num_ostreams);
printf("sinit_max_instreams=%d ",
data->init.sinit_max_instreams);
printf("sinit_max_attempts=%d ",
data->init.sinit_max_attempts);
printf("sinit_max_init_timeo=%d\n",
data->init.sinit_max_init_timeo);
}
break;
case SCTP_SNDRCV:
DEBUG_PRINT(DEBUG_MAX, "\t SNDRCV");
if (DEBUG_MAX == debug_level) {
printf("(stream=%u ", data->sndrcv.sinfo_stream);
printf("ssn=%u ", data->sndrcv.sinfo_ssn);
printf("tsn=%u ", data->sndrcv.sinfo_tsn);
printf("flags=0x%x ", data->sndrcv.sinfo_flags);
printf("ppid=%u\n", data->sndrcv.sinfo_ppid);
printf("cumtsn=%u\n", data->sndrcv.sinfo_cumtsn);
}
break;
default:
DEBUG_PRINT(DEBUG_MIN, "\tUnknown type: %d\n", type);
break;
}
fflush(stdout);
return 0;
} /* print_cmsg() */
/* This function prints the message. */
static int
print_message(const int sk, struct msghdr *msg, size_t msg_len) {
struct cmsghdr *scmsg;
sctp_cmsg_data_t *data;
int i;
if (!(MSG_NOTIFICATION & msg->msg_flags)) {
int index = 0;
DEBUG_PRINT(DEBUG_MIN, "Data %zu bytes.", msg_len);
DEBUG_PRINT(DEBUG_MAX, " First %zu bytes: ",
(msg_len < BODYSIZE)?msg_len:BODYSIZE);
/* Make sure that everything is printable and that we
* are NUL terminated...
*/
while ( msg_len > 0 ) {
char *text, tmptext[BODYSIZE];
int len;
memset(tmptext, 0x0, BODYSIZE);
text = msg->msg_iov[index].iov_base;
len = msg->msg_iov[index].iov_len;
if (msg_len == 1 && text[0] == 0) {
DEBUG_PRINT(DEBUG_MIN, "<empty> text[0]=%d",
text[0]);
break;
}
if ( len > msg_len ) {
/* text[(len = msg_len) - 1] = '\0'; */
text[(len = msg_len)] = '\0';
}
if ( (msg_len -= len) > 0 ) { index++; }
for (i = 0; i < len - 1; ++i) {
if (!isprint(text[i])) text[i] = '.';
}
strncpy(tmptext, text, BODYSIZE);
tmptext[BODYSIZE-1] = '\0';
DEBUG_PRINT(DEBUG_MAX, "%s", tmptext);
}
DEBUG_PRINT(DEBUG_MIN, "\n");
fflush(stdout);
} else { /* if(we have notification) */
struct sctp_assoc_change *sac;
struct sctp_send_failed *ssf;
struct sctp_paddr_change *spc;
struct sctp_remote_error *sre;
union sctp_notification *snp;
char addrbuf[INET6_ADDRSTRLEN];
const char *ap;
struct sockaddr_in *sin;
struct sockaddr_in6 *sin6;
int index = 0;
snp = (union sctp_notification *)msg->msg_iov[index].iov_base;
DEBUG_PRINT(DEBUG_MIN, "Notification:");
switch (snp->sn_header.sn_type) {
case SCTP_ASSOC_CHANGE:
sac = &snp->sn_assoc_change;
DEBUG_PRINT(DEBUG_MIN,
" SCTP_ASSOC_CHANGE(%s)\n",
sac_state_tbl[sac->sac_state]);
DEBUG_PRINT(DEBUG_MAX,
"\t\t(assoc_change: state=%hu, "
"error=%hu, instr=%hu "
"outstr=%hu)\n",
sac->sac_state, sac->sac_error,
sac->sac_inbound_streams,
sac->sac_outbound_streams);
break;
case SCTP_PEER_ADDR_CHANGE:
spc = &snp->sn_paddr_change;
DEBUG_PRINT(DEBUG_MIN,
" SCTP_PEER_ADDR_CHANGE\n");
if (spc->spc_aaddr.ss_family == AF_INET) {
sin = (struct sockaddr_in *)
&spc->spc_aaddr;
ap = inet_ntop(AF_INET, &sin->sin_addr,
addrbuf,
INET6_ADDRSTRLEN);
} else {
sin6 = (struct sockaddr_in6 *)
&spc->spc_aaddr;
ap = inet_ntop(AF_INET6,
&sin6->sin6_addr,
addrbuf,
INET6_ADDRSTRLEN);
}
DEBUG_PRINT(DEBUG_MAX,
"\t\t(peer_addr_change: %s "
"state=%d, error=%d)\n",
ap, spc->spc_state,
spc->spc_error);
break;
case SCTP_SEND_FAILED:
ssf = &snp->sn_send_failed;
DEBUG_PRINT(DEBUG_MIN,
" SCTP_SEND_FAILED\n");
DEBUG_PRINT(DEBUG_MAX,
"\t\t(sendfailed: len=%hu "
"err=%d)\n",
ssf->ssf_length, ssf->ssf_error);
break;
case SCTP_REMOTE_ERROR:
sre = &snp->sn_remote_error;
DEBUG_PRINT(DEBUG_MIN,
" SCTP_REMOTE_ERROR\n");
DEBUG_PRINT(DEBUG_MAX,
"\t\t(remote_error: err=%hu)\n",
ntohs(sre->sre_error));
break;
case SCTP_SHUTDOWN_EVENT:
DEBUG_PRINT(DEBUG_MIN,
" SCTP_SHUTDOWN_EVENT\n");
break;
default:
DEBUG_PRINT(DEBUG_MIN,
" Unknown type: %hu\n",
snp->sn_header.sn_type);
break;
}
fflush(stdout);
return 1;
} /* notification received */
for (scmsg = CMSG_FIRSTHDR(msg);
scmsg != NULL;
scmsg = CMSG_NXTHDR(msg, scmsg)) {
data = (sctp_cmsg_data_t *)CMSG_DATA(scmsg);
if (debug_level) print_cmsg(scmsg->cmsg_type, data);
}
fflush(stdout);
return 0;
} /* print_message() */
struct sockaddr *
append_addr(const char *parm, struct sockaddr *addrs, int *ret_count)
{
struct sockaddr *new_addrs = NULL;
void *aptr;
struct sockaddr *sa_addr;
struct sockaddr_in *b4ap;
struct sockaddr_in6 *b6ap;
struct hostent *hst4 = NULL;
struct hostent *hst6 = NULL;
int i4 = 0;
int i6 = 0;
int j;
int orig_count = *ret_count;
int count = orig_count;
char *ipaddr = strdup(parm);
char *ifname;
int ifindex = 0;
/* check the interface. */
ifname = strchr(ipaddr,'%');
if (ifname) {
*ifname=0;
ifname++;
ifindex = if_nametoindex(ifname);
if (!ifindex) {
fprintf(stderr, "bad interface name: %s\n", ifname);
goto finally;
}
}
/* Get the entries for this host. */
hst4 = gethostbyname(ipaddr);
hst6 = gethostbyname2(ipaddr, AF_INET6);
if ((NULL == hst4 || hst4->h_length < 1)
&& (NULL == hst6 || hst6->h_length < 1)) {
fprintf(stderr, "bad hostname: %s\n", ipaddr);
goto finally;
}
/* Figure out the number of addresses. */
if (NULL != hst4) {
for (i4 = 0; NULL != hst4->h_addr_list[i4]; ++i4) {
count++;
}
}
if (NULL != hst6) {
for (i6 = 0; NULL != hst6->h_addr_list[i6]; ++i6) {
count++;
}
}
/* Expand memory for the new addresses. Assume all the addresses
* are v6 addresses.
*/
new_addrs = (struct sockaddr *)
realloc(addrs, sizeof(struct sockaddr_in6) * count);
if (NULL == new_addrs) {
count = *ret_count;
goto finally;
}
/* Skip the existing addresses. */
aptr = new_addrs;
for (j = 0; j < orig_count; j++) {
sa_addr = (struct sockaddr *)aptr;
switch(sa_addr->sa_family) {
case AF_INET:
aptr += sizeof(struct sockaddr_in);
break;
case AF_INET6:
aptr += sizeof(struct sockaddr_in6);
break;
default:
count = orig_count;
goto finally;
}
}
/* Put the new addresses away. */
if (NULL != hst4) {
for (j = 0; j < i4; ++j) {
b4ap = (struct sockaddr_in *)aptr;
memset(b4ap, 0x00, sizeof(*b4ap));
b4ap->sin_family = AF_INET;
b4ap->sin_port = htons(local_port);
bcopy(hst4->h_addr_list[j], &b4ap->sin_addr,
hst4->h_length);
aptr += sizeof(struct sockaddr_in);
} /* for (loop through the new v4 addresses) */
}
if (NULL != hst6) {
for (j = 0; j < i6; ++j) {
b6ap = (struct sockaddr_in6 *)aptr;
memset(b6ap, 0x00, sizeof(*b6ap));
b6ap->sin6_family = AF_INET6;
b6ap->sin6_port = htons(local_port);
b6ap->sin6_scope_id = if_index;
bcopy(hst6->h_addr_list[j], &b6ap->sin6_addr,
hst6->h_length);
if (!ifindex) {
b6ap->sin6_scope_id = ifindex;
}
aptr += sizeof(struct sockaddr_in6);
} /* for (loop through the new v6 addresses) */
}
finally:
free(ipaddr);
*ret_count = count;
return new_addrs;
} /* append_addr() */
int socket_r(void)
{
struct sctp_event_subscribe subscribe;
int sk, error;
DEBUG_PRINT(DEBUG_MIN, "\tsocket(%s, IPPROTO_SCTP)",
(socket_type == SOCK_SEQPACKET) ? "SOCK_SEQPACKET" : "SOCK_STREAM");
if ((sk = socket(s_loc.ss_family, socket_type, IPPROTO_SCTP)) < 0 ) {
if (do_exit) {
fprintf(stderr, "\n\n\t\t*** socket: failed to create"
" socket: %s ***\n",
strerror(errno));
exit(1);
} else {
return -1;
}
}
DEBUG_PRINT(DEBUG_MIN, " -> sk=%d\n", sk);
memset(&subscribe, 0, sizeof(subscribe));
subscribe.sctp_data_io_event = 1;
subscribe.sctp_association_event = 1;
error = setsockopt(sk, SOL_SCTP, SCTP_EVENTS, (char *)&subscribe,
sizeof(subscribe));
if (error) {
fprintf(stderr, "SCTP_EVENTS: error: %d\n", error);
exit(1);
}
if (max_stream > 0) {
struct sctp_initmsg initmsg;
memset(&initmsg, 0, sizeof(struct sctp_initmsg));
initmsg.sinit_num_ostreams = max_stream;
error = setsockopt(sk, IPPROTO_SCTP, SCTP_INITMSG, &initmsg, sizeof(struct sctp_initmsg));
if (error) {
fprintf(stderr, "SCTP_INITMSG: error: %d\n", error);
exit(1);
}
}
return sk;
} /* socket_r() */
int bind_r(int sk, struct sockaddr_storage *saddr)
{
int error = 0, i = 0;
char *host_s, *serv_s;
if ((host_s = malloc(NI_MAXHOST)) == NULL) {
fprintf(stderr, "\n\t\t*** host_s malloc failed!!! ***\n");
exit(1);
}
if ((serv_s = malloc(NI_MAXSERV)) == NULL) {
fprintf(stderr, "\n\t\t*** serv_s malloc failed!!! ***\n");
exit(1);
}
do {
if (i > 0) sleep(1); /* sleep a while before new try... */
error = getnameinfo((struct sockaddr *)saddr, l_len, host_s,
NI_MAXHOST, serv_s, NI_MAXSERV,
NI_NUMERICHOST);
if (error)
printf("%s\n", gai_strerror(error));
DEBUG_PRINT(DEBUG_MIN,
"\tbind(sk=%d, [a:%s,p:%s]) -- attempt %d/%d\n",
sk, host_s, serv_s, i+1, MAX_BIND_RETRYS);
error = bind(sk, (struct sockaddr *)saddr, l_len);
if (error != 0) {
if( errno != EADDRINUSE ) {
if (do_exit) {
fprintf(stderr, "\n\n\t\t***bind: can "
"not bind to %s:%s: %s ****\n",
host_s, serv_s,
strerror(errno));
exit(1);
} else {
return -1;
}
}
}
i++;
if (i >= MAX_BIND_RETRYS) {
fprintf(stderr, "Maximum bind() attempts. "
"Die now...\n\n");
exit(1);
}
} while (error < 0 && i < MAX_BIND_RETRYS);
free(host_s);
free(serv_s);
return 0;
} /* bind_r() */
int
bindx_r(int sk, struct sockaddr *addrs, int count, int flag)
{
int error;
int i;
struct sockaddr *sa_addr;
void *aptr;
/* Set the port in every address. */
aptr = addrs;
for (i = 0; i < count; i++) {
sa_addr = (struct sockaddr *)aptr;
switch(sa_addr->sa_family) {
case AF_INET:
((struct sockaddr_in *)sa_addr)->sin_port =
htons(local_port);
aptr += sizeof(struct sockaddr_in);
break;
case AF_INET6:
((struct sockaddr_in6 *)sa_addr)->sin6_port =
htons(local_port);
aptr += sizeof(struct sockaddr_in6);
break;
default:
fprintf(stderr, "Invalid address family\n");
exit(1);
}
}
error = sctp_bindx(sk, addrs, count, flag);
if (error != 0) {
fprintf(stderr, "\n\n\t\t***bindx_r: error adding addrs:"
" %s. ***\n", strerror(errno));
exit(1);
}
return 0;
} /* bindx_r() */
int listen_r(int sk, int listen_count)
{
int error = 0;
DEBUG_PRINT(DEBUG_MIN, "\tlisten(sk=%d,backlog=%d)\n",
sk, listen_count);
/* Mark sk as being able to accept new associations */
error = listen(sk, listen_count);
if (error != 0) {
if (do_exit) {
fprintf(stderr, "\n\n\t\t*** listen: %s ***\n\n\n",
strerror(errno));
exit(1);
}
else return -1;
}
return 0;
} /* listen_r() */
int accept_r(int sk){
socklen_t len = 0;
int subsk;
DEBUG_PRINT(DEBUG_MIN, "\taccept(sk=%d)\n", sk);
subsk = accept(sk, NULL, &len);
if (subsk < 0) {
fprintf(stderr, "\n\n\t\t*** accept: %s ***\n\n\n", strerror(errno));
exit(1);
}
return subsk;
} /* accept_r() */
int connect_r(int sk, const struct sockaddr *serv_addr, socklen_t addrlen)
{
int error = 0;
DEBUG_PRINT(DEBUG_MIN, "\tconnect(sk=%d)\n", sk);
/* Mark sk as being able to accept new associations */
error = connect(sk, serv_addr, addrlen);
if (error != 0) {
if (do_exit) {
fprintf(stderr, "\n\n\t\t*** connect: %s ***\n\n\n",
strerror(errno));
exit(1);
}
else return -1;
}
return 0;
} /* connect_r() */
int connectx_r(int sk, struct sockaddr *addrs, int count)
{
int error;
int i;
struct sockaddr *sa_addr;
void *aptr;
/* Set the port in every address. */
aptr = addrs;
for (i = 0; i < count; i++) {
sa_addr = (struct sockaddr *)aptr;
switch(sa_addr->sa_family) {
case AF_INET:
((struct sockaddr_in *)sa_addr)->sin_port =
htons(remote_port);
aptr += sizeof(struct sockaddr_in);
break;
case AF_INET6:
((struct sockaddr_in6 *)sa_addr)->sin6_port =
htons(remote_port);
aptr += sizeof(struct sockaddr_in6);
break;
default:
fprintf(stderr, "Invalid address family\n");
exit(1);
}
}
error = sctp_connectx(sk, addrs, count, NULL);
if (error != 0) {
fprintf(stderr, "\n\n\t\t*** connectx_r: error connecting"
" to addrs: %s ***\n", strerror(errno));
exit(1);
}
return 0;
} /* connectx_r() */
int receive_r(int sk, int once)
{
int recvsk = sk, i = 0, error = 0;
char incmsg[CMSG_SPACE(sizeof(_sctp_cmsg_data_t))];
struct iovec iov;
struct msghdr inmessage;
/* Initialize inmessage with enough space for DATA... */
memset(&inmessage, 0, sizeof(inmessage));
if ((iov.iov_base = malloc(REALLY_BIG)) == NULL) {
fprintf(stderr, "\n\t\t*** malloc not enough memory!!! ***\n");
exit(1);
}
iov.iov_len = REALLY_BIG;
inmessage.msg_iov = &iov;
inmessage.msg_iovlen = 1;
/* or a control message. */
inmessage.msg_control = incmsg;
inmessage.msg_controllen = sizeof(incmsg);
/* Get the messages sent */
while (1) {
if (recvsk == sk && socket_type == SOCK_STREAM &&
role == SERVER)
recvsk = accept_r(sk);
DEBUG_PRINT(DEBUG_MIN, "\trecvmsg(sk=%d) ", sk);
error = recvmsg(recvsk, &inmessage, MSG_WAITALL);
if (error < 0 && errno != EAGAIN) {
if (errno == ENOTCONN && socket_type == SOCK_STREAM &&
role == SERVER) {
printf("No association is present now!!\n");
close(recvsk);
recvsk = sk;
continue;
}
fprintf(stderr, "\n\t\t*** recvmsg: %s ***\n\n",
strerror(errno));
fflush(stdout);
if (do_exit) exit(1);
else goto error_out;
}
else if (error == 0) {
if (socket_type == SOCK_STREAM && role == SERVER) {
printf("No association is present now!!\n");
close(recvsk);
recvsk = sk;
continue;
}
printf("\n\t\trecvmsg() returned 0 !!!!\n");
fflush(stdout);
}
if (print_message(recvsk, &inmessage, error) > 0)
continue; /* got a notification... */
inmessage.msg_control = incmsg;
inmessage.msg_controllen = sizeof(incmsg);
iov.iov_len = REALLY_BIG;
i++;
if (once)
break;
}
if (recvsk != sk)
close(recvsk);
free(iov.iov_base);
return 0;
error_out:
close(sk);
free(iov.iov_base);
return -1;
} /* receive_r () */
int next_order(int state, int pattern)
{
switch (pattern){
case ORDER_PATTERN_UNORDERED:
state = 0;
break;
case ORDER_PATTERN_ORDERED:
state = 1;
break;
case ORDER_PATTERN_ALTERNATE:
state = state ? 0 : 1;
break;
case ORDER_PATTERN_RANDOM:
state = rand() % 2;
break;
}
return state;
}
int next_stream(int state, int pattern)
{
switch (pattern){
case STREAM_PATTERN_RANDOM:
state = rand() % (max_stream == 0 ? 1 : max_stream);
break;
case STREAM_PATTERN_SEQUENTIAL:
state = state + 1;
if (state >= max_stream)
state = 0;
break;
}
return state;
}
int next_msg_size(int msg_cnt)
{
int msg_size;
if (size_arg) {
msg_size = size_arg;
} else if (test_case < NCASES) {
msg_size = msg_sizes[test_case][msg_cnt];
} else {
msg_size = (rand() % max_msgsize) + 1;
}
return msg_size;
} /* next_msg_size() */
int next_assoc(int i, int state, int pattern)
{
int j;
int found = 0;
_assoc_state *as;
switch (pattern){
case ASSOC_PATTERN_RANDOM:
state = rand() % tosend;
break;
case ASSOC_PATTERN_SEQUENTIAL:
state = state + 1;
if (state >= tosend)
state = 0;
break;
}
as = poll_sks[i].assoc_state;
j = state;
do {
if (as[j].msg_sent < repeat_count) {
found = 1;
break;
}
if (++j >= tosend) {
j = 0;
}
} while (j != state);
if (found) {
return j;
} else {
return -1;
}
} /* next_assoc() */
int send_r(int sk, int stream, int order, int send_size, int assoc_i)
{
int error = 0;
struct msghdr outmsg;
struct iovec iov;
char *message = NULL;
int msglen = 0;
char outcmsg[CMSG_SPACE(sizeof(struct sctp_sndrcvinfo))];
struct cmsghdr *cmsg;
struct sctp_sndrcvinfo *sinfo;
if (send_size > 0) {
message = build_msg(send_size);
msglen = strlen(message) + 1;
iov.iov_base = message;
iov.iov_len = msglen;
}
else {
if (do_exit) {
exit(1);
} else {
goto error_out;
}
}
outmsg.msg_name = &s_rem;
outmsg.msg_namelen = sizeof(struct sockaddr_storage);
outmsg.msg_iov = &iov;
outmsg.msg_iovlen = 1;
outmsg.msg_control = outcmsg;
outmsg.msg_controllen = sizeof(outcmsg);
outmsg.msg_flags = 0;
cmsg = CMSG_FIRSTHDR(&outmsg);
cmsg->cmsg_level = IPPROTO_SCTP;
cmsg->cmsg_type = SCTP_SNDRCV;
cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_sndrcvinfo));
outmsg.msg_controllen = cmsg->cmsg_len;
sinfo = (struct sctp_sndrcvinfo *)CMSG_DATA(cmsg);
memset(sinfo, 0, sizeof(struct sctp_sndrcvinfo));
sinfo->sinfo_ppid = rand();
sinfo->sinfo_stream = stream;
sinfo->sinfo_flags = 0;
if (!order)
sinfo->sinfo_flags = SCTP_UNORDERED;
if (timetolive)
sinfo->sinfo_timetolive = timetolive;
DEBUG_PRINT(DEBUG_MIN, "\tsendmsg(sk=%d, assoc=%d) %4d bytes.\n",
sk, assoc_i, send_size);
DEBUG_PRINT(DEBUG_MAX, "\t SNDRCV");
if (DEBUG_MAX == debug_level) {
printf("(stream=%u ", sinfo->sinfo_stream);
printf("flags=0x%x ", sinfo->sinfo_flags);
printf("ppid=%u\n", sinfo->sinfo_ppid);
}
/* Send to our neighbor. */
error = sendmsg(sk, &outmsg, MSG_WAITALL);
if (error != msglen) {
fprintf(stderr, "\n\t\t*** sendmsg: %s ***\n\n",
strerror(errno));
fflush(stdout);
if (do_exit) {
exit(1);
} else {
if (!drain)
goto error_out;
}
}
if (send_size > 0) free(message);
return 0;
error_out:
if (send_size > 0) free(message);
return -1;
} /* send_r() */
int close_r(int sk)
{
int error = 0;
DEBUG_PRINT(DEBUG_MIN, "\tclose(sk=%d)\n",sk);
error = close(sk);
if (error != 0) {
if (do_exit) {
fprintf(stderr, "\n\n\t\t*** close: %s ***\n\n",
strerror(errno));
exit(1);
} else {
return -1;
}
}
fflush(stdout);
return 0;
} /* close_r() */
void
server(int sk)
{
if (max_msgsize > DEFAULT_MAX_WINDOW) {
if (setsockopt(sk, SOL_SOCKET, SO_RCVBUF, &max_msgsize,
sizeof(max_msgsize)) < 0) {
perror("setsockopt(SO_RCVBUF)");
exit(1);
}
}
receive_r(sk, 0);
} /* server() */
void
client(int sk)
{
int msg_size;
int i;
for (i = 0; i < msg_cnt; i++) {
msg_size = next_msg_size(i);
order_state = next_order(order_state, order_pattern);
stream_state = next_stream(stream_state, stream_pattern);
if (send_r(sk, stream_state, order_state, msg_size, 0) < 0)
break;
/* The sender is echoing so do discard the echoed data. */
if (drain) {
receive_r(sk, 1);
}
}
} /* client() */
void
mixed_mode_test(void)
{
int error, i, j, max_fd, sks, size;
int assoc_i, n_msg_size, n_order, n_stream;
int done = 0;
fd_set *ibitsp = NULL, *obitsp = NULL, *xbitsp = NULL;
char incmsg[CMSG_SPACE(sizeof(_sctp_cmsg_data_t))];
struct iovec iov;
struct msghdr inmessage;
_assoc_state *as;
/* Set up the listeners. If listeners is 0, set up one socket for
* transmitting only.
*/
iov.iov_base = NULL;
max_fd = -1;
sks = (0 == listeners) ? 1 : listeners;
memset(poll_sks, 0, sks * sizeof(_poll_sks));
for (i = 0; i < sks; i++) {
poll_sks[i].sk = socket_r();
if (s_loc.ss_family == AF_INET6)
( (struct sockaddr_in6 *)&s_loc)->sin6_port =
htons(local_port + i);
else
( (struct sockaddr_in *)&s_loc)->sin_port =
htons(local_port + i);
bind_r(poll_sks[i].sk, &s_loc);
if (listeners) {
listen_r(poll_sks[i].sk, 100);
}
if (max_msgsize > DEFAULT_MAX_WINDOW) {
if (setsockopt(poll_sks[i].sk, SOL_SOCKET, SO_RCVBUF,
&max_msgsize, sizeof(max_msgsize)) < 0) {
perror("setsockopt(SO_RCVBUF)");
exit(1);
}
}
if (tosend) {
if ((poll_sks[i].assoc_state = (_assoc_state *)malloc(
sizeof(_assoc_state) * tosend)) == NULL) {
printf("Can't allocate memory.\n");
goto clean_up;
}
memset(poll_sks[i].assoc_state, 0,
sizeof(_assoc_state) * tosend);
}
if (poll_sks[i].sk > max_fd) {
max_fd = poll_sks[i].sk;
}
}
size = howmany(max_fd + 1, NFDBITS) * sizeof(fd_mask);
if ((ibitsp = (fd_set *)malloc(size)) == NULL) {
printf("Can't allocate memory.\n");
goto clean_up;
}
if ((obitsp = (fd_set *)malloc(size)) == NULL) {
printf("Can't allocate memory.\n");
goto clean_up;
}
if ((xbitsp = (fd_set *)malloc(size)) == NULL) {
printf("Can't allocate memory.\n");
goto clean_up;
}
memset(ibitsp, 0, size);
memset(obitsp, 0, size);
memset(xbitsp, 0, size);
/* Initialize inmessage with enough space for DATA... */
memset(&inmessage, 0, sizeof(inmessage));
if ((iov.iov_base = malloc(REALLY_BIG)) == NULL) {
fprintf(stderr, "\n\t\t*** malloc not enough memory!!! ***\n");
goto clean_up;
}
iov.iov_len = REALLY_BIG;
inmessage.msg_iov = &iov;
inmessage.msg_iovlen = 1;
/* or a control message. */
inmessage.msg_control = incmsg;
inmessage.msg_controllen = sizeof(incmsg);
/* Set up the remote port number per association for output. */
for (i = 0; i < sks; i++) {
as = poll_sks[i].assoc_state;
for (j = 0; j < tosend; j++) {
as[j].rem_port = remote_port + j;
}
}
while (!done) {
for (i = 0; i < sks; i++) {
FD_SET(poll_sks[i].sk, ibitsp);
FD_SET(poll_sks[i].sk, obitsp);
FD_SET(poll_sks[i].sk, xbitsp);
}
if ((error = select(max_fd + 1, ibitsp, obitsp, xbitsp,
(struct timeval *)0)) < 0) {
fprintf(stderr, "\n\t\t*** select() failed ");
fprintf(stderr, "with error: %s\n\n",
strerror(errno));
fflush(stdout);
goto clean_up;
}
for (i = 0; i < sks; i++) {
/* Is there anything to read from the socket? */
if (listeners && FD_ISSET(poll_sks[i].sk, ibitsp)) {
FD_CLR(poll_sks[i].sk, ibitsp);
error = recvmsg(poll_sks[i].sk, &inmessage,
MSG_WAITALL);
if (error < 0) {
fprintf(stderr,
"\n\t\t*** recvmsg: %s ***\n\n",
strerror(errno));
fflush(stdout);
goto clean_up;
}
else if (error == 0) {
printf("\n\t\trecvmsg() returned ");
printf("0 !!!!\n");
fflush(stdout);
}
print_message(poll_sks[i].sk, &inmessage,
error);
inmessage.msg_control = incmsg;
inmessage.msg_controllen = sizeof(incmsg);
iov.iov_len = REALLY_BIG;
}
/* Is this socket writeable? */
if (tosend && FD_ISSET(poll_sks[i].sk, obitsp)) {
FD_CLR(poll_sks[i].sk, obitsp);
/* Pick an association. */
assoc_i = next_assoc(i, poll_sks[i].assoc_i,
assoc_pattern);
if (assoc_i < 0) {
/* No work to do on any associations.
* We are probably done. */
if (!listeners) {
done = 1;
}
continue;
}
poll_sks[i].assoc_i = assoc_i;
as = poll_sks[i].assoc_state;
n_msg_size = next_msg_size(as[assoc_i].msg_cnt);
n_order = as[assoc_i].order_state =
next_order(as[assoc_i].order_state,
order_pattern);
n_stream = as[assoc_i].stream_state =
next_stream(as[assoc_i].stream_state,
stream_pattern);
/* Set the destination port. */
if (s_rem.ss_family == AF_INET6)
( (struct sockaddr_in6 *)&s_rem)->
sin6_port =
htons(as[assoc_i].rem_port);
else
( (struct sockaddr_in *)&s_rem)->
sin_port =
htons(as[assoc_i].rem_port);
/* Send a message thru the association. */
if (send_r(poll_sks[i].sk, n_stream, n_order,
n_msg_size, assoc_i) < 0) {
/* Don't increment counter if there
* is a problem of sending.
*/
continue;
}
/* Increment counters. */
if (++as[assoc_i].msg_cnt >= MSG_CNT) {
as[assoc_i].msg_cnt = 0;
}
if (++as[assoc_i].msg_sent >=
repeat_count) {
fprintf(stderr, "Association #%d in ",
assoc_i);
fprintf(stderr, "sk=%d has ",
poll_sks[i].sk);
fprintf(stderr, "completed %d msg as ",
as[assoc_i].msg_sent);
fprintf(stderr, "cycle %d.\n",
++as[assoc_i].cycle);
/* In the mixed mode, -x not only
* specify the longer repeat cycle,
* but it also mean to run the test
* forever.
*/
if (xflag) {
as[assoc_i].msg_sent = 0;
}
}
}
}
}
clean_up:
for (i = 0; i < sks; i++) {
close(poll_sks[i].sk);
if (poll_sks[i].assoc_state) {
free(poll_sks[i].assoc_state);
}
}
if (ibitsp) free(ibitsp);
if (obitsp) free(obitsp);
if (xbitsp) free(xbitsp);
if (iov.iov_base) free(iov.iov_base);
} /* mixed_mode_test() */
void start_test(int role)
{
int sk;
int i = 0;
DEBUG_PRINT(DEBUG_NONE, "\nStarting tests...\n");
repeat_count = repeat;
if (MIXED == role) {
repeat_count = repeat_count * msg_cnt; /* Repeat per assoc. */
mixed_mode_test();
return;
}
sk = socket_r();
if (sk < 0) {
DEBUG_PRINT(DEBUG_NONE, "\nSocket create err %d\n", errno);
return;
}
if (bind_r(sk, &s_loc) == -1) {
DEBUG_PRINT(DEBUG_NONE, "\nSocket bind err %d\n", errno);
return;
}
/* Do we need to do bindx() to add any additional addresses? */
if (bindx_add_addrs)
bindx_r(sk, bindx_add_addrs, bindx_add_count,
SCTP_BINDX_ADD_ADDR);
if (role == SERVER) {
listen_r(sk, 100);
} else {
if (socket_type == SOCK_STREAM && connectx_count == 0)
connect_r(sk, (struct sockaddr *)&s_rem, r_len);
if (connectx_count != 0)
connectx_r(sk, connectx_addrs, connectx_count);
}
if (!debug_level) {
printf(" ");
}
for(i = 0; i < repeat_count; i++) {
if (role == SERVER) {
DEBUG_PRINT(DEBUG_NONE,
"Server: Receiving packets.\n");
server(sk);
} else {
DEBUG_PRINT(DEBUG_NONE,
"Client: Sending packets.(%d/%d)\n",
i+1, repeat_count);
client(sk);
}
fflush(stdout);
}
close_r(sk);
} /* start_test() */
int
main(int argc, char *argv[])
{
int c;
char *interface = NULL;
struct sockaddr_in *t_addr;
struct sockaddr_in6 *t_addr6;
struct sockaddr *tmp_addrs = NULL;
/* Parse the arguments. */
while ((c = getopt(argc, argv, ":H:L:P:S:a:h:p:c:d:lm:sx:X:o:t:M:r:w:Di:TB:C:O:")) >= 0 ) {
switch (c) {
case 'H':
local_host = optarg;
break;
case 'L':
role = MIXED;
listeners = atoi(optarg);
if (listeners > MAX_POLL_SKS) {
usage(argv[0]);
exit(1);
}
break;
case 'P':
local_port = atoi(optarg);
break;
case 'S':
role = MIXED;
tosend = atoi(optarg);
if (tosend > MAX_POLL_SKS) {
usage(argv[0]);
exit(1);
}
break;
case 'a':
assoc_pattern = atoi(optarg);
if (assoc_pattern < ASSOC_PATTERN_SEQUENTIAL
|| assoc_pattern > ASSOC_PATTERN_RANDOM ) {
usage(argv[0]);
exit(1);
}
break;
case 'h':
remote_host = optarg;
break;
case 'D':
drain = 1;
do_exit = 0;
break;
case 'p':
remote_port = atoi(optarg);
break;
case 's':
if (role != NOT_DEFINED) {
printf("%s: only -s or -l\n", argv[0]);
usage(argv[0]);
exit(1);
}
role = CLIENT;
break;
case 'l':
if (role != NOT_DEFINED) {
printf("%s: only -s or -l\n", argv[0]);
usage(argv[0]);
exit(1);
}
role = SERVER;
break;
case 'd':
debug_level = atoi(optarg);
if (debug_level < DEBUG_NONE
|| debug_level > DEBUG_MAX) {
usage(argv[0]);
exit(1);
}
break;
case 'x':
repeat = atoi(optarg);
if (!repeat) {
xflag = 1;
repeat = BIG_REPEAT;
}
break;
case 'X':
msg_cnt = atoi(optarg);
if ((msg_cnt <= 0) || (msg_cnt > MSG_CNT)) {
usage(argv[0]);
exit(1);
}
break;
case 'c':
test_case = atoi(optarg);
if (test_case > NCASES) {
usage(argv[0]);
exit(1);
}
if (test_case < 0) {
size_arg = -test_case;
}
break;
case 'o':
order_pattern = atoi(optarg);
if (order_pattern < ORDER_PATTERN_UNORDERED
|| order_pattern > ORDER_PATTERN_RANDOM ) {
usage(argv[0]);
exit(1);
}
break;
case 'O':
timetolive = atoi(optarg);
if (timetolive < 0) {
usage(argv[0]);
exit(1);
}
break;
case 't':
stream_pattern = atoi(optarg);
if (stream_pattern < STREAM_PATTERN_SEQUENTIAL
|| stream_pattern > STREAM_PATTERN_RANDOM ) {
usage(argv[0]);
exit(1);
}
break;
case 'M':
max_stream = atoi(optarg);
if (max_stream < 0
|| max_stream >= (1<<16)) {
usage(argv[0]);
exit(1);
}
break;
case 'r':
seed = atoi(optarg);
break;
case 'm':
max_msgsize = atoi(optarg);
#if 0
if ((max_msgsize < DEFAULT_MIN_WINDOW) ||
(max_msgsize > 65515)) {
usage(argv[0]);
exit(1);
}
#endif
break;
case 'i':
interface = optarg;
if_index = if_nametoindex(interface);
if (!if_index) {
printf("Interface %s unknown\n", interface);
exit(1);
}
break;
case 'T':
socket_type = SOCK_STREAM;
break;
case 'B':
tmp_addrs = append_addr(optarg, bindx_add_addrs,
&bindx_add_count);
if (NULL == tmp_addrs) {
fprintf(stderr, "No memory to add ");
fprintf(stderr, "%s\n", optarg);
exit(1);
}
bindx_add_addrs = tmp_addrs;
break;
case 'C':
tmp_addrs = append_addr(optarg, connectx_addrs,
&connectx_count);
if (NULL == tmp_addrs) {
fprintf(stderr, "No memory to add ");
fprintf(stderr, "%s\n", optarg);
exit(1);
}
connectx_addrs = tmp_addrs;
break;
case '?':
default:
usage(argv[0]);
exit(0);
}
} /* while() */
if (NOT_DEFINED == role) {
usage(argv[0]);
exit(1);
}
if (SERVER == role && NULL == local_host && remote_host != NULL) {
fprintf (stderr, "%s: Server needs local address, "
"not remote address\n", argv[0]);
usage(argv[0]);
exit(1);
}
if (CLIENT == role && NULL == remote_host && connectx_count == 0) {
fprintf (stderr, "%s: Client needs at least remote address "
"& port\n", argv[0]);
usage(argv[0]);
exit(1);
}
if (MIXED == role) {
if (listeners && NULL == local_host) {
fprintf (stderr, "%s: Servers need local address\n",
argv[0]);
usage(argv[0]);
exit(1);
}
if (tosend && NULL == remote_host) {
fprintf (stderr, "%s: Clients need remote address ",
argv[0]);
fprintf (stderr, "& port\n");
usage(argv[0]);
exit(1);
}
}
if (optind < argc) {
fprintf(stderr, "%s: non-option arguments are illegal: ",
argv[0]);
while (optind < argc)
fprintf(stderr, "%s ", argv[optind++]);
fprintf (stderr, "\n");
usage(argv[0]);
exit(1);
}
if (remote_host != NULL && connectx_count != 0) {
fprintf(stderr, "%s: You can not provide both -h and -C options.\n",
argv[0]);
usage(argv[0]);
exit(1);
}
if (remote_host != NULL && remote_port != 0) {
struct addrinfo *res;
int error;
char *host_s, *serv_s;
if ((host_s = malloc(NI_MAXHOST)) == NULL) {
fprintf(stderr, "\n*** host_s malloc failed!!! ***\n");
exit(1);
}
if ((serv_s = malloc(NI_MAXSERV)) == NULL) {
fprintf(stderr, "\n*** serv_s malloc failed!!! ***\n");
exit(1);
}
error = getaddrinfo(remote_host, 0, NULL, &res);
if (error) {
printf("%s.\n", gai_strerror(error));
usage(argv[0]);
exit(1);
}
switch (res->ai_family) {
case AF_INET:
t_addr = (struct sockaddr_in *)&s_rem;
memcpy(t_addr, res->ai_addr,
res->ai_addrlen);
t_addr->sin_family = res->ai_family;
t_addr->sin_port = htons(remote_port);
r_len = res->ai_addrlen;
#ifdef __FreeBSD__
t_addr->sin_len = r_len;
#endif
break;
case AF_INET6:
t_addr6 = (struct sockaddr_in6 *)&s_rem;
memcpy(t_addr6, res->ai_addr,
res->ai_addrlen);
t_addr6->sin6_family = res->ai_family;
t_addr6->sin6_port = htons(remote_port);
if (interface)
t_addr6->sin6_scope_id =
if_nametoindex(interface);
r_len = res->ai_addrlen;
#ifdef __FreeBSD__
t_addr6->sin6_len = r_len;
#endif
break;
}
getnameinfo((struct sockaddr *)&s_rem, r_len, host_s,
NI_MAXHOST, serv_s, NI_MAXSERV, NI_NUMERICHOST);
DEBUG_PRINT(DEBUG_MAX, "remote:addr=%s, port=%s, family=%d\n",
host_s, serv_s, res->ai_family);
freeaddrinfo(res);
}
if (connectx_count != 0) {
switch (connectx_addrs->sa_family) {
case AF_INET:
t_addr = (struct sockaddr_in *)&s_rem;
r_len = sizeof(struct sockaddr_in);
memcpy(t_addr, connectx_addrs, r_len);
t_addr->sin_port = htons(remote_port);
break;
case AF_INET6:
t_addr6 = (struct sockaddr_in6 *)&s_rem;
r_len = sizeof(struct sockaddr_in6);
memcpy(t_addr6, connectx_addrs, r_len);
t_addr6->sin6_port = htons(remote_port);
break;
}
}
if (local_host != NULL) {
struct addrinfo *res;
int error;
char *host_s, *serv_s;
struct sockaddr_in *t_addr;
struct sockaddr_in6 *t_addr6;
if ((host_s = malloc(NI_MAXHOST)) == NULL) {
fprintf(stderr, "\n*** host_s malloc failed!!! ***\n");
exit(1);
}
if ((serv_s = malloc(NI_MAXSERV)) == NULL) {
fprintf(stderr, "\n*** serv_s malloc failed!!! ***\n");
exit(1);
}
if (strcmp(local_host, "0") == 0)
local_host = "0.0.0.0";
error = getaddrinfo(local_host, 0, NULL, &res);
if (error) {
printf("%s.\n", gai_strerror(error));
usage(argv[0]);
exit(1);
}
switch (res->ai_family) {
case AF_INET:
t_addr = (struct sockaddr_in *)&s_loc;
memcpy(t_addr, res->ai_addr,
res->ai_addrlen);
t_addr->sin_family = res->ai_family;
t_addr->sin_port = htons(local_port);
l_len = res->ai_addrlen;
#ifdef __FreeBSD__
t_addr->sin_len = l_len;
#endif
break;
case AF_INET6:
t_addr6 = (struct sockaddr_in6 *)&s_loc;
memcpy(t_addr6, res->ai_addr,
res->ai_addrlen);
t_addr6->sin6_family = res->ai_family;
t_addr6->sin6_port = htons(local_port);
if (interface)
t_addr6->sin6_scope_id =
if_nametoindex(interface);
l_len = res->ai_addrlen;
#ifdef __FreeBSD__
t_addr6->sin6_len = l_len;
#endif
break;
}
error = getnameinfo((struct sockaddr *)&s_loc, l_len, host_s,
NI_MAXHOST, serv_s, NI_MAXSERV, NI_NUMERICHOST);
if (error)
printf("%s..\n", gai_strerror(error));
DEBUG_PRINT(DEBUG_MAX, "local:addr=%s, port=%s, family=%d\n",
host_s, serv_s, res->ai_family);
freeaddrinfo(res);
}
/* A half-hearted attempt to seed rand() */
if (seed == 0 ) {
seed = time(0);
DEBUG_PRINT(DEBUG_NONE, "seed = %d\n", seed);
}
srand(seed);
/* Let the testing begin. */
start_test(role);
return 0;
} /* main() */