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 ¶m, &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