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 ¶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