blob: 49e3d4b6785234c272c9f6e969c67789aa5e3dde [file]
#include <stdlib.h>
#include <stdio.h>
#include <strings.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <netinet/sctp.h>
#include <sys/uio.h>
#include <arpa/inet.h>
int sd;
struct sockaddr_in to;
static void
handle_notification(int fd,char *notify_buf) {
union sctp_notification *snp;
struct sctp_assoc_change *sac;
struct sctp_paddr_change *spc;
struct sctp_remote_error *sre;
struct sctp_send_failed *ssf;
struct sctp_shutdown_event *sse;
char *str;
char buf[256];
struct sockaddr_in *sin;
struct sockaddr_in6 *sin6;
snp = (union sctp_notification *)notify_buf;
switch(snp->sn_header.sn_type) {
case SCTP_ASSOC_CHANGE:
sac = &snp->sn_assoc_change;
switch(sac->sac_state) {
case SCTP_COMM_UP:
str = "COMMUNICATION UP";
break;
case SCTP_COMM_LOST:
str = "COMMUNICATION LOST";
break;
case SCTP_RESTART:
str = "RESTART";
break;
case SCTP_SHUTDOWN_COMP:
str = "SHUTDOWN COMPLETE";
break;
case SCTP_CANT_STR_ASSOC:
str = "CANT START ASSOC";
break;
default:
str = "UNKNOWN";
} /* end switch(sac->sac_state) */
printf("SCTP_ASSOC_CHANGE: %s, assoc=%xh\n", str,
(u_int32_t)sac->sac_assoc_id);
break;
case SCTP_PEER_ADDR_CHANGE:
spc = &snp->sn_paddr_change;
switch(spc->spc_state) {
case SCTP_ADDR_AVAILABLE:
str = "ADDRESS AVAILABLE";
break;
case SCTP_ADDR_UNREACHABLE:
str = "ADDRESS UNAVAILABLE";
break;
case SCTP_ADDR_REMOVED:
str = "ADDRESS REMOVED";
break;
case SCTP_ADDR_ADDED:
str = "ADDRESS ADDED";
break;
case SCTP_ADDR_MADE_PRIM:
str = "ADDRESS MADE PRIMARY";
break;
default:
str = "UNKNOWN";
} /* end switch */
sin6 = (struct sockaddr_in6 *)&spc->spc_aaddr;
if (sin6->sin6_family == AF_INET6) {
inet_ntop(AF_INET6, (char*)&sin6->sin6_addr, buf, sizeof(buf));
} else {
sin = (struct sockaddr_in *)&spc->spc_aaddr;
inet_ntop(AF_INET, (char*)&sin->sin_addr, buf, sizeof(buf));
}
printf("SCTP_PEER_ADDR_CHANGE: %s, addr=%s, assoc=%xh\n", str,
buf, (u_int32_t)spc->spc_assoc_id);
break;
case SCTP_REMOTE_ERROR:
sre = &snp->sn_remote_error;
printf("SCTP_REMOTE_ERROR: assoc=%xh\n",
(u_int32_t)sre->sre_assoc_id);
break;
case SCTP_SEND_FAILED:
ssf = &snp->sn_send_failed;
printf("SCTP_SEND_FAILED: assoc=%xh\n",
(u_int32_t)ssf->ssf_assoc_id);
break;
case SCTP_ADAPTION_INDICATION:
{
struct sctp_adaptation_event *ae;
ae = &snp->sn_adaptation_event;
printf("\nSCTP_adaption_indication bits:0x%x\n",
(u_int)ae->sai_adaptation_ind);
}
break;
case SCTP_PARTIAL_DELIVERY_EVENT:
{
struct sctp_pdapi_event *pdapi;
pdapi = &snp->sn_pdapi_event;
printf("SCTP_PD-API event:%u\n",
pdapi->pdapi_indication);
if(pdapi->pdapi_indication == SCTP_PARTIAL_DELIVERY_ABORTED){
printf("Message was truncated\n");
}
}
break;
case SCTP_SHUTDOWN_EVENT:
sse = &snp->sn_shutdown_event;
printf("SCTP_SHUTDOWN_EVENT: assoc=%xh\n",
(u_int32_t)sse->sse_assoc_id);
break;
default:
printf("Unknown notification event type=%xh\n",
snp->sn_header.sn_type);
} /* end switch(snp->sn_header.sn_type) */
}
void
do_a_read()
{
struct sctp_sndrcvinfo *s_info;
int sz,i,disped;
struct msghdr msg;
struct iovec iov[2];
unsigned char from[200];
char readBuffer[65535];
char controlVector[65535];
struct cmsghdr *cmsg;
disped = i = 0;
s_info = NULL;
iov[0].iov_base = readBuffer;
iov[0].iov_len = sizeof(readBuffer);
iov[1].iov_base = NULL;
iov[1].iov_len = 0;
msg.msg_name = (caddr_t)from;
msg.msg_namelen = sizeof(from);
msg.msg_iov = iov;
msg.msg_iovlen = 1;
msg.msg_control = (caddr_t)controlVector;
msg.msg_controllen = sizeof(controlVector);
errno = 0;
sz = recvmsg(sd,&msg,0);
if(sz <= 0){
if((errno != ENOBUFS) && (errno != EAGAIN))
printf("Read returns %d errno:%d control len is %d msgflg:%x\n",
sz,errno,
msg.msg_controllen,msg.msg_flags);
return;
}
if (msg.msg_flags & MSG_NOTIFICATION) {
handle_notification(sd,readBuffer);
return;
}
if(msg.msg_controllen){
/* parse through and see if we find
* the sctp_sndrcvinfo
*/
cmsg = (struct cmsghdr *)controlVector;
while(cmsg){
if(cmsg->cmsg_level == IPPROTO_SCTP){
if(cmsg->cmsg_type == SCTP_SNDRCV){
/* Got it */
s_info = (struct sctp_sndrcvinfo *)CMSG_DATA(cmsg);
break;
}
}
cmsg = CMSG_NXTHDR(&msg,cmsg);
}
}
printf("Read %d bytes first string is %s\n",
sz,readBuffer);
if(s_info){
printf("str:%d sseq:%d flg:%d ppid:%u tsn:%x cum:%x\n",
(int)s_info->sinfo_stream,
(int)s_info->sinfo_ssn,
(int)s_info->sinfo_flags,
(int)ntohl(s_info->sinfo_ppid),
(u_int)s_info->sinfo_tsn,
(u_int)s_info->sinfo_cumtsn);
}
return;
}
void
do_listener()
{
char cmd[128];
int not_done = 1;
int i,len;
while(not_done) {
if( fgets( cmd, 128, stdin) == NULL) {
not_done = 0;
printf("fgets failed err:%d\n",errno);
continue;
}
len = strlen(cmd);
if(cmd[(len-1)] == '\n') {
cmd[(len-1)] = 0;
len--;
}
if((strcmp(cmd,"done") == 0) ||
(strcmp(cmd,"quit") == 0)){
not_done = 0;
printf("exiting\n");
} else if (strncmp(cmd, "read:",5) == 0) {
len = strtol(&cmd[5],NULL,0);
if(len <= 0){
printf("Sorry number must be postive (specified %d)\n",
len);
continue;
}
for(i=0;i<len;i++){
do_a_read();
}
printf("reading completes\n");
} else {
printf("Only commands are read:num <or> done %s not known\n",
cmd);
}
}
}
static int send_at=1;
void
do_sender()
{
u_int32_t ppid=0;
u_int32_t flags=0;
u_int16_t stream=0;
u_int32_t timetolive=0;
u_int32_t context=0;
u_int32_t msg_sz=0;
char msg[2048];
char cmd[128];
int not_done = 1;
int len,i,j,ret;
while(not_done) {
if( fgets( cmd, 128, stdin) == NULL) {
not_done = 0;
printf("fgets failed err:%d\n",errno);
continue;
}
len = strlen(cmd);
if(cmd[(len-1)] == '\n') {
cmd[(len-1)] = 0;
len--;
}
if((strcmp(cmd,"done") == 0) ||
(strcmp(cmd,"quit") == 0)){
not_done = 0;
printf("exiting\n");
} else if (strncmp(cmd, "send:",5) == 0) {
if(msg_sz == 0){
printf("size not set\n");
continue;
}
len = strtol(&cmd[5],NULL,0);
for (i=0; i<len; i++) {
for (j=0; j<msg_sz; j++) {
msg[j] = '0' + i;
}
sprintf(msg,"%d",send_at++);
ret = sctp_sendmsg(sd, msg, msg_sz, (struct sockaddr *)&to,
sizeof(to),
ppid, flags, stream, timetolive, context);
printf("return from send %d is %d err:%d\n",
i,ret,errno);
}
printf("sending is complete\n");
} else if (strncmp(cmd,"context:",8) == 0) {
context = strtoul(&cmd[8],NULL,0);
printf("context set to %u\n",
context);
} else if (strncmp(cmd,"ppid:",5) == 0) {
ppid = strtoul(&cmd[5],NULL,0);
printf("context set to %u\n",
ppid);
} else if (strncmp(cmd,"flags:",6) == 0) {
flags = strtoul(&cmd[6],NULL,0);
printf("flags set to %x\n",
flags);
} else if (strncmp(cmd,"stream:",7) == 0) {
stream = strtoul(&cmd[7],NULL,0);
printf("stream set to %d\n",
stream);
} else if (strncmp(cmd,"ttl:",4) == 0) {
timetolive = strtoul(&cmd[4],NULL,0);
printf("ttl set to %d\n",
timetolive);
} else if (strncmp(cmd,"sz:",3) == 0) {
msg_sz= strtoul(&cmd[3],NULL,0);
printf("sz set to %d\n",
msg_sz);
if(msg_sz > sizeof(msg)) {
#if defined(_LP64) || defined(__LP64__) || defined(__APPLE__)
printf("Sorry max size is %lu, override to this value\n",
#else
printf("Sorry max size is %u, override to this value\n",
#endif
sizeof(msg));
msg_sz = sizeof(msg);
}
} else if (strncmp(cmd,"?",1) == 0) {
goto help;
} else if (strncmp(cmd,"help",4) == 0) {
help:
printf("Commands are:\n");
printf(" context:ctx\n");
printf(" stream:strno\n");
printf(" ttl:time-to-live\n");
printf(" flags:flags-to-use-in-send(hex)\n");
printf(" ppid:per-proto-id\n");
printf(" sz:sz-to-send\n");
} else {
printf("Command not recognized '%s'\n",cmd);
}
}
}
int
main(int argc, char **argv)
{
int i;
u_int16_t his_port=0,my_port=0;
int host_set=0;
int send_buf = 8000;
int rcv_buf = 8000;
int optlen;
memset(&to,0,sizeof(to));
to.sin_family = AF_INET;
to.sin_len = sizeof(to);
while ((i= getopt(argc,argv,"p:m:r:s:h:")) != EOF) {
switch (i) {
case 's':
send_buf = strtol(optarg,NULL,0);
break;
case 'r':
rcv_buf = strtol(optarg,NULL,0);
break;
case 'p':
his_port = strtol(optarg,NULL,0);
break;
case 'm':
my_port = strtol(optarg,NULL,0);
break;
case 'h':
if(inet_pton(AF_INET, optarg, &to.sin_addr)) {
host_set = 1;
}
break;
default:
goto exit_use;
break;
};
}
if ((his_port == 0) && (my_port == 0)) {
exit_use:
printf("Use %s -p port -h dest-addr OR -m my-port [-r rcvbuf -s sndbuf]\n",
argv[0]);
return(0);
}
optlen = 4;
sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_SCTP);
if(sd == -1) {
printf("Can't open a socket '%s' err:%d\n",
strerror(errno),
errno);
return(0);
}
if (setsockopt(sd,SOL_SOCKET, SO_SNDBUF, &send_buf, optlen) != 0){
printf("Could not set sndbuf '%s err:%d\n",
strerror(errno),
errno);
}
if (setsockopt(sd,SOL_SOCKET, SO_RCVBUF, &rcv_buf, optlen) != 0){
printf("Could not set rcvbuf '%s err:%d\n",
strerror(errno),
errno);
}
{
struct linger linger;
socklen_t oop;
linger.l_onoff = 0;
linger.l_linger = 0;
oop = sizeof(linger);
if (getsockopt(sd, SOL_SOCKET, SO_LINGER, (void *)&linger, &oop) < 0) {
perror("getsockopt");
} else {
printf("linger.l_onoff before calls is %d\n",
linger.l_onoff);
printf("linger.l_linger before calls is %d\n",
linger.l_linger);
}
}
if(my_port){
to.sin_port = htons(my_port);
if(bind(sd,(struct sockaddr *)&to, to.sin_len) < 0) {
printf("Failed to bind address '%s' err:%d\n",
strerror(errno),
errno);
return(0);
}
if(listen(sd, 5) == -1) {
printf("Failed to set listen '%s' err:%d\n",
strerror(errno),
errno);
return(0);
}
do_listener();
} else {
to.sin_port = htons(his_port);
do_sender();
}
{
struct linger linger;
socklen_t oop;
linger.l_onoff = 0;
linger.l_linger = 0;
oop = sizeof(linger);
if (getsockopt(sd, SOL_SOCKET, SO_LINGER, (void *)&linger, &oop) < 0) {
perror("getsockopt");
} else {
printf("linger.l_onoff after calls is %d\n",
linger.l_onoff);
printf("linger.l_linger after calls is %d\n",
linger.l_linger);
}
}
return(0);
}