blob: 1b64f2de0a206fb9ae441dc25c94cb8ce82848e0 [file] [log] [blame]
/* SCTP kernel reference Implementation: User API extensions.
*
* addrs.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 reference 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. 2003
* Copyright (c) 2001-2002 Intel Corp.
*
* Written or modified by:
* Ardelle Fan <ardelle.fan@intel.com>
* Sridhar Samudrala <sri@us.ibm.com>
*/
#include <string.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <netinet/sctp.h>
#include <errno.h>
/*
* Get local/peer addresses using the old API
* Old kernels (2.6.13 and earlier) only support this API but it breaks 32-bit
* programs on 64-bit kernels.
*/
static int
sctp_getaddrs_old(int sd, sctp_assoc_t id, int optname_num, int optname_old,
struct sockaddr **addrs)
{
socklen_t len = sizeof(sctp_assoc_t);
int cnt, err;
struct sctp_getaddrs_old getaddrs;
cnt = getsockopt(sd, SOL_SCTP, optname_num, &id, &len);
if (cnt < 0)
return -1;
if (0 == cnt) {
*addrs = NULL;
return 0;
}
len = cnt * sizeof(struct sockaddr_in6);
getaddrs.assoc_id = id;
getaddrs.addr_num = cnt;
getaddrs.addrs = (struct sockaddr *)malloc(len);
if (NULL == getaddrs.addrs)
return -1;
len = sizeof(getaddrs);
err = getsockopt(sd, SOL_SCTP, optname_old, &getaddrs, &len);
if (err < 0) {
free(getaddrs.addrs);
return -1;
}
*addrs = getaddrs.addrs;
return getaddrs.addr_num;
} /* sctp_getaddrs_old() */
/*
* Common getsockopt() layer
* If the NEW getsockopt() API fails this function will fall back to using
* the old API
*/
static int
sctp_getaddrs(int sd, sctp_assoc_t id,
int optname_new, int optname_num_old, int optname_old,
struct sockaddr **addrs)
{
int cnt, err;
socklen_t len;
size_t bufsize = 4096; /*enough for most cases*/
struct sctp_getaddrs *getaddrs = (struct sctp_getaddrs*)malloc(bufsize);
if (!getaddrs)
return -1;
for (;;) {
char *new_buf;
len = bufsize;
getaddrs->assoc_id = id;
err = getsockopt(sd, SOL_SCTP, optname_new, getaddrs, &len);
if (err == 0) {
/*got it*/
break;
}
if (errno == ENOPROTOOPT) {
/*Kernel does not support new API*/
free(getaddrs);
return sctp_getaddrs_old(sd, id,
optname_num_old, optname_old,
addrs);
}
if (errno != ENOMEM) {
/*unknown error*/
return -1;
}
/*expand buffer*/
if (bufsize > 128*1024) {
/*this is getting ridiculous*/
free(getaddrs);
errno = ENOBUFS;
return -1;
}
new_buf = realloc(getaddrs, bufsize+4096);
if (!new_buf) {
free(getaddrs);
return -1;
}
bufsize += 4096;
getaddrs = (struct sctp_getaddrs*)new_buf;
}
/* we skip traversing the list, allocating a new buffer etc. and enjoy
* a simple hack*/
cnt = getaddrs->addr_num;
memmove(getaddrs, getaddrs + 1, len - sizeof(struct sctp_getaddrs));
*addrs = (struct sockaddr*)getaddrs;
return cnt;
} /* sctp_getaddrs() */
/* Get all peer address on a socket. This is a new SCTP API
* described in the section 8.3 of the Sockets API Extensions for SCTP.
* This is implemented using the getsockopt() interface.
*/
int
sctp_getpaddrs(int sd, sctp_assoc_t id, struct sockaddr **addrs)
{
return sctp_getaddrs(sd, id,
SCTP_GET_PEER_ADDRS,
SCTP_GET_PEER_ADDRS_NUM_OLD,
SCTP_GET_PEER_ADDRS_OLD,
addrs);
} /* sctp_getpaddrs() */
/* Frees all resources allocated by sctp_getpaddrs(). This is a new SCTP API
* described in the section 8.4 of the Sockets API Extensions for SCTP.
*/
int
sctp_freepaddrs(struct sockaddr *addrs)
{
free(addrs);
return 0;
} /* sctp_freepaddrs() */
/* Get all locally bound address on a socket. This is a new SCTP API
* described in the section 8.5 of the Sockets API Extensions for SCTP.
* This is implemented using the getsockopt() interface.
*/
int
sctp_getladdrs(int sd, sctp_assoc_t id, struct sockaddr **addrs)
{
return sctp_getaddrs(sd, id,
SCTP_GET_LOCAL_ADDRS,
SCTP_GET_LOCAL_ADDRS_NUM_OLD,
SCTP_GET_LOCAL_ADDRS_OLD,
addrs);
} /* sctp_getladdrs() */
/* Frees all resources allocated by sctp_getladdrs(). This is a new SCTP API
* described in the section 8.6 of the Sockets API Extensions for SCTP.
*/
int
sctp_freeladdrs(struct sockaddr *addrs)
{
free(addrs);
return 0;
} /* sctp_freeladdrs() */