Squashed 'third_party/lksctp-tools/' content from commit 200eca7f1

Change-Id: I8f7575513f114b205178cac5c6b3706f3d725cb5
git-subtree-dir: third_party/lksctp-tools
git-subtree-split: 200eca7f1419b1ae53958b51e8551f7e7f6cd467
diff --git a/src/lib/connectx.c b/src/lib/connectx.c
new file mode 100644
index 0000000..5f4552b
--- /dev/null
+++ b/src/lib/connectx.c
@@ -0,0 +1,190 @@
+/* SCTP kernel Implementation: User API extensions.
+ *
+ * connectx.c
+ *
+ * Distributed under the terms of the LGPL v2.1 as described in
+ * http://www.gnu.org/copyleft/lesser.txt.
+ *
+ * This file is part of the user library that offers support for the
+ * SCTP kernel Implementation. The main purpose of this
+ * code is to provide the SCTP Socket API mappings for user
+ * application to interface with the SCTP in kernel.
+ *
+ * This implementation is based on the Socket API Extensions for SCTP
+ * defined in <draft-ietf-tsvwg-sctpsocket-10.txt.
+ *
+ * (C) Copyright IBM Corp. 2001, 2005
+ *
+ * Written or modified by:
+ *   Frank Filz     <ffilz@us.ibm.com>
+ */
+
+#include <sys/socket.h>   /* struct sockaddr_storage, setsockopt() */
+#include <netinet/in.h>
+#include <netinet/sctp.h> /* SCTP_SOCKOPT_CONNECTX_* */
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+
+/* Support the sctp_connectx() interface.
+ *
+ * See Sockets API Extensions for SCTP. Section 8.1.
+ *
+ * Instead of implementing through a socket call in sys_socketcall(),
+ * tunnel the request through setsockopt().
+ */
+static int __connectx_addrsize(const struct sockaddr *addrs,
+				const int addrcnt)
+{
+	const char *addrbuf;
+	const struct sockaddr *sa_addr;
+	int addrs_size = 0;
+	int i;
+
+	addrbuf = (char *)addrs;
+	for (i = 0; i < addrcnt; i++) {
+		sa_addr = (const struct sockaddr *)addrbuf;
+		switch (sa_addr->sa_family) {
+		case AF_INET:
+			addrs_size += sizeof(struct sockaddr_in);
+			addrbuf += sizeof(struct sockaddr_in);
+			break;
+		case AF_INET6:
+			addrs_size += sizeof(struct sockaddr_in6);
+			addrbuf += sizeof(struct sockaddr_in6);
+			break;
+		default:
+			errno = EINVAL;
+			return -1;
+		}
+	}
+
+	return addrs_size;
+}
+			
+
+int __sctp_connectx(int fd, struct sockaddr *addrs, int addrcnt)
+{
+	int addrs_size = __connectx_addrsize(addrs, addrcnt);
+
+	if (addrs_size < 0)
+		return addrs_size;
+
+	return setsockopt(fd, SOL_SCTP, SCTP_SOCKOPT_CONNECTX_OLD, addrs,
+			    addrs_size);
+}
+
+extern int sctp_connectx_orig (int)
+	__attribute ((alias ("__sctp_connectx")));
+
+
+static int __connectx(int fd, struct sockaddr *addrs, socklen_t addrs_size,
+			sctp_assoc_t *id)
+{
+	int status;
+
+	if (id)
+		*id = 0;
+
+	status = setsockopt(fd, SOL_SCTP, SCTP_SOCKOPT_CONNECTX, addrs,
+			    addrs_size);
+
+	/* Normalize status and set association id */
+	if (status > 0) {
+		if (id)
+			*id = status;
+		return 0;
+	}
+
+	/* The error is something other then "Option not supported" */
+	if (status < 0 && errno != ENOPROTOOPT)
+		return status;
+
+	/* At this point, if the application wanted the id, we can't
+	 * really provide it, so we can return ENOPROTOOPT.
+	 */
+	if (id) {
+		errno = ENOPROTOOPT;
+		return -1;
+	}
+
+	/* Finally, try the old API */
+	return setsockopt(fd, SOL_SCTP, SCTP_SOCKOPT_CONNECTX_OLD,
+			  addrs, addrs_size);
+}
+
+int sctp_connectx2(int fd, struct sockaddr *addrs, int addrcnt,
+		      sctp_assoc_t *id)
+{
+	int addrs_size = __connectx_addrsize(addrs, addrcnt);
+
+	if (addrs_size < 0)
+		return addrs_size;
+
+	return __connectx(fd, addrs, addrs_size, id);
+}
+
+int sctp_connectx3(int fd, struct sockaddr *addrs, int addrcnt,
+		      sctp_assoc_t *id)
+{
+	int addrs_size = __connectx_addrsize(addrs, addrcnt);
+	int status;
+	struct sctp_getaddrs_old param;
+	socklen_t opt_len = sizeof(param);
+
+	if (addrs_size < 0)
+		return addrs_size;
+
+	/* First try the new socket api
+	 * Because the id is returned in the option buffer we have prepend
+	 * 32bit to it for the returned association id
+	 */
+	param.assoc_id = 0;
+	param.addr_num = addrs_size;
+	param.addrs = addrs;
+	status = getsockopt(fd, SOL_SCTP, SCTP_SOCKOPT_CONNECTX3,
+		            &param, &opt_len);
+	if (status == 0 || errno == EINPROGRESS) {
+		/* Succeeded immediately, or initiated on non-blocking
+		 * socket.
+		 */
+		if (id)
+			*id = param.assoc_id;
+	}
+
+	if (errno != ENOPROTOOPT) {
+		/* No point in trying the fallbacks*/
+		return status;
+	}
+
+	/* The first incarnation of updated connectx api didn't work for
+	 * non-blocking sockets.  So if the application wants the association
+	 * id and the socket is non-blocking, we can't really do anything.
+	 */
+	if (id) {
+		/* Program wants the association-id returned. We can only do
+		 * that if the socket is blocking */
+		status = fcntl(fd, F_GETFL);
+		if (status < 0)
+			return status;
+
+		if (status & O_NONBLOCK) {
+			/* Socket is non-blocking. Fail */
+			errno = ENOPROTOOPT;
+			return -1;
+		}
+	}
+
+	return __connectx(fd, addrs, addrs_size, id);
+}
+
+#define __SYMPFX(pfx, sym) #pfx sym
+#define _SYMPFX(pfx, sym) __SYMPFX(pfx, sym)
+#define SYMPFX(sym) _SYMPFX(__USER_LABEL_PREFIX__, #sym)
+#define SYMVER(name, name2) __asm__(".symver " SYMPFX(name) "," SYMPFX(name2))
+
+SYMVER(__sctp_connectx, sctp_connectx@);
+SYMVER(sctp_connectx_orig, sctp_connectx@VERS_1);
+SYMVER(sctp_connectx2, sctp_connectx@VERS_2);
+SYMVER(sctp_connectx3, sctp_connectx@@VERS_3);