Oracle® Solaris 11.2 Programming Interfaces Guide

Exit Print View

Updated: July 2014
 
 

Code Examples of SCTP Use

This section details two uses of SCTP sockets.

Example 7-17  SCTP Echo Client
/*
 * 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  SCTP Echo Server
/*
 * 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);
	}
}