blob: 5f4552bd011be8e5e02a47b5db0860b2904aea76 [file] [log] [blame]
Austin Schuh8d0a2852019-12-28 22:54:28 -08001/* 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 */
37static int __connectx_addrsize(const struct sockaddr *addrs,
38 const int addrcnt)
39{
40 const char *addrbuf;
41 const struct sockaddr *sa_addr;
42 int addrs_size = 0;
43 int i;
44
45 addrbuf = (char *)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
67int __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
78extern int sctp_connectx_orig (int)
79 __attribute ((alias ("__sctp_connectx")));
80
81
82static 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
117int 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
128int 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
187SYMVER(__sctp_connectx, sctp_connectx@);
188SYMVER(sctp_connectx_orig, sctp_connectx@VERS_1);
189SYMVER(sctp_connectx2, sctp_connectx@VERS_2);
190SYMVER(sctp_connectx3, sctp_connectx@@VERS_3);