16f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu/* SCTP kernel Implementation: User API extensions.
26f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu *
36f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu * connectx.c
46f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu *
56f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu * Distributed under the terms of the LGPL v2.1 as described in
66f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu * http://www.gnu.org/copyleft/lesser.txt.
76f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu *
86f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu * This file is part of the user library that offers support for the
96f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu * SCTP kernel Implementation. The main purpose of this
106f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu * code is to provide the SCTP Socket API mappings for user
116f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu * application to interface with the SCTP in kernel.
126f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu *
136f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu * This implementation is based on the Socket API Extensions for SCTP
146f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu * defined in <draft-ietf-tsvwg-sctpsocket-10.txt.
156f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu *
166f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu * (C) Copyright IBM Corp. 2001, 2005
176f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu *
186f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu * Written or modified by:
196f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu *   Frank Filz     <ffilz@us.ibm.com>
206f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu */
216f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu
226f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu#include <sys/socket.h>   /* struct sockaddr_storage, setsockopt() */
236f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu#include <netinet/in.h>
246f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu#include <netinet/sctp.h> /* SCTP_SOCKOPT_CONNECTX_* */
256f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu#include <errno.h>
266f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu#include <stdlib.h>
276f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu#include <string.h>
286f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu#include <fcntl.h>
296f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu
306f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu/* Support the sctp_connectx() interface.
316f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu *
326f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu * See Sockets API Extensions for SCTP. Section 8.1.
336f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu *
346f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu * Instead of implementing through a socket call in sys_socketcall(),
356f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu * tunnel the request through setsockopt().
366f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu */
376f22494d19b605ded308dc0fa713e91cb873f44aSimon Xustatic int __connectx_addrsize(const struct sockaddr *addrs,
386f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu				const int addrcnt)
396f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu{
406f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu	const void *addrbuf;
416f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu	const struct sockaddr *sa_addr;
426f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu	int addrs_size = 0;
436f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu	int i;
446f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu
456f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu	addrbuf = addrs;
466f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu	for (i = 0; i < addrcnt; i++) {
476f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu		sa_addr = (const struct sockaddr *)addrbuf;
486f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu		switch (sa_addr->sa_family) {
496f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu		case AF_INET:
506f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu			addrs_size += sizeof(struct sockaddr_in);
516f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu			addrbuf += sizeof(struct sockaddr_in);
526f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu			break;
536f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu		case AF_INET6:
546f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu			addrs_size += sizeof(struct sockaddr_in6);
556f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu			addrbuf += sizeof(struct sockaddr_in6);
566f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu			break;
576f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu		default:
586f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu			errno = EINVAL;
596f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu			return -1;
606f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu		}
616f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu	}
626f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu
636f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu	return addrs_size;
646f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu}
656f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu
666f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu
676f22494d19b605ded308dc0fa713e91cb873f44aSimon Xuint __sctp_connectx(int fd, struct sockaddr *addrs, int addrcnt)
686f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu{
699d048f81cb2c60d2f019ea3677f12928f8629cf1Zeng Linggang	int addrs_size = __connectx_addrsize(addrs, addrcnt);
706f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu
716f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu	if (addrs_size < 0)
726f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu		return addrs_size;
736f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu
746f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu	return setsockopt(fd, SOL_SCTP, SCTP_SOCKOPT_CONNECTX_OLD, addrs,
756f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu			    addrs_size);
766f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu}
776f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu
786f22494d19b605ded308dc0fa713e91cb873f44aSimon Xuextern int sctp_connectx_orig (int)
796f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu	__attribute ((alias ("__sctp_connectx")));
806f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu
816f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu
826f22494d19b605ded308dc0fa713e91cb873f44aSimon Xustatic int __connectx(int fd, struct sockaddr *addrs, socklen_t addrs_size,
836f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu			sctp_assoc_t *id)
846f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu{
856f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu	int status;
866f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu
876f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu	if (id)
886f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu		*id = 0;
896f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu
906f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu	status = setsockopt(fd, SOL_SCTP, SCTP_SOCKOPT_CONNECTX, addrs,
916f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu			    addrs_size);
926f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu
936f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu	/* Normalize status and set association id */
946f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu	if (status > 0) {
956f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu		if (id)
966f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu			*id = status;
976f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu		return 0;
986f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu	}
996f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu
1006f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu	/* The error is something other then "Option not supported" */
1016f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu	if (status < 0 && errno != ENOPROTOOPT)
1026f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu		return status;
1036f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu
1046f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu	/* At this point, if the application wanted the id, we can't
1056f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu	 * really provide it, so we can return ENOPROTOOPT.
1066f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu	 */
1076f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu	if (id) {
1086f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu		errno = ENOPROTOOPT;
1096f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu		return -1;
1106f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu	}
1116f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu
1126f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu	/* Finally, try the old API */
1136f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu	return setsockopt(fd, SOL_SCTP, SCTP_SOCKOPT_CONNECTX_OLD,
1146f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu			  addrs, addrs_size);
1156f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu}
1166f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu
1176f22494d19b605ded308dc0fa713e91cb873f44aSimon Xuint sctp_connectx2(int fd, struct sockaddr *addrs, int addrcnt,
1186f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu		      sctp_assoc_t *id)
1196f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu{
1209d048f81cb2c60d2f019ea3677f12928f8629cf1Zeng Linggang	int addrs_size = __connectx_addrsize(addrs, addrcnt);
1216f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu
1226f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu	if (addrs_size < 0)
1236f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu		return addrs_size;
1246f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu
1256f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu	return __connectx(fd, addrs, addrs_size, id);
1266f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu}
1276f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu
1286f22494d19b605ded308dc0fa713e91cb873f44aSimon Xuint sctp_connectx3(int fd, struct sockaddr *addrs, int addrcnt,
1296f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu		      sctp_assoc_t *id)
1306f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu{
1319d048f81cb2c60d2f019ea3677f12928f8629cf1Zeng Linggang	int addrs_size = __connectx_addrsize(addrs, addrcnt);
1326f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu	int status;
1336f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu	struct sctp_getaddrs_old param;
1346f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu	socklen_t opt_len = sizeof(param);
1356f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu
1366f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu	if (addrs_size < 0)
1376f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu		return addrs_size;
1386f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu
1396f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu	/* First try the new socket api
1406f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu	 * Because the id is returned in the option buffer we have prepend
1416f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu	 * 32bit to it for the returned association id
1426f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu	 */
1436f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu	param.assoc_id = 0;
1446f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu	param.addr_num = addrs_size;
1456f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu	param.addrs = addrs;
1466f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu	status = getsockopt(fd, SOL_SCTP, SCTP_SOCKOPT_CONNECTX3,
1476f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu		            &param, &opt_len);
1486f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu	if (status == 0 || errno == EINPROGRESS) {
1496f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu		/* Succeeded immediately, or initiated on non-blocking
1506f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu		 * socket.
1516f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu		 */
1526f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu		if (id)
1536f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu			*id = param.assoc_id;
1546f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu	}
1556f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu
1566f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu	if (errno != ENOPROTOOPT) {
1576f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu		/* No point in trying the fallbacks*/
1586f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu		return status;
1596f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu	}
1606f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu
1616f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu	/* The first incarnation of updated connectx api didn't work for
1626f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu	 * non-blocking sockets.  So if the application wants the association
1636f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu	 * id and the socket is non-blocking, we can't really do anything.
1646f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu	 */
1656f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu	if (id) {
1666f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu		/* Program wants the association-id returned. We can only do
1676f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu		 * that if the socket is blocking */
1686f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu		status = fcntl(fd, F_GETFL);
1696f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu		if (status < 0)
1706f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu			return status;
1716f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu
1726f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu		if (status & O_NONBLOCK) {
1736f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu			/* Socket is non-blocking. Fail */
1746f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu			errno = ENOPROTOOPT;
1756f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu			return -1;
1766f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu		}
1776f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu	}
1786f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu
1796f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu	return __connectx(fd, addrs, addrs_size, id);
1806f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu}
1816f22494d19b605ded308dc0fa713e91cb873f44aSimon Xu
182699a579e3d8a30bc4cec93616696848a8ae8b225Markos Chandras#define __SYMPFX(pfx, sym) #pfx sym
183699a579e3d8a30bc4cec93616696848a8ae8b225Markos Chandras#define _SYMPFX(pfx, sym) __SYMPFX(pfx, sym)
184699a579e3d8a30bc4cec93616696848a8ae8b225Markos Chandras#define SYMPFX(sym) _SYMPFX(__USER_LABEL_PREFIX__, #sym)
185699a579e3d8a30bc4cec93616696848a8ae8b225Markos Chandras#define SYMVER(name, name2) __asm__(".symver " SYMPFX(name) "," SYMPFX(name2))
186699a579e3d8a30bc4cec93616696848a8ae8b225Markos Chandras
187699a579e3d8a30bc4cec93616696848a8ae8b225Markos ChandrasSYMVER(__sctp_connectx, sctp_connectx@);
188699a579e3d8a30bc4cec93616696848a8ae8b225Markos ChandrasSYMVER(sctp_connectx_orig, sctp_connectx@VERS_1);
189699a579e3d8a30bc4cec93616696848a8ae8b225Markos ChandrasSYMVER(sctp_connectx2, sctp_connectx@VERS_2);
190699a579e3d8a30bc4cec93616696848a8ae8b225Markos ChandrasSYMVER(sctp_connectx3, sctp_connectx@@VERS_3);
191