• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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@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  */
__connectx_addrsize(const struct sockaddr * addrs,const int addrcnt)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 
__sctp_connectx(int fd,struct sockaddr * addrs,int addrcnt)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 
__connectx(int fd,struct sockaddr * addrs,socklen_t addrs_size,sctp_assoc_t * id)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 
sctp_connectx2(int fd,struct sockaddr * addrs,int addrcnt,sctp_assoc_t * id)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 
sctp_connectx3(int fd,struct sockaddr * addrs,int addrcnt,sctp_assoc_t * id)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