You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
191 lines
4.7 KiB
191 lines
4.7 KiB
/* SCTP kernel Implementation: User API extensions.
|
|
*
|
|
* connectx.c
|
|
*
|
|
* Distributed under the terms of the LGPL v2.1 as described in
|
|
* http://www.gnu.org/copyleft/lesser.txt.
|
|
*
|
|
* This file is part of the user library that offers support for the
|
|
* SCTP kernel Implementation. The main purpose of this
|
|
* code is to provide the SCTP Socket API mappings for user
|
|
* application to interface with the SCTP in kernel.
|
|
*
|
|
* This implementation is based on the Socket API Extensions for SCTP
|
|
* defined in <draft-ietf-tsvwg-sctpsocket-10.txt.
|
|
*
|
|
* (C) Copyright IBM Corp. 2001, 2005
|
|
*
|
|
* Written or modified by:
|
|
* Frank Filz <ffilz@us.ibm.com>
|
|
*/
|
|
|
|
#include <sys/socket.h> /* struct sockaddr_storage, setsockopt() */
|
|
#include <netinet/in.h>
|
|
#include <netinet/sctp.h> /* SCTP_SOCKOPT_CONNECTX_* */
|
|
#include <errno.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <fcntl.h>
|
|
|
|
/* Support the sctp_connectx() interface.
|
|
*
|
|
* See Sockets API Extensions for SCTP. Section 8.1.
|
|
*
|
|
* Instead of implementing through a socket call in sys_socketcall(),
|
|
* tunnel the request through setsockopt().
|
|
*/
|
|
static int __connectx_addrsize(const struct sockaddr *addrs,
|
|
const int addrcnt)
|
|
{
|
|
const void *addrbuf;
|
|
const struct sockaddr *sa_addr;
|
|
int addrs_size = 0;
|
|
int i;
|
|
|
|
addrbuf = addrs;
|
|
for (i = 0; i < addrcnt; i++) {
|
|
sa_addr = (const struct sockaddr *)addrbuf;
|
|
switch (sa_addr->sa_family) {
|
|
case AF_INET:
|
|
addrs_size += sizeof(struct sockaddr_in);
|
|
addrbuf += sizeof(struct sockaddr_in);
|
|
break;
|
|
case AF_INET6:
|
|
addrs_size += sizeof(struct sockaddr_in6);
|
|
addrbuf += sizeof(struct sockaddr_in6);
|
|
break;
|
|
default:
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return addrs_size;
|
|
}
|
|
|
|
|
|
int __sctp_connectx(int fd, struct sockaddr *addrs, int addrcnt)
|
|
{
|
|
int addrs_size = __connectx_addrsize(addrs, addrcnt);
|
|
|
|
if (addrs_size < 0)
|
|
return addrs_size;
|
|
|
|
return setsockopt(fd, SOL_SCTP, SCTP_SOCKOPT_CONNECTX_OLD, addrs,
|
|
addrs_size);
|
|
}
|
|
|
|
extern int sctp_connectx_orig (int)
|
|
__attribute ((alias ("__sctp_connectx")));
|
|
|
|
|
|
static int __connectx(int fd, struct sockaddr *addrs, socklen_t addrs_size,
|
|
sctp_assoc_t *id)
|
|
{
|
|
int status;
|
|
|
|
if (id)
|
|
*id = 0;
|
|
|
|
status = setsockopt(fd, SOL_SCTP, SCTP_SOCKOPT_CONNECTX, addrs,
|
|
addrs_size);
|
|
|
|
/* Normalize status and set association id */
|
|
if (status > 0) {
|
|
if (id)
|
|
*id = status;
|
|
return 0;
|
|
}
|
|
|
|
/* The error is something other then "Option not supported" */
|
|
if (status < 0 && errno != ENOPROTOOPT)
|
|
return status;
|
|
|
|
/* At this point, if the application wanted the id, we can't
|
|
* really provide it, so we can return ENOPROTOOPT.
|
|
*/
|
|
if (id) {
|
|
errno = ENOPROTOOPT;
|
|
return -1;
|
|
}
|
|
|
|
/* Finally, try the old API */
|
|
return setsockopt(fd, SOL_SCTP, SCTP_SOCKOPT_CONNECTX_OLD,
|
|
addrs, addrs_size);
|
|
}
|
|
|
|
int sctp_connectx2(int fd, struct sockaddr *addrs, int addrcnt,
|
|
sctp_assoc_t *id)
|
|
{
|
|
int addrs_size = __connectx_addrsize(addrs, addrcnt);
|
|
|
|
if (addrs_size < 0)
|
|
return addrs_size;
|
|
|
|
return __connectx(fd, addrs, addrs_size, id);
|
|
}
|
|
|
|
int sctp_connectx3(int fd, struct sockaddr *addrs, int addrcnt,
|
|
sctp_assoc_t *id)
|
|
{
|
|
int addrs_size = __connectx_addrsize(addrs, addrcnt);
|
|
int status;
|
|
struct sctp_getaddrs_old param;
|
|
socklen_t opt_len = sizeof(param);
|
|
|
|
if (addrs_size < 0)
|
|
return addrs_size;
|
|
|
|
/* First try the new socket api
|
|
* Because the id is returned in the option buffer we have prepend
|
|
* 32bit to it for the returned association id
|
|
*/
|
|
param.assoc_id = 0;
|
|
param.addr_num = addrs_size;
|
|
param.addrs = addrs;
|
|
status = getsockopt(fd, SOL_SCTP, SCTP_SOCKOPT_CONNECTX3,
|
|
¶m, &opt_len);
|
|
if (status == 0 || errno == EINPROGRESS) {
|
|
/* Succeeded immediately, or initiated on non-blocking
|
|
* socket.
|
|
*/
|
|
if (id)
|
|
*id = param.assoc_id;
|
|
}
|
|
|
|
if (errno != ENOPROTOOPT) {
|
|
/* No point in trying the fallbacks*/
|
|
return status;
|
|
}
|
|
|
|
/* The first incarnation of updated connectx api didn't work for
|
|
* non-blocking sockets. So if the application wants the association
|
|
* id and the socket is non-blocking, we can't really do anything.
|
|
*/
|
|
if (id) {
|
|
/* Program wants the association-id returned. We can only do
|
|
* that if the socket is blocking */
|
|
status = fcntl(fd, F_GETFL);
|
|
if (status < 0)
|
|
return status;
|
|
|
|
if (status & O_NONBLOCK) {
|
|
/* Socket is non-blocking. Fail */
|
|
errno = ENOPROTOOPT;
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return __connectx(fd, addrs, addrs_size, id);
|
|
}
|
|
|
|
#define __SYMPFX(pfx, sym) #pfx sym
|
|
#define _SYMPFX(pfx, sym) __SYMPFX(pfx, sym)
|
|
#define SYMPFX(sym) _SYMPFX(__USER_LABEL_PREFIX__, #sym)
|
|
#define SYMVER(name, name2) __asm__(".symver " SYMPFX(name) "," SYMPFX(name2))
|
|
|
|
SYMVER(__sctp_connectx, sctp_connectx@);
|
|
SYMVER(sctp_connectx_orig, sctp_connectx@VERS_1);
|
|
SYMVER(sctp_connectx2, sctp_connectx@VERS_2);
|
|
SYMVER(sctp_connectx3, sctp_connectx@@VERS_3);
|