Using SCTP Sockets
This section details three uses of SCTP sockets.
Example 7-17 Using SCTP Echo Client in One-to-One Socket Model
/*
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
*/
/*
* IPv4 echo client.
*/
/* To enable socket features used for SCTP socket. */
#define _XPG4_2
#define __EXTENSIONS__
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
#include <netinet/sctp.h>
#include <errno.h>
#define BUFLEN 2048
#define SERVER_PORT 5000
#define MAX_STREAM 64
static void
usagBe(char *a0)
{
fprintf(stderr, "Usage: %s <server address>\n", a0);
}
static void
print_notif(char *buf)
{
union sctp_notification *snp;
struct sctp_assoc_change *sac;
snp = (union sctp_notification *)buf;
/* We only subscribe the association change event. */
if (snp->sn_header.sn_type != SCTP_ASSOC_CHANGE) {
fprintf(stderr, "unexpected notification type: %d\n",
snp->sn_header.sn_type);
exit(1);
}
sac = &snp->sn_assoc_change;
printf("[ Receive assocication change event: state = %hu," error = %hu,"
instr = %hu, outstr = %hu ]\n", sac->sac_state,
sac->sac_error,sac->sac_inbound_streams,
sac->sac_outbound_streams);
}
/*
* Read from the network.
*/
static void
readit(void *vfdp)
{
int fd;
ssize_t n;
char buf[BUFLEN];
struct iovec iov[1];
int flags;
socklen_t info_len;
uint_t info_type;
struct sctp_rcvinfo info;
union sctp_notification *snp;
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
fd = *(int *)vfdp;
/* Initialize the iov for receiving */
memset(buf, 0, BUFLEN);
iov->iov_base = buf;
iov->iov_len = BUFLEN;
info_len = sizeof (info);
info_type = 0;
flags = 0;
while ((n = sctp_recvv(fd, iov, 1, NULL, NULL, &info,
&info_len,&info_type, &flags)) > 0) {
/* Intercept notifications here */
if (flags & MSG_NOTIFICATION) {
print_notif(buf);
continue;
}
/* The message should be accompanied by sctp_rcvinfo. */
if (info_type != SCTP_RECVV_RCVINFO) {
fprintf(stderr, "no sctp_rcvinfo attached\n");
exit(1);
}
printf("[ Receive echo (%u bytes): stream = %hu, ssn = %hu," "tsn = %hu,
flags = %hx, ppid = %u ]\n", n, info.rcv_sid, info.rcv_ssn, info.rcv_tsn,
info.rcv_flags, info.rcv_ppid);
flags = 0;
info_len = sizeof (info);
}
if (n < 0) {
perror("sctp_recvv");
exit(1);
}
close(fd);
exit(0);
}
static void
echo(struct sockaddr_in *addrs, int addrcnt)
{
int fd;
uchar_t buf[BUFLEN];
ssize_t n;
int perr;
pthread_t tid;
struct iovec iov[1];
int ret, on;
struct sctp_sndinfo info;
struct sctp_initmsg initmsg;
struct sctp_event event;
/* Create a one-one SCTP socket */
if ((fd = socket(AF_INET, SOCK_STREAM, IPPROTO_SCTP)) == -1) {
perror("socket");
exit(1);
}
/*
* We are interested in association change events and we want
* to get sctp_rcvinfo in each receive.
*/
event.se_assoc_id = 0; /* Ignored for one-one SCTP socket */
event.se_type = SCTP_ASSOC_CHANGE;
event.se_on = 1;
ret = setsockopt(fd, IPPROTO_SCTP, SCTP_EVENT, &event, sizeof (event));
if (ret < 0) {
perror("setsockopt SCTP_EVENT");
exit(1);
}
on = 1;
ret = setsockopt(fd, IPPROTO_SCTP, SCTP_RECVRCVINFO, &on, sizeof (on));
if (ret < 0) {
perror("setsockopt SCTP_RECVRCVINFO");
exit(1);
}
/*
* Set the SCTP stream parameters to tell the other side when
* setting up the association.
*/
memset(&initmsg, 0, sizeof (struct sctp_initmsg));
initmsg.sinit_num_ostreams = MAX_STREAM;
initmsg.sinit_max_instreams = MAX_STREAM;
initmsg.sinit_max_attempts = MAX_STREAM;
ret = setsockopt(fd, IPPROTO_SCTP, SCTP_INITMSG, &initmsg,sizeof (struct sctp_initmsg));
if (ret < 0) {
perror("setsockopt SCTP_INITMSG");
exit(1);
}
/* Now connect to the peer. */
if (sctp_connectx(fd, (struct sockaddr *)addrs, addrcnt, NULL) == -1) {
perror("sctp_connectx");
exit(1);
}
/* Initialize the struct sctp_sndinfo for sending. */
memset(&info, 0, sizeof (info));
/* Start sending to stream 0. */
info.snd_sid = 0;
/*
* Note that the server is expected to echo back the snd_ppid value.
* So we don't need to do any conversion here. But if the server needs
* to understand this value, we need to do a htonl() on it so that the
* server side can do a ntohl() to convert it back to the host byte
* order.
*/
info.snd_ppid = 0;
/* Create a thread to receive network traffic. */
perr = pthread_create(&tid, NULL, (void *(*)(void *))readit, &fd);
if (perr != 0) {
fprintf(stderr, "pthread_create: %d\n", perr);
exit(1);
}
iov->iov_base = buf;
/* Read from stdin and then send to the echo server. */
while ((n = read(fileno(stdin), buf, BUFLEN)) > 0) {
iov->iov_len = n;
if (sctp_sendv(fd, iov, 1, NULL, 0, &info, sizeof (info),
SCTP_SENDV_SNDINFO, 0) < 0) {
perror("sctp_sendv");
exit(1);
}
/* Send the next message to a different stream. */
info.snd_sid = (info.snd_sid + 1) % MAX_STREAM;
info.snd_ppid++;
}
pthread_cancel(tid);
close(fd);
}
static struct sockaddr_in *
setup_addrs(const char *name, int *addrcnt)
{
int num_addrs, i;
int error;
struct hostent *hp;
struct sockaddr_in *addrs;
hp = getipnodebyname(name, AF_INET, AI_DEFAULT, &error);
if (hp == NULL) {
fprintf(stderr, "host %s not found\n", name);
return (NULL);
}
for (num_addrs = 0; hp->h_addr_list[num_addrs] != NULL; num_addrs++)
;
addrs = malloc((num_addrs) * sizeof (*addrs));
if (addrs == NULL) {
fprintf(stderr, "cannot allocate address list\n");
return (NULL);
}
for (i = 0; i < num_addrs; i++) {
addrs[i].sin_family = AF_INET;
addrs[i].sin_addr.s_addr = *(ipaddr_t *)hp->h_addr_list[i];
addrs[i].sin_port = htons(SERVER_PORT);
}
*addrcnt = num_addrs;
return (addrs);
}
int
main(int argc, char **argv)
{
struct sockaddr_in *addrs;
int addrcnt;
if (argc < 2) {
usage(*argv);
exit(1);
}
/* Find the host to connect to. */
if ((addrs = setup_addrs(argv[1], &addrcnt)) == NULL)
exit(1);
echo(addrs, addrcnt);
return (0);
}
Example 7-18 Using SCTP Echo Server in One-to-One Socket Model
/*
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
*/
/*
* IPv4 echo server
*/
/* To enable socket features used for SCTP socket. */
#define _XPG4_2
#define __EXTENSIONS__
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
#include <netinet/sctp.h>
#include <netdb.h>
#define BUFLEN 1024
#define SERVER_PORT 5000
#define MAX_STREAM 64
/*
* Given an event notification, print out what it is.
*/
static void
handle_event(void *buf)
{
struct sctp_assoc_change *sac;
struct sctp_send_failed_event *ssfe;
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;
snp = 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_EVENT:
ssfe = &snp->sn_send_failed_event;
printf(">>> sendfailed: len=%hu err=%d\n", ssfe->ssfe_length,
ssfe->ssfe_error);
break;
case SCTP_PEER_ADDR_CHANGE:
spc = &snp->sn_paddr_change;
if (spc->spc_aaddr.ss_family != AF_INET) {
fprintf(stderr, "getmsg: unexpected family %d\n",spc->spc_aaddr.ss_family);
exit(1);
} else {
sin = (struct sockaddr_in *)&spc->spc_aaddr;
ap = inet_ntop(AF_INET, &sin->sin_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(">>> unexpected type: %hu\n", snp->sn_header.sn_type);
break;
}
}
/*
* Receive a message from the network.
*/
static ssize_t
getmsg(int fd, struct iovec *iov, struct sctp_rcvinfo *info, int *flags)
{
ssize_t tot = 0, nr;
size_t buflen;
socklen_t info_len;
uint_t info_type;
char *buf;
buf = iov->iov_base;
buflen = iov->iov_len;
/* Loop until a whole message is received. */
for (;;) {
info_len = sizeof (*info);
memset(info, 0, sizeof (*info));
*flags = 0;
nr = sctp_recvv(fd, iov, 1, NULL, NULL, info, &info_len,
&info_type, flags);
if (nr <= 0) {
/* EOF or error */
iov->iov_base = buf;
return (nr);
}
tot += nr;
/* Whole message/notification is received, return it. */
if (*flags & MSG_EOR || *flags & MSG_NOTIFICATION) {
iov->iov_base = buf;
/* Buffer may be realloc(). Return the new size. */
iov->iov_len = buflen;
return (tot);
}
/* Only sctp_rcvinfo is expected. */
if (info_type != SCTP_RECVV_RCVINFO) {
fprintf(stderr, "unexpected info received: %d\n",
info_type);
iov->iov_base = buf;
return (-1);
}
/* Maybe we need a bigger buffer, do realloc(). */
if (buflen == tot) {
buf = realloc(buf, buflen * 2);
if (buf == NULL) {
fprintf(stderr, "out of memory\n");
exit(1);
}
buflen *= 2;
}
/* Set the next read offset */
iov->iov_base = buf + tot;
iov->iov_len = buflen - tot;
}
}
/*
* The echo server.
*/
static void
echo(int fd)
{
ssize_t nr;
size_t buflen;
int flags;
struct iovec iov[1];
struct sctp_rcvinfo rinfo;
struct sctp_sndinfo sinfo;
if ((iov->iov_base = malloc(BUFLEN)) == NULL) {
fprintf(stderr, "out of memory\n");
exit(1);
}
iov->iov_len = BUFLEN;
memset(&sinfo, 0, sizeof (sinfo));
/* Wait for something to echo */
while ((nr = getmsg(fd, iov, &rinfo, &flags)) > 0) {
/* Intercept notifications here */
if (flags & SCTP_NOTIFICATION) {
handle_event(iov->iov_base);
continue;
}
printf(">>> got %u bytes on stream %hu: ", nr, rinfo.rcv_sid);
fflush(stdout);
write(fileno(stdout), iov->iov_base, nr);
fflush(stdout);
/* The buffer may be realloc(), so get the new size. */
buflen = iov->iov_len;
/*
* Echo the message back using the incoming info.
*
* Note that rcv_sid is in host byte order. But rcv_ppid is
* what is stored by the peer. If both sides wnat to use this
* value for communication (interpreting it on both sides),
* the sender needs to do htonl() when setting snd_ppid. And
* the receiver side needs to do ntohl() to convert rcv_ppid
* back to the host byte order.
*/
sinfo.snd_sid = rinfo.rcv_sid;
sinfo.snd_ppid = rinfo.rcv_ppid;
iov->iov_len = nr;
if (sctp_sendv(fd, iov, 1, NULL, 0, &sinfo, sizeof (sinfo),
SCTP_SENDV_SNDINFO, 0) < 0) {
fprintf(stderr, "sctp_sendv\n");
exit(1);
}
/* Restore the original buffer size. */
iov->iov_len = buflen;
}
free(iov->iov_base);
close(fd);
}
static void
subscribe_event(int fd, uint16_t event)
{
struct sctp_event ev;
int ret;
ev.se_assoc_id = 0; /* Ignored for one-one SCTP socket */
ev.se_type = event;
ev.se_on = 1;
ret = setsockopt(fd, IPPROTO_SCTP, SCTP_EVENT, &ev, sizeof (ev));
if (ret < 0) {
fprintf(stderr, "%s: setsockopt SCTP_EVENT: %d\n",strerror(errno), event);
exit(1);
}
}
/* List of events we are interested in. */
static uint16_t event_interested[] = {
SCTP_ASSOC_CHANGE,
SCTP_SEND_FAILED_EVENT,
SCTP_PEER_ADDR_CHANGE,
SCTP_REMOTE_ERROR,
SCTP_SHUTDOWN_EVENT
};
int main(void)
{
int lfd;
int cfd;
int onoff;
int i;
struct sockaddr_in sin[1];
struct sctp_initmsg initmsg;
if ((lfd = socket(AF_INET, SOCK_STREAM, IPPROTO_SCTP)) == -1) {
perror("socket");
exit(1);
}
sin->sin_family = AF_INET;
sin->sin_port = htons(SERVER_PORT);
sin->sin_addr.s_addr = INADDR_ANY;
if (bind(lfd, (struct sockaddr *)sin, sizeof (*sin)) == -1) {
perror("bind");
exit(1);
}
if (listen(lfd, 1) == -1) {
perror("listen");
exit(1);
}
(void) memset(&initmsg, 0, sizeof (struct sctp_initmsg));
initmsg.sinit_num_ostreams = MAX_STREAM;
initmsg.sinit_max_instreams = MAX_STREAM;
initmsg.sinit_max_attempts = MAX_STREAM;
if (setsockopt(lfd, IPPROTO_SCTP, SCTP_INITMSG, &initmsg,
sizeof (struct sctp_initmsg)) < 0) {
perror("SCTP_INITMSG");
exit(1);
}
/* Subscribe to events. */
for (i = 0; i < sizeof (event_interested) / sizeof (uint16_t); i++)
subscribe_event(lfd, event_interested[i]);
/* Wait for new associations */
for (;;) {
if ((cfd = accept(lfd, NULL, 0)) == -1) {
perror("accept");
exit(1);
}
/* Subcribe to interesting events for the new association. */
for (i = 0; i < sizeof (event_interested) / sizeof (int); i++)
subscribe_event(cfd, event_interested[i]);
/* We want sctp_rcvinfo in each receive. */
onoff = 1;
i = setsockopt(cfd, IPPROTO_SCTP, SCTP_RECVRCVINFO, &onoff,
sizeof (onoff));
if (i < 0) {
perror("setsockopt SCTP_RECVRCVINFO");
close(cfd);
continue;
}
/* Echo back any and all data */
echo(cfd);
}
}
Example 7-19 Using SCTP Echo Server in One-to-Many Sockets Model
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
*/
/*
* IPv4 echo server.
*/
/* To enable socket features used for SCTP socket. */
#define _XPG4_2
#define __EXTENSIONS__
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
#include <netinet/sctp.h>
#include <errno.h>
#define BUFLEN 1024
#define SERVER_PORT 5000
#define MAX_STREAM 64
/*
* Given an event notification, print out what it is.
*/
static void
handle_event(void *buf)
{
struct sctp_assoc_change *sac;
struct sctp_send_failed_event *ssfe;
struct sctp_paddr_change *spc;
struct sctp_remote_error *sre;
struct sctp_shutdown_event *sse;
union sctp_notification *snp;
char addrbuf[INET6_ADDRSTRLEN];
const char *ap;
struct sockaddr_in *sin;
snp = buf;
switch (snp->sn_header.sn_type) {
case SCTP_ASSOC_CHANGE:
sac = &snp->sn_assoc_change;
switch (sac->sac_state) {
case SCTP_COMM_UP:
/* A new association comes in. */
printf(">>> assoc_change COMM_UP: aid=%d instr=%hu "
"outstr=%hu\n", sac->sac_assoc_id,
sac->sac_inbound_streams,
sac->sac_outbound_streams);
break;
case SCTP_SHUTDOWN_COMP:
/* An association goes away. */
printf(">>> assoc_change SHUTDOWN_COMPLETE: aid=%d\n",
sac->sac_assoc_id);
break;
defaul:
printf(">>> assoc_change: aid=%d state=%hu error=%hu\n",
sac->sac_assoc_id, sac->sac_state, sac->sac_error);
break;
}
break;
case SCTP_SEND_FAILED_EVENT:
ssfe = &snp->sn_send_failed_event;
printf(">>> sendfailed: aid=%d len=%hu err=%d\n",
ssfe->ssfe_assoc_id, ssfe->ssfe_length, ssfe->ssfe_error);
break;
case SCTP_PEER_ADDR_CHANGE:
spc = &snp->sn_paddr_change;
if (spc->spc_aaddr.ss_family != AF_INET) {
fprintf(stderr, "getmsg: unexpected family %d\n",
spc->spc_aaddr.ss_family);
exit(1);
} else {
sin = (struct sockaddr_in *)&spc->spc_aaddr;
ap = inet_ntop(AF_INET, &sin->sin_addr, addrbuf,
INET6_ADDRSTRLEN);
}
printf(">>> intf_change: aid=%d %s state=%d, error=%d\n",
spc->spc_assoc_id, ap, spc->spc_state, spc->spc_error);
break;
case SCTP_REMOTE_ERROR:
sre = &snp->sn_remote_error;
printf(">>> remote_error: aid=%d err=%hu len=%hu\n",
sre->sre_assoc_id, ntohs(sre->sre_error),
ntohs(sre->sre_length));
break;
case SCTP_SHUTDOWN_EVENT:
sse = &snp->sn_shutdown_event;
printf(">>> shutdown event: aid=%d\n", sse->sse_assoc_id);
break;
default:
printf(">>> unexpected type: %hu\n", snp->sn_header.sn_type);
break;
}
}
/*
* Receive a message from the network.
*/
static ssize_t
getmsg(int fd, struct iovec *iov, struct sctp_rcvinfo *info, int *flags)
{
ssize_t tot = 0, nr;
size_t buflen;
socklen_t info_len;
uint_t info_type;
char *buf;
struct sockaddr_in addr;
socklen_t addrlen;
buf = iov->iov_base;
buflen = iov->iov_len;
/* Loop until a whole message is received. */
for (;;) {
info_len = sizeof (*info);
memset(info, 0, sizeof (*info));
*flags = 0;
nr = sctp_recvv(fd, iov, 1, (struct sockaddr *)&addr, &addrlen,
info, &info_len, &info_type, flags);
if (nr <= 0) {
if (nr < 0)
perror("sctp_recvv");
/* EOF or error */
iov->iov_base = buf;
return (nr);
}
tot += nr;
/* Whole message/notification is received, return it. */
if (*flags & MSG_EOR || *flags & MSG_NOTIFICATION) {
iov->iov_base = buf;
/* Buffer may be realloc(). Return the new size. */
iov->iov_len = buflen;
return (tot);
}
/* Only sctp_rcvinfo is expected. */
if (info_type != SCTP_RECVV_RCVINFO) {
fprintf(stderr, "unexpected info received: %d\n",
info_type);
iov->iov_base = buf;
return (-1);
}
/* Maybe we need a bigger buffer, do realloc(). */
if (buflen == tot) {
buf = realloc(buf, buflen * 2);
if (buf == NULL) {
fprintf(stderr, "out of memory\n");
exit(1);
}
buflen *= 2;
}
/* Set the next read offset */
iov->iov_base = buf + tot;
iov->iov_len = buflen - tot;
}
}
/*
* The echo server.
*/
static void
echo(int fd)
{
ssize_t nr;
size_t buflen;
int flags;
struct iovec iov[1];
struct sctp_rcvinfo rinfo;
struct sctp_sndinfo sinfo;
if ((iov->iov_base = malloc(BUFLEN)) == NULL) {
fprintf(stderr, "out of memory\n");
exit(1);
}
iov->iov_len = BUFLEN;
memset(&sinfo, 0, sizeof (sinfo));
/* Wait for something to echo */
while ((nr = getmsg(fd, iov, &rinfo, &flags)) > 0) {
/* Intercept notifications here */
if (flags & SCTP_NOTIFICATION) {
handle_event(iov->iov_base);
continue;
}
printf(">>> got %u bytes from aid %d on stream %hu (ssn %hu): ",
nr, rinfo.rcv_assoc_id, rinfo.rcv_sid, rinfo.rcv_ssn);
fflush(stdout);
write(fileno(stdout), iov->iov_base, nr);
fflush(stdout);
printf("\n");
/* The buffer may be realloc(), so get the new size. */
buflen = iov->iov_len;
/*
* Echo the message back using the incoming info.
*
* Note that rcv_sid is in host byte order. But rcv_ppid is
* what is stored by the peer. If both sides wnat to use this
* value for communication (interpreting it on both sides),
* the sender needs to do htonl() when setting snd_ppid. And
* the receiver side needs to do ntohl() to convert rcv_ppid
* back to the host byte order.
*/
sinfo.snd_sid = rinfo.rcv_sid;
sinfo.snd_ppid = rinfo.rcv_ppid;
sinfo.snd_assoc_id = rinfo.rcv_assoc_id;
iov->iov_len = nr;
if (sctp_sendv(fd, iov, 1, NULL, 0, &sinfo, sizeof (sinfo),
SCTP_SENDV_SNDINFO, 0) < 0) {
fprintf(stderr, "sctp_sendv: %s\n", strerror(errno));
exit(1);
}
/* Restore the original buffer size. */
iov->iov_len = buflen;
}
free(iov->iov_base);
close(fd);
}
static void
subscribe_event(int fd, uint16_t event)
{
struct sctp_event ev;
int ret;
ev.se_assoc_id = 0;
ev.se_type = event;
ev.se_on = 1;
ret = setsockopt(fd, IPPROTO_SCTP, SCTP_EVENT, &ev, sizeof (ev));
if (ret < 0) {
fprintf(stderr, "%s: setsockopt SCTP_EVENT: %d\n",
strerror(errno), event);
exit(1);
}
}
/* List of events we are interested in. */
static uint16_t event_interested[] = {
SCTP_ASSOC_CHANGE,
SCTP_SEND_FAILED_EVENT,
SCTP_PEER_ADDR_CHANGE,
SCTP_REMOTE_ERROR,
SCTP_SHUTDOWN_EVENT
};
int
main(void)
{
int fd;
int onoff;
int i;
struct sockaddr_in sin[1];
struct sctp_initmsg initmsg;
if ((fd = socket(AF_INET, SOCK_SEQPACKET, IPPROTO_SCTP)) == -1) {
perror("socket");
exit(1);
}
sin->sin_family = AF_INET;
sin->sin_port = htons(SERVER_PORT);
sin->sin_addr.s_addr = INADDR_ANY;
if (bind(fd, (struct sockaddr *)sin, sizeof (*sin)) == -1) {
perror("bind");
exit(1);
}
/* Enable accepting assocation set up request. */
if (listen(fd, 5) == -1) {
perror("listen");
exit(1);
}
(void) memset(&initmsg, 0, sizeof (struct sctp_initmsg));
initmsg.sinit_num_ostreams = MAX_STREAM;
initmsg.sinit_max_instreams = MAX_STREAM;
initmsg.sinit_max_attempts = MAX_STREAM;
if (setsockopt(fd, IPPROTO_SCTP, SCTP_INITMSG, &initmsg,
sizeof (struct sctp_initmsg)) < 0) {
perror("SCTP_INITMSG");
exit(1);
}
/* Subscribe to events. */
for (i = 0; i < sizeof (event_interested) / sizeof (uint16_t); i++)
subscribe_event(fd, event_interested[i]);
/* Enable receiving SCTP_RCVINFO on every recv. */
onoff = 1;
if (setsockopt(fd, IPPROTO_SCTP, SCTP_RECVRCVINFO, &onoff,
sizeof(onoff)) < 0) {
perror("SCTP_RECVRCVINFO");
exit(1);
}
/* Start the echo server. */
echo (fd);
return (0);
}