SCTP rwnd issues [1/2]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hello!

A sample program that demonstrates a situation when rwnd drops to 0 because
of memory pressure and never grows to its original value...

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>
#include <sys/socket.h>
#include <unistd.h>
#include <error.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <netinet/sctp.h>
#include <arpa/inet.h>
#include <sys/select.h>
#include <errno.h>

int open_sockets(int *rcsock, int *trsock, int *rcbufsize,
                 struct sockaddr_in *saddr)
{
	int optval, optlen;
	int ret;
	struct sctp_event_subscribe events;

	*rcsock = socket(PF_INET, SOCK_SEQPACKET, IPPROTO_SCTP);
	if (*rcsock == -1)
		return errno;

	*trsock = socket(PF_INET, SOCK_SEQPACKET, IPPROTO_SCTP);
	if (*trsock == -1)
		return errno;

	optlen = sizeof(optval);
	ret = getsockopt(*rcsock, SOL_SOCKET, SO_RCVBUF, &optval, &optlen);
	if (ret == -1)
		return errno;

	printf("Receive socket buffer size: %d\n", optval);
	*rcbufsize = optval;

	memset(&events, 0, sizeof(events));
	events.sctp_data_io_event = 1;
	ret = setsockopt(*rcsock, IPPROTO_SCTP, SCTP_EVENTS, &events,
	                 sizeof(events));
	if (ret == -1)
		return errno;

	optval = 1;
	ret = setsockopt(*trsock, IPPROTO_SCTP, SCTP_NODELAY, &optval,
	                 sizeof(optval));
	if (ret == -1)
		return errno;

	saddr->sin_family = AF_INET;
	ret = inet_pton(AF_INET, "127.0.0.1", &saddr->sin_addr);
	if (ret != 1)
		return EFAULT;
	saddr->sin_port = htons(54321);

	ret = bind(*rcsock, (struct sockaddr *)saddr, sizeof(*saddr));
	if (ret == -1)
		return errno;

	ret = listen(*rcsock, 1);
	if (ret == -1)
		return errno;

	printf("Server is listening on 127.0.0.1:%d...\n",
	       ntohs(saddr->sin_port));
	return 0;
}

/* Fill receive buffer until memory pressure closes rwnd */
int fill_rxbuffer(int trsock, int rcsock, struct sockaddr_in *saddr,
                  sctp_assoc_t *rcassoc, void *buf, int bufsize)
{
	int ret;
	int optlen;
	struct sockaddr_in caddr;
	int rflags;
	struct sctp_sndrcvinfo sinfo;
	struct sctp_assocparams args;
	int total = 0;
	int reported = 0;

	while (1) {
		ret = sctp_sendmsg(trsock, buf, bufsize,
		                   (struct sockaddr *)saddr,
		                   sizeof(struct sockaddr_in), 0, 0, 0, 0, 0);
		if (ret == -1)
			return errno;

		if (*rcassoc == 0) {
			optlen = sizeof(caddr);
			rflags = 0;
			ret = sctp_recvmsg(rcsock, buf, bufsize,
			                   (struct sockaddr *)&caddr, &optlen,
			                   &sinfo, &rflags);
			if (ret == -1)
				return errno;
			if (!(rflags & MSG_NOTIFICATION)) {
				*rcassoc = sinfo.sinfo_assoc_id;
				printf("Receiver association=%d\n", *rcassoc);
				continue;
			}
		}

		optlen = sizeof(args);
		ret = sctp_opt_info(rcsock, *rcassoc, SCTP_ASSOCINFO, &args,
		                    &optlen);
		if (ret == -1)
			return errno;

		total += bufsize;
		if ((total >= reported * 2) || !args.sasoc_local_rwnd) {
			reported = total;
			printf("Sent %d bytes ... rwnd=%d\n", total,
			       args.sasoc_local_rwnd);
		}

		if (!args.sasoc_local_rwnd)
			break;
	}

	return 0;
}

/* Clean the receive buffer */
int free_rxbuffer(int rcsock, sctp_assoc_t rcassoc, void *buf, int bufsize)
{
	int ret;
	int total = 0;
	int reported = 0;
	int rflags;
	struct sctp_sndrcvinfo sinfo;
	int optlen;
	struct sctp_assocparams args;
	int err;

	while (1) {
		rflags = MSG_DONTWAIT;
		ret = sctp_recvmsg(rcsock, buf, bufsize, NULL, NULL, &sinfo,
		                   &rflags);
		if (ret == -1) {
			if ((errno == EWOULDBLOCK) || (errno == EAGAIN))
				ret = 0;
			else
				return errno;
		}

		if (rflags & MSG_NOTIFICATION)
			continue;

		optlen = sizeof(args);
		err = sctp_opt_info(rcsock, rcassoc, SCTP_ASSOCINFO, &args,
		                    &optlen);
		if (err == -1)
			return errno;

		total += ret;
		if ((total >= reported * 2) || !ret) {
			reported = total;
			printf("Received %d bytes ... rwnd=%d\n", total,
			       args.sasoc_local_rwnd);
		}

		if (!ret)
			break;
	}

	return 0;
}

/* Transmit some packets, give rwnd a chance to restore */
int give_chance(int trsock, int rcsock, struct sockaddr_in *saddr, void *buf,
                int bufsize, int rcbufsize)
{
	int total = rcbufsize;
	struct sctp_sndrcvinfo sinfo;
	int ret;
	int rflags;

	printf("Transferring %d packets...\n", total);

	while (total--) {
		ret = sctp_sendmsg(trsock, buf, bufsize,
		                   (struct sockaddr *)saddr,
		                   sizeof(struct sockaddr_in), 0, 0, 0, 0, 0);

		rflags = 0;
		if (ret != -1)
			ret = sctp_recvmsg(rcsock, buf, bufsize, NULL, NULL,
			                   &sinfo, &rflags);

		if (ret == -1) {
			if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
				printf("We are LOCKED\n");
			else
				printf("Some unexpected error...\n");
			return errno;
		}
	}

	return 0;
}

int main(int argc, char **argv)
{
	int rcsock, trsock;
	int ret;
	int optlen;
	struct sockaddr_in saddr;
	struct sctp_assocparams args;
	sctp_assoc_t rcassoc = 0;
	char buf[1];
	int rcbufsize;

	ret = open_sockets(&rcsock, &trsock, &rcbufsize, &saddr);
	if (ret)
		exit(ret);

	ret = fill_rxbuffer(trsock, rcsock, &saddr, &rcassoc, &buf, sizeof(buf));
	if (ret)
		exit(ret);

	ret = free_rxbuffer(rcsock, rcassoc, &buf, sizeof(buf));
	if (ret)
		exit(ret);

	ret = give_chance(trsock, rcsock, &saddr, &buf, sizeof(buf), rcbufsize);
	if (ret)
		exit(ret);

	optlen = sizeof(args);
	ret = sctp_opt_info(rcsock, rcassoc, SCTP_ASSOCINFO, &args, &optlen);
	if (ret == -1)
		exit(errno);

	printf("rwnd=%d\n", args.sasoc_local_rwnd);

	printf("Finished.\n");

	close(trsock);
	close(rcsock);
}

-- 
Best regards,
Alexander Sverdlin.
--
To unsubscribe from this list: send the line "unsubscribe linux-sctp" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Linux Networking Development]     [Linux OMAP]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux