SCTP Discussions

Text archives Help


[SCTP-Discussion] RETRY: An error in the SCTPlib/SocketAPI


Chronological Thread 
  • From: Torbjörn Andersson <torbjorn.andersson AT kau.se>
  • To: 'Torbjörn Andersson' <torbjorn.andersson AT kau.se>
  • Cc: <discussion AT sctp.de>
  • Subject: [SCTP-Discussion] RETRY: An error in the SCTPlib/SocketAPI
  • Date: Thu Oct 23 10:27:01 2003
  • Importance: Normal
  • List-id: SCTP Discussions <discussion.sctp.de>

Hi all

I'm writing to you again now because I still have problem with the SCTP
layer. The 1.0.0 version did improve things but there is still a problem
when one sends messages without intermediate delays.

You can try it yourself if you compile my program and send 1000 messages
with an interval of 0ms, each message containing 500 bytes. SCTP does
not fail every time but it will eventually. I guess within 20 retries.
The simplest network setup will do fine(2 hosts). What happens is that
SCTP stalls somewhere as

I hope you find this information valuable and find the problem, what
ever it is.

Another problem:
When I initiate a shutdown on an association in a UDP socket I should be
able to retrieve the SCTP_SHUT_COMP notification but I'm not. I'm
sending a message with MSG_EOF set and the association is closed
correctly except that the initiator does not retrieve the
SCTP_SHUT_COMP.

Can you look into this? I find it difficult to debug your code so I
leave it to you.


Regards, Torbjörn Andersson
#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/uio.h>
#include <ext_socket.h>
#include <netdb.h>
#include <iostream>
#include <list>
using std::list;

#ifdef __APPLE__
#include "inet_ntop.c"
#endif
#include <string.h>

#define BUFLEN 100
#define PACKETSIZE 1400
#define S2MS 1000

//#define VERBOSE_1
//#define VERBOSE_0

typedef struct{
u_long seq_num;
struct timeval queued;
struct timeval recved;
}message_t;

#ifdef VERBOSE_1
static void
handle_event(void *buf)
{
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;

snp = (sctp_notification *)buf;

switch ((snp->sn_header).sn_type) {
case SCTP_ASSOC_CHANGE:
sac = &snp->sn_assoc_change;
printf("^^^ 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_SEND_FAILED:
ssf = &snp->sn_send_failed;
printf("^^^ sendfailed: len=%hu err=%d\n", ssf->ssf_length,
ssf->ssf_error);
break;
case SCTP_PEER_ADDR_CHANGE:
spc = &snp->sn_paddr_change;
if (((struct sockaddr_in *)&spc->spc_aaddr)->sin_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);
}
printf("^^^ intf_change: %s state=%d, error=%d\n", ap, spc->spc_state,
spc->spc_error);
break;
case SCTP_REMOTE_ERROR:
sre = &snp->sn_remote_error;
printf("^^^ remote_error: err=%hu len=%hu\n", ntohs(sre->sre_error),
ntohs(sre->sre_length));
break;
case SCTP_SHUTDOWN_EVENT:
printf("^^^ shutdown event\n");
break;
default:
printf("unknown type: %hu\n", snp->sn_header.sn_type);
break;
}
}
#endif

void prnMessage(message_t msg)
{
struct timeval curr_time;
gettimeofday(&curr_time,NULL);

printf("%ld\t", msg.seq_num);
printf("%lf\t", msg.queued.tv_sec + msg.queued.tv_usec/1000000.0);
printf("%lf\n", msg.recved.tv_sec + msg.recved.tv_usec/1000000.0);
}


unsigned long receiveData(int fd, list<message_t>& mesgs)
{
unsigned long total = 0;
struct msghdr msg[1];
struct cmsghdr *cmsg;
struct iovec iov[1];
char cbuf[CMSG_SPACE(sizeof(struct sctp_sndrcvinfo))];
char *buf;
ssize_t buflen;
ssize_t cmsglen = CMSG_SPACE(sizeof(struct sctp_sndrcvinfo));


/* Allocate the initial data buffer */
buflen = PACKETSIZE;
if (!(buf = (char*)malloc(sizeof(char)*PACKETSIZE))) {
fprintf(stderr, "out of memory\n");
exit(1);
}

iov[0].iov_base = buf;
iov[0].iov_len = sizeof(char)*PACKETSIZE;

/* Set up the msghdr structure for receiving */
memset(msg, 0, sizeof (*msg));
msg->msg_name = NULL;
msg->msg_namelen = 0;
msg->msg_control = cbuf;
msg->msg_controllen = cmsglen;
msg->msg_flags = 0;
msg->msg_iov = iov;
msg->msg_iovlen = 1;
cmsg = (struct cmsghdr *)cbuf;

/* Wait for something to echo */
while (1){
u_int nr = 0;
msg->msg_flags = 0;
iov[0].iov_len = sizeof(char)*PACKETSIZE;
nr = ext_recvmsg(fd, msg, 0);
if( (msg->msg_flags & MSG_EOR) == 0) //Everything is not read
printf("Everything is not read yet");
/* Intercept notifications here */
if (msg->msg_flags & MSG_NOTIFICATION) {
#ifdef VERBOSE_2
handle_event(buf);
#endif
union sctp_notification *snp = (union sctp_notification *)buf;
if(snp->sn_header.sn_type == SCTP_SHUTDOWN_EVENT){
#ifdef VERBOSE_0
printf("Shutdown notification!\n");
fflush(stdout);
#endif
}

if(snp->sn_header.sn_type == SCTP_ASSOC_CHANGE){
#ifdef VERBOSE_0
printf("SCTP ASSOC CHANGE!\n");
fflush(stdout);
#endif
if(snp->sn_assoc_change.sac_state == SCTP_SHUTDOWN_COMP){
#ifdef VERBOSE_0
printf("SCTP SHUTDOWN COMPLETE!\n");
fflush(stdout);
#endif
return total;
}
}
}
else if(nr != 20){
total += nr;
#ifdef VERBOSE_0
struct sctp_sndrcvinfo *sri =
(struct sctp_sndrcvinfo *)CMSG_FIRSTHDR(msg);
printf("Msg[%d]%d bytes received at stream
%d\n",mesgs.size(),nr,sri->sinfo_stream);
#endif
message_t msg;
bcopy(buf,&msg,sizeof(message_t));
gettimeofday(&(msg.recved),0);
mesgs.insert(mesgs.end(),msg);
}
}
return total;
}


int main(int argc, char** argv)
{
int fd;
struct hostent *addr;
char * localIP, * remoteIP;
int port;

if(argc != 4){
printf("Usage: sctpClient <local ip> <remote ip> <port>\n");
perror("Got:");
for(int i = 1; i < argc; i++){
fprintf(stderr,"%s ",argv[1]);
}
perror("\n");
exit(-1);
}


localIP = argv[1];
remoteIP = argv[2];
port = atoi(argv[3]);

struct sockaddr_in sin_src[1];
if (atoi(localIP) > 0 ) {
/* Numeric */
sin_src->sin_family = AF_INET;
sin_src->sin_addr.s_addr = inet_addr(localIP);
} else {
if ((addr=gethostbyname(localIP)) == NULL){
perror("bad hostname");
exit(-1);
}
sin_src->sin_family = addr->h_addrtype;

struct in_addr addr_tmp[1];
bcopy(addr->h_addr_list[0],(char*)&addr_tmp, sizeof(struct in_addr));
sin_src->sin_addr.s_addr = addr_tmp->s_addr;
}
sin_src->sin_port = htons(0);

struct sockaddr_in sin_dest[1];
if (atoi(remoteIP) > 0 ) {
/* Numeric */
sin_dest->sin_family = AF_INET;
sin_dest->sin_addr.s_addr = inet_addr(remoteIP);
} else {
if ((addr=gethostbyname(remoteIP)) == NULL){
perror("bad hostname");
exit(-1);
}
sin_dest->sin_family = addr->h_addrtype;

struct in_addr addr_tmp[1];
bcopy(addr->h_addr_list[0],(char*)&addr_tmp, sizeof(struct in_addr));
sin_dest->sin_addr.s_addr = addr_tmp->s_addr;
}
sin_dest->sin_port = htons(port);

/*
if ((fd = ext_socket(PF_INET, SOCK_SEQPACKET, IPPROTO_SCTP)) == -1) {
perror("socket");
exit(1);
}
*/
if ((fd = ext_socket(PF_INET, SOCK_STREAM, IPPROTO_SCTP)) == -1) {
perror("socket");
exit(1);
}

/* Enable notifications */

struct sctp_event_subscribe subscription;
subscription.sctp_data_io_event = 1;
subscription.sctp_association_event = 1;
subscription.sctp_address_event = 0;
subscription.sctp_send_failure_event = 0;
subscription.sctp_peer_error_event = 0;
subscription.sctp_partial_delivery_event = 0;
subscription.sctp_adaption_layer_event = 0;
subscription.sctp_shutdown_event = 1;

if(ext_setsockopt(fd,IPPROTO_SCTP,SCTP_EVENTS, &subscription,
sizeof(subscription)) != 0){
perror("setevent failed\n");
exit(1);
}


struct sctp_initmsg initmsg;
initmsg.sinit_num_ostreams = 1;
initmsg.sinit_max_instreams = 1;
initmsg.sinit_max_attempts = 10;
initmsg.sinit_max_init_timeo = 60;

unsigned long size = sizeof(initmsg);
if (ext_setsockopt(fd, IPPROTO_SCTP, SCTP_INITMSG, &initmsg, size) < 0) {
perror("setsockopt SCTP_INITMSG");
exit(1);
}

if (ext_bind(fd, (struct sockaddr *)sin_src, sizeof (*sin_src)) == -1) {
perror("bind");
exit(1);
}

if(ext_connect(fd,(struct sockaddr*)sin_dest,sizeof(struct sockaddr)) != 0){
printf("0 messages were retrieved\n");
printf("Unable to connect to server after 10 retries\n");
exit(-1);
}
class list<message_t> results;
receiveData(fd,results);
close(fd);
class list<message_t>::iterator iter = results.begin();
while(iter != results.end()){
prnMessage(*iter);
iter++;
}
}

#include <ext_socket.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <sys/time.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define MS2USEC 1000
#define BUFLEN 2000
#define INTERVALL_CORRECTION 10000

//#define VERBOSE_0
//#define VERBOSE_1
//#define VERBOSE_2
#define VERBOSE_SHUT

#include <stdio.h>
#include <stdlib.h>
struct message{
u_long seq_num;
struct timeval queued;
struct timeval recved;
};

void shutdownAssoc(int fd, sctp_assoc_t aId){
struct sctp_sndrcvinfo *sri;
struct msghdr msg[1];
struct cmsghdr *cmsg;
struct iovec iov[1];

char cbuf[CMSG_SPACE(sizeof(*sri))];
msg->msg_name = 0; //peer;
msg->msg_namelen = 0;//addr_len; //addrLen;

iov->iov_base = 0;
iov->iov_len = 0;

msg->msg_iov = iov;
msg->msg_iovlen = 1;
msg->msg_flags = 0;

msg->msg_control = cbuf;
msg->msg_controllen = CMSG_SPACE(sizeof(*sri));

cmsg = CMSG_FIRSTHDR(msg);
cmsg->cmsg_len = CMSG_LEN(sizeof(*sri));
cmsg->cmsg_level = IPPROTO_SCTP;
cmsg->cmsg_type = SCTP_SNDRCV;
sri = (struct sctp_sndrcvinfo*)CMSG_DATA(cmsg);

sri->sinfo_stream = 0;
sri->sinfo_ssn = 0; //Ignored when sending
sri->sinfo_flags = MSG_EOF; //Unordered delivery
sri->sinfo_ppid = 0;
sri->sinfo_assoc_id = aId; //0 assoc_id;

if(ext_sendmsg(fd,msg,0) < 0){
perror("something went wrong during sending");
exit(-1);
}
}

void
send(int socket, sctp_assoc_t aId, struct message msgStamp, unsigned int
msgSize)
{
struct sctp_sndrcvinfo *sri;
struct msghdr msg[1];
struct cmsghdr *cmsg;
char cbuf[CMSG_SPACE(sizeof(*sri))];
char *buf;
struct iovec iov[1];

/* Allocate the initial data buffer */
if (!(buf = (char*)malloc(sizeof(char)*msgSize))) {
fprintf(stderr, "out of memory\n");
exit(1);
}
memset(buf,'1',msgSize);

/* Insert messagestamp */
bcopy(&msgStamp,buf,sizeof(struct message));

/* Set up the msghdr structure for receiving */
msg->msg_name = 0; //peer;
msg->msg_namelen = 0;//addr_len; //addrLen;

msg->msg_iov = iov;
msg->msg_iovlen = 1;
iov->iov_base = buf;
iov->iov_len = msgSize;

msg->msg_control = cbuf;
msg->msg_controllen = CMSG_SPACE(sizeof(*sri));
msg->msg_flags = 0;

cmsg = CMSG_FIRSTHDR(msg);

cmsg->cmsg_len = CMSG_LEN(sizeof(*sri));
cmsg->cmsg_level = IPPROTO_SCTP;
cmsg->cmsg_type = SCTP_SNDRCV;
sri = (struct sctp_sndrcvinfo*)CMSG_DATA(cmsg);

sri->sinfo_stream = 0;
sri->sinfo_ssn = 0; //Ignored when sending
sri->sinfo_flags = MSG_UNORDERED;// | last == true ? MSG_EOF : 0;
//Unordered delivery
sri->sinfo_ppid = getpid();
sri->sinfo_assoc_id = aId; //0 assoc_id;

if(ext_sendmsg(socket,msg,0) < 0){
perror("something went wrong during sending");
exit(-1);
}
free(buf);
}

static void
waitForShutdown(int fd)
{
struct iovec iov[1];
struct msghdr msg[1];
ssize_t len;

char *buf;
ssize_t buflen;

/* Allocate the initial data buffer */
buflen = BUFLEN;
if (!(buf = (char*)malloc(BUFLEN))) {
fprintf(stderr, "out of memory\n");
exit(1);
}
iov[0].iov_base = buf;
iov[0].iov_len = sizeof(char)*BUFLEN;

/* Set up the msghdr structure for receiving */
memset(msg, 0, sizeof (*msg));
msg->msg_iov = &iov[0];
msg->msg_iovlen = 1;

/* Wait for a notification */
while (1){
#ifdef VERBOSE_SHUT
printf("Waiting for shutdown_comp event!\n");
#endif
len = ext_recvmsg(fd, msg, 0);
/* Intercept notifications here */
if(len > 0){
#ifdef VERBOSE_SHUT
printf("Received message!\n");
// handle_event(buf);
#endif
if (msg->msg_flags & MSG_NOTIFICATION) {
union sctp_notification *snp = (union sctp_notification *)buf;
if(snp->sn_header.sn_type == SCTP_ASSOC_CHANGE &&
snp->sn_assoc_change.sac_state == SCTP_SHUTDOWN_COMP){
return;
}
else{
#ifdef VERBOSE_SHUT
printf("Some other state!\n");
#endif
}
}
else{
#ifdef VERBOSE_SHUT
printf("Some other notification!\n");
#endif
}
}
else{
#ifdef VERBOSE_SHUT
printf("Unalbe to read from fd!\n");
#endif
return;
}
}
}

sctp_assoc_t getNewConnection(int fd)
{
struct msghdr msg[1];
char *buf;
struct iovec iov[1];
ssize_t buflen;
ssize_t len;

/* Allocate the initial data buffer */
buflen = BUFLEN;
if (!(buf = (char*)malloc(BUFLEN))) {
fprintf(stderr, "out of memory\n");
exit(1);
}
iov[0].iov_base = buf;
iov[0].iov_len = sizeof(char)*BUFLEN;

/* Set up the msghdr structure for receiving */
memset(msg, 0, sizeof (*msg));
msg->msg_iov = &iov[0];
msg->msg_iovlen = 1;

#ifdef VERBOSE_1
printf("Waiting for new connection\n");
fflush(stdout);
#endif
while (1){
len = ext_recvmsg(fd, msg, 0);
/* Intercept notifications here */
#ifdef VERBOSE_2
printf("Received message!\n");
fflush(stdout);
#endif
if (msg->msg_flags & MSG_NOTIFICATION) {
union sctp_notification *snp = (union sctp_notification *)buf;
if(snp->sn_header.sn_type == SCTP_ASSOC_CHANGE){

if(snp->sn_assoc_change.sac_state == SCTP_COMM_UP){
return snp->sn_assoc_change.sac_assoc_id;
}
}
}
}
}


int setSocketOptions(int fd)
{
/* Enable notifications */

struct sctp_event_subscribe subscription;
subscription.sctp_data_io_event = 1;
subscription.sctp_association_event = 1;
subscription.sctp_address_event = 0;
subscription.sctp_send_failure_event = 1;
subscription.sctp_peer_error_event = 0;
subscription.sctp_partial_delivery_event = 0;
subscription.sctp_adaption_layer_event = 0;
subscription.sctp_shutdown_event = 1;

if(ext_setsockopt(fd,IPPROTO_SCTP,SCTP_EVENTS, &subscription,
sizeof(subscription)) != 0){
perror("setevent failed\n");
return -1;
}
return 0;
}

struct message createMsg(struct timeval stamp){
static int id = 0;
struct message msg;
msg.seq_num = id++;
msg.queued = stamp;
memset(&(msg.recved),'A',sizeof(struct timeval));
return msg;
}

void prnMessage(struct message msg)
{
struct timeval curr_time;
gettimeofday(&curr_time,NULL);

printf("%ld\t", msg.seq_num);
printf("%lf\t", msg.queued.tv_sec + msg.queued.tv_usec/1000000.0);

printf("%lf\n", msg.recved.tv_sec + msg.recved.tv_usec/1000000.0);
}


void waitFor(long time){
long sleepTime;
// if(time > 10000){
#ifdef VERBOSE_2
printf("Sleeping\n");
fflush(stdout);
#endif
sleepTime = time - INTERVALL_CORRECTION;
usleep( (sleepTime < 0) ? 0: sleepTime);
/* }
else{
struct timeval start, now;
long currTime, startTime;
#ifdef VERBOSE_2
printf("busy-wait sleep: %ld ms\n", time/1000);
fflush(stdout);
#endif
gettimeofday(&start,0);
startTime = start.tv_sec*1000000 + start.tv_usec;
do{
gettimeofday(&now,0);
currTime = now.tv_sec*1000000 + now.tv_usec;
}while( (currTime - startTime) < time );
}
*/
}

void sendData(int newFd, sctp_assoc_t aId, unsigned int interval, unsigned
int msgSize, unsigned int msgCnt){
unsigned int sent = 0;
struct timeval start[1], now[1], tmp[1];

gettimeofday(start,0);
while(sent < msgCnt){
gettimeofday(now,0);
tmp->tv_sec = start->tv_sec + (interval/1000)*sent;
tmp->tv_usec = start->tv_usec + (interval%1000)*MS2USEC*sent;
if(tmp->tv_usec/1000000 > 0){
tmp->tv_sec += tmp->tv_usec/1000000;
tmp->tv_usec = tmp->tv_usec%1000000;
}
if( timercmp(now, tmp, >=) ){
struct message msg = createMsg(*tmp);

#ifdef VERBOSE_1
printf("Sending msg: %d...",sent+1);
fflush(stdout);
#endif
#ifdef VERBOSE_2
printf("stamp %lf\t", tmp->tv_sec + tmp->tv_usec/1000000.0);
printf("now %lf\t\n", now->tv_sec + now->tv_usec/1000000.0);
fflush(stdout);
#endif
send(newFd,aId,msg,msgSize);
sent++;
}
else{
long sleepTime = (tmp->tv_sec*1000000 + tmp->tv_usec) -
(now->tv_sec*1000000 + now->tv_usec);
#ifdef VERBOSE_1
printf("Waiting %ld ms\n",sleepTime/1000);
fflush(stdout);
#endif
waitFor(sleepTime);
}
}
#ifdef VERBOSE_0
printf("Everything sent!\n");
fflush(stdout);
#endif
}



int main(int argc, char** argv)
{
int port, interval, msgsize, msgCnt;
int fd;
char* localIP;
struct sockaddr_in sin_src[1];
struct hostent *addr;

if(argc != 6){
perror("Usage: sctpServer <local ip> <port> <interval between msgs(ms)>
<message size>(bytes) <messages>\n");
perror("Got:");
for(int i = 1; i < argc; i++){
fprintf(stderr,"%s ",argv[1]);
}
perror("\n");
exit(-1);
}

if ((fd = ext_socket(PF_INET, SOCK_SEQPACKET, IPPROTO_SCTP)) == -1) {
perror("socket");
exit(1);
}

setSocketOptions(fd);

localIP = argv[1];
port = atoi(argv[2]);
interval = atoi(argv[3]);
msgsize = atoi(argv[4]);
msgCnt = atoi(argv[5]);

if (atoi(localIP) > 0 ) {
/* Numeric */
sin_src->sin_family = AF_INET;
sin_src->sin_addr.s_addr = inet_addr(localIP);
} else {
if ((addr=gethostbyname(localIP)) == NULL){
perror("bad hostname");
exit(-1);
}
sin_src->sin_family = addr->h_addrtype;

struct in_addr addr_tmp[1];
bcopy(addr->h_addr_list[0],(char*)&addr_tmp, sizeof(struct in_addr));
sin_src->sin_addr.s_addr = addr_tmp->s_addr;
}
sin_src->sin_port = htons(port);

if (ext_bind(fd, (struct sockaddr *)sin_src, sizeof (*sin_src)) == -1) {
perror("Unable to bind socket\n");
exit(1);
}

struct sctp_initmsg initmsg;
initmsg.sinit_num_ostreams = 1;
initmsg.sinit_max_instreams = 1;
initmsg.sinit_max_attempts = 10;
initmsg.sinit_max_init_timeo = 600;

unsigned long size = sizeof(initmsg);
if (ext_setsockopt(fd, IPPROTO_SCTP, SCTP_INITMSG, &initmsg, size) < 0) {
perror("setsockopt SCTP_INITMSG");
exit(1);
}

/* Allow new associations to be accepted */
if (ext_listen(fd, 10) < 0) {
perror("listen");
exit(1);
}

sctp_assoc_t aId = getNewConnection(fd);

#ifdef VERBOSE_1
printf("New connection recieved!\n");
fflush(stdout);
#endif

sendData(fd,aId,interval,msgsize,msgCnt);
shutdownAssoc(fd,aId);
// ext_close(fd);
waitForShutdown(fd);
/* while(1){
sleep(2);
}
*/
}



Archive powered by MHonArc 2.6.19+.

Top of page