Home | History | Annotate | Download | only in lib
      1 /* SCTP kernel Implementation: User API extensions.
      2  *
      3  * connectx.c
      4  *
      5  * Distributed under the terms of the LGPL v2.1 as described in
      6  * http://www.gnu.org/copyleft/lesser.txt.
      7  *
      8  * This file is part of the user library that offers support for the
      9  * SCTP kernel Implementation. The main purpose of this
     10  * code is to provide the SCTP Socket API mappings for user
     11  * application to interface with the SCTP in kernel.
     12  *
     13  * This implementation is based on the Socket API Extensions for SCTP
     14  * defined in <draft-ietf-tsvwg-sctpsocket-10.txt.
     15  *
     16  * (C) Copyright IBM Corp. 2001, 2005
     17  *
     18  * Written or modified by:
     19  *   Frank Filz     <ffilz (at) us.ibm.com>
     20  */
     21 
     22 #include <sys/socket.h>   /* struct sockaddr_storage, setsockopt() */
     23 #include <netinet/in.h>
     24 #include <netinet/sctp.h> /* SCTP_SOCKOPT_CONNECTX_* */
     25 #include <errno.h>
     26 #include <stdlib.h>
     27 #include <string.h>
     28 #include <fcntl.h>
     29 
     30 /* Support the sctp_connectx() interface.
     31  *
     32  * See Sockets API Extensions for SCTP. Section 8.1.
     33  *
     34  * Instead of implementing through a socket call in sys_socketcall(),
     35  * tunnel the request through setsockopt().
     36  */
     37 static int __connectx_addrsize(const struct sockaddr *addrs,
     38 				const int addrcnt)
     39 {
     40 	const void *addrbuf;
     41 	const struct sockaddr *sa_addr;
     42 	int addrs_size = 0;
     43 	int i;
     44 
     45 	addrbuf = addrs;
     46 	for (i = 0; i < addrcnt; i++) {
     47 		sa_addr = (const struct sockaddr *)addrbuf;
     48 		switch (sa_addr->sa_family) {
     49 		case AF_INET:
     50 			addrs_size += sizeof(struct sockaddr_in);
     51 			addrbuf += sizeof(struct sockaddr_in);
     52 			break;
     53 		case AF_INET6:
     54 			addrs_size += sizeof(struct sockaddr_in6);
     55 			addrbuf += sizeof(struct sockaddr_in6);
     56 			break;
     57 		default:
     58 			errno = EINVAL;
     59 			return -1;
     60 		}
     61 	}
     62 
     63 	return addrs_size;
     64 }
     65 
     66 
     67 int __sctp_connectx(int fd, struct sockaddr *addrs, int addrcnt)
     68 {
     69 	int addrs_size = __connectx_addrsize(addrs, addrcnt);
     70 
     71 	if (addrs_size < 0)
     72 		return addrs_size;
     73 
     74 	return setsockopt(fd, SOL_SCTP, SCTP_SOCKOPT_CONNECTX_OLD, addrs,
     75 			    addrs_size);
     76 }
     77 
     78 extern int sctp_connectx_orig (int)
     79 	__attribute ((alias ("__sctp_connectx")));
     80 
     81 
     82 static int __connectx(int fd, struct sockaddr *addrs, socklen_t addrs_size,
     83 			sctp_assoc_t *id)
     84 {
     85 	int status;
     86 
     87 	if (id)
     88 		*id = 0;
     89 
     90 	status = setsockopt(fd, SOL_SCTP, SCTP_SOCKOPT_CONNECTX, addrs,
     91 			    addrs_size);
     92 
     93 	/* Normalize status and set association id */
     94 	if (status > 0) {
     95 		if (id)
     96 			*id = status;
     97 		return 0;
     98 	}
     99 
    100 	/* The error is something other then "Option not supported" */
    101 	if (status < 0 && errno != ENOPROTOOPT)
    102 		return status;
    103 
    104 	/* At this point, if the application wanted the id, we can't
    105 	 * really provide it, so we can return ENOPROTOOPT.
    106 	 */
    107 	if (id) {
    108 		errno = ENOPROTOOPT;
    109 		return -1;
    110 	}
    111 
    112 	/* Finally, try the old API */
    113 	return setsockopt(fd, SOL_SCTP, SCTP_SOCKOPT_CONNECTX_OLD,
    114 			  addrs, addrs_size);
    115 }
    116 
    117 int sctp_connectx2(int fd, struct sockaddr *addrs, int addrcnt,
    118 		      sctp_assoc_t *id)
    119 {
    120 	int addrs_size = __connectx_addrsize(addrs, addrcnt);
    121 
    122 	if (addrs_size < 0)
    123 		return addrs_size;
    124 
    125 	return __connectx(fd, addrs, addrs_size, id);
    126 }
    127 
    128 int sctp_connectx3(int fd, struct sockaddr *addrs, int addrcnt,
    129 		      sctp_assoc_t *id)
    130 {
    131 	int addrs_size = __connectx_addrsize(addrs, addrcnt);
    132 	int status;
    133 	struct sctp_getaddrs_old param;
    134 	socklen_t opt_len = sizeof(param);
    135 
    136 	if (addrs_size < 0)
    137 		return addrs_size;
    138 
    139 	/* First try the new socket api
    140 	 * Because the id is returned in the option buffer we have prepend
    141 	 * 32bit to it for the returned association id
    142 	 */
    143 	param.assoc_id = 0;
    144 	param.addr_num = addrs_size;
    145 	param.addrs = addrs;
    146 	status = getsockopt(fd, SOL_SCTP, SCTP_SOCKOPT_CONNECTX3,
    147 		            &param, &opt_len);
    148 	if (status == 0 || errno == EINPROGRESS) {
    149 		/* Succeeded immediately, or initiated on non-blocking
    150 		 * socket.
    151 		 */
    152 		if (id)
    153 			*id = param.assoc_id;
    154 	}
    155 
    156 	if (errno != ENOPROTOOPT) {
    157 		/* No point in trying the fallbacks*/
    158 		return status;
    159 	}
    160 
    161 	/* The first incarnation of updated connectx api didn't work for
    162 	 * non-blocking sockets.  So if the application wants the association
    163 	 * id and the socket is non-blocking, we can't really do anything.
    164 	 */
    165 	if (id) {
    166 		/* Program wants the association-id returned. We can only do
    167 		 * that if the socket is blocking */
    168 		status = fcntl(fd, F_GETFL);
    169 		if (status < 0)
    170 			return status;
    171 
    172 		if (status & O_NONBLOCK) {
    173 			/* Socket is non-blocking. Fail */
    174 			errno = ENOPROTOOPT;
    175 			return -1;
    176 		}
    177 	}
    178 
    179 	return __connectx(fd, addrs, addrs_size, id);
    180 }
    181 
    182 #define __SYMPFX(pfx, sym) #pfx sym
    183 #define _SYMPFX(pfx, sym) __SYMPFX(pfx, sym)
    184 #define SYMPFX(sym) _SYMPFX(__USER_LABEL_PREFIX__, #sym)
    185 #define SYMVER(name, name2) __asm__(".symver " SYMPFX(name) "," SYMPFX(name2))
    186 
    187 SYMVER(__sctp_connectx, sctp_connectx@);
    188 SYMVER(sctp_connectx_orig, sctp_connectx@VERS_1);
    189 SYMVER(sctp_connectx2, sctp_connectx@VERS_2);
    190 SYMVER(sctp_connectx3, sctp_connectx@@VERS_3);
    191