sctp_opt_data.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#define _SUN_TPI_VERSION 2
#include <sys/xti_inet.h>
#include <inet/ipclassifier.h>
#include <inet/ipsec_impl.h>
#include <inet/sctp_itf.h>
#include "sctp_impl.h"
#include "sctp_asconf.h"
#include "sctp_addr.h"
static int sctp_getpeeraddrs(sctp_t *, void *, int *);
/*
* Set optbuf and optlen for the option.
* Allocate memory (if not already present).
* Otherwise just point optbuf and optlen at invalp and inlen.
* Returns failure if memory can not be allocated.
*/
static int
{
/* Unchanged length - no need to realocate */
return (0);
}
if (inlen != 0) {
/* Allocate new buffer before free */
return (ENOMEM);
} else {
}
/* Free old buffer */
if (*optlenp != 0)
return (0);
}
/*
* Use the outgoing IP header to create an IP_OPTIONS option the way
* it was passed down from the application.
*/
static int
{
int totallen;
len += IP_ADDR_LEN;
totallen <<= 2;
while (totallen != 0) {
case IPOPT_EOL:
goto done;
case IPOPT_NOP:
optlen = 1;
break;
default:
}
break;
switch (optval) {
int off;
case IPOPT_SSRR:
case IPOPT_LSRR:
/*
* Insert ipha_dst as the first entry in the source
* route and move down the entries on step.
* The last entry gets placed at buf1.
*/
if (off < 0) {
/* No entries in source route */
break;
}
/* Last entry in source route */
off -= IP_ADDR_LEN;
while (off > 0) {
off -= IP_ADDR_LEN;
}
/* ipha_dst into first slot */
break;
default:
break;
}
}
done:
/* Pad the resulting options */
while (len & 0x3) {
len++;
}
return (len);
}
/*
* Copy the standard header into its new location,
* lay in the new options and then update the relevant
* fields in both sctp_t and the standard header.
* NOTE: this could be simpler if we trusted bcopy()
*/
static int
{
char buf[SCTP_MAX_HDR_LENGTH];
if (checkonly) {
/*
* do not really set, just pretend to - T_CHECK
*/
if (len != 0) {
/*
* there is value supplied, validate it as if
* for a real set operation.
*/
return (EINVAL);
}
return (0);
}
return (EINVAL);
sctph_len = sizeof (sctp_hdr_t);
if (sctp->sctp_current) {
/*
* Could be setting options before setting up connection.
*/
}
return (0);
}
static int
{
struct sockaddr_in *sin;
struct sockaddr_in6 *sin6;
struct sctp_paddrinfo *sp;
int i;
if (!sctp->sctp_primary) {
goto noprim;
}
} else {
}
sstat->sstat_unackdata = 0;
sstat->sstat_penddata = 0;
sizeof (sctp_data_hdr_t);
/* count unack'd */
if (!SCTP_CHUNK_ISSENT(mp)) {
break;
}
if (!SCTP_CHUNK_ISACKED(mp)) {
sstat->sstat_unackdata++;
}
}
}
/*
* Count penddata chunks. We can only count chunks in SCTP (not
* data already delivered to socket layer).
*/
for (i = 0; i < sctp->sctp_num_istr; i++) {
sstat->sstat_penddata++;
}
}
}
}
}
/* Un-Ordered Frag list */
sstat->sstat_penddata++;
return (sizeof (*sstat));
}
/*
* SCTP_GET_PEER_ADDR_INFO
*/
static int
{
struct sockaddr_in *sin4;
struct sockaddr_in6 *sin6;
case AF_INET:
break;
case AF_INET6:
break;
default:
return (EAFNOSUPPORT);
}
return (EINVAL);
*optlen = sizeof (struct sctp_paddrinfo);
return (0);
}
/*
* SCTP_RTOINFO
*/
static int
{
return (sizeof (*srto));
}
static int
{
const struct sctp_rtoinfo *srto;
return (EINVAL);
}
/*
* Bounds checking. Priviledged user can set the RTO initial
* outside the ndd boundary.
*/
if (srto->srto_initial != 0 &&
return (EINVAL);
}
return (EINVAL);
}
return (EINVAL);
}
if (srto->srto_initial != 0) {
}
}
}
return (0);
}
/*
* SCTP_ASSOCINFO
*/
static int
{
uint16_t i;
/*
* Count the number of peer addresses
*/
i++;
}
return (sizeof (*sap));
}
static int
{
return (EINVAL);
}
if (sap->sasoc_asocmaxrxt) {
if (sctp->sctp_faddrs) {
/*
* Bounds check: as per rfc2960, assoc max retr cannot
* exceed the sum of all individual path max retr's.
*/
}
return (EINVAL);
}
}
/*
* Out of bounds.
*/
return (EINVAL);
}
}
if (sap->sasoc_cookie_life != 0 &&
return (EINVAL);
}
if (sap->sasoc_asocmaxrxt > 0) {
}
if (sap->sasoc_cookie_life > 0) {
}
return (0);
}
/*
* SCTP_INITMSG
*/
static int
{
return (sizeof (*si));
}
static int
{
return (EINVAL);
}
return (EINVAL);
}
if (si->sinit_num_ostreams != 0 &&
/*
* Out of bounds.
*/
return (EINVAL);
}
if (si->sinit_max_instreams != 0 &&
return (EINVAL);
}
if (si->sinit_max_attempts != 0 &&
return (EINVAL);
}
if (si->sinit_max_init_timeo != 0 &&
return (EINVAL);
}
if (si->sinit_num_ostreams != 0)
if (si->sinit_max_instreams != 0)
if (si->sinit_max_attempts != 0)
if (si->sinit_max_init_timeo != 0) {
}
return (0);
}
/*
* SCTP_PEER_ADDR_PARAMS
*/
static int
sctp_faddr_t **fpp)
{
struct sockaddr_in *sin;
struct sockaddr_in6 *sin6;
return (EAFNOSUPPORT);
}
} else {
return (EINVAL);
}
}
return (0);
}
static int
{
int retval;
if (retval) {
return (retval);
}
if (fp) {
} else {
}
return (sizeof (*spp));
}
static int
{
int retval;
return (EINVAL);
}
if (retval != 0) {
return (retval);
}
return (EINVAL);
}
if (spp->spp_pathmaxrxt &&
return (EINVAL);
}
} else {
}
}
return (EINVAL);
}
}
/*
* Send heartbeat immediatelly, don't modify the
* current setting.
*/
} else {
/*
* Restart the heartbeat timer using the new intrvl.
* We need to call sctp_heartbeat_timer() to set
* the earliest heartbeat expiry time.
*/
}
if (spp->spp_pathmaxrxt) {
}
} else {
/*
* Send heartbeat immediatelly, don't modify
* the current setting.
*/
} else {
}
if (spp->spp_pathmaxrxt) {
}
}
/* Restart the heartbeat timer using the new intrvl. */
}
if (spp->spp_pathmaxrxt) {
}
}
return (0);
}
/*
* SCTP_DEFAULT_SEND_PARAM
*/
static int
{
sinfo->sinfo_cumtsn = 0;
return (sizeof (*sinfo));
}
static int
{
return (EINVAL);
}
return (EINVAL);
}
return (0);
}
static int
{
int retval;
return (EINVAL);
}
if (retval)
return (retval);
return (EINVAL);
return (0);
/* Only switch current if fp is alive */
return (0);
}
/* Reset the addrs in the composite header */
return (0);
}
/* Handy on off switch for socket option processing. */
#define ONOFF(x) ((x) == 0 ? 0 : 1)
/*
* SCTP routine to get the values of options.
*/
int
{
int retval = 0;
/* In most cases, the return buffer is just an int */
switch (level) {
case SOL_SOCKET:
switch (name) {
case SO_LINGER: {
break;
}
case SO_DEBUG:
break;
case SO_DONTROUTE:
break;
case SO_USELOOPBACK:
break;
case SO_BROADCAST:
break;
case SO_REUSEADDR:
break;
case SO_DGRAM_ERRIND:
break;
case SO_SNDBUF:
break;
case SO_RCVBUF:
break;
default:
break;
}
break;
case IPPROTO_SCTP:
switch (name) {
case SCTP_RTOINFO:
if (buflen < sizeof (struct sctp_rtoinfo)) {
break;
}
break;
case SCTP_ASSOCINFO:
if (buflen < sizeof (struct sctp_assocparams)) {
break;
}
break;
case SCTP_INITMSG:
if (buflen < sizeof (struct sctp_initmsg)) {
break;
}
break;
case SCTP_NODELAY:
break;
case SCTP_AUTOCLOSE:
break;
case SCTP_ADAPTION_LAYER:
if (buflen < sizeof (struct sctp_setadaption)) {
break;
}
break;
case SCTP_PEER_ADDR_PARAMS:
if (buflen < sizeof (struct sctp_paddrparams)) {
break;
}
break;
case SCTP_DEFAULT_SEND_PARAM:
if (buflen < sizeof (struct sctp_sndrcvinfo)) {
break;
}
break;
case SCTP_EVENTS: {
struct sctp_event_subscribe *ev;
if (buflen < sizeof (struct sctp_event_subscribe)) {
break;
}
*optlen = sizeof (struct sctp_event_subscribe);
break;
}
case SCTP_STATUS:
if (buflen < sizeof (struct sctp_status)) {
break;
}
break;
case SCTP_GET_PEER_ADDR_INFO:
if (buflen < sizeof (struct sctp_paddrinfo)) {
break;
}
break;
case SCTP_GET_NLADDRS:
break;
case SCTP_GET_LADDRS: {
int addr_cnt;
int addr_size;
addr_size = sizeof (struct sockaddr_in);
else
addr_size = sizeof (struct sockaddr_in6);
if (retval == 0)
break;
}
case SCTP_GET_NPADDRS: {
int i;
;
break;
}
case SCTP_GET_PADDRS: {
int addr_cnt;
int addr_size;
addr_size = sizeof (struct sockaddr_in);
else
addr_size = sizeof (struct sockaddr_in6);
if (retval == 0)
break;
}
case SCTP_PRSCTP:
break;
case SCTP_MAXSEG:
case SCTP_DISABLE_FRAGMENTS:
/* Not yet supported. */
default:
break;
}
break;
case IPPROTO_IP:
break;
}
switch (name) {
case IP_OPTIONS:
case T_IP_OPTIONS: {
/*
* This is compatible with BSD in that in only return
* the reverse source route with the final destination
* as the last entry. The first 4 bytes of the option
* will contain the final destination. Allocate a
* buffer large enough to hold all the options, we
* add IP_ADDR_LEN to SCTP_MAX_IP_OPTIONS_LENGTH since
* sctp_opt_get_user() adds the final destination
* at the start.
*/
char *opt_ptr;
int opt_len;
/* Caller ensures enough space */
if (opt_len > 0) {
/*
* TODO: Do we have to handle getsockopt on an
* initiator as well?
*/
obuf);
} else {
opt_len = 0;
}
/* Silently truncate */
}
break;
}
case IP_TOS:
case T_IP_TOS:
break;
case IP_TTL:
break;
default:
break;
}
break;
case IPPROTO_IPV6:
break;
}
switch (name) {
case IPV6_UNICAST_HOPS:
break; /* goto sizeof (int) option return */
case IPV6_RECVPKTINFO:
if (sctp->sctp_ipv6_recvancillary &
*i1 = 1;
} else {
*i1 = 0;
}
break; /* goto sizeof (int) option return */
case IPV6_RECVHOPLIMIT:
if (sctp->sctp_ipv6_recvancillary &
*i1 = 1;
} else {
*i1 = 0;
}
break; /* goto sizeof (int) option return */
case IPV6_RECVHOPOPTS:
if (sctp->sctp_ipv6_recvancillary &
*i1 = 1;
} else {
*i1 = 0;
}
break; /* goto sizeof (int) option return */
case IPV6_RECVDSTOPTS:
if (sctp->sctp_ipv6_recvancillary &
*i1 = 1;
} else {
*i1 = 0;
}
break; /* goto sizeof (int) option return */
case IPV6_RECVRTHDR:
if (sctp->sctp_ipv6_recvancillary &
*i1 = 1;
} else {
*i1 = 0;
}
break; /* goto sizeof (int) option return */
case IPV6_RECVRTHDRDSTOPTS:
if (sctp->sctp_ipv6_recvancillary &
*i1 = 1;
} else {
*i1 = 0;
}
break; /* goto sizeof (int) option return */
case IPV6_PKTINFO: {
struct in6_pktinfo *pkti;
if (buflen < sizeof (struct in6_pktinfo)) {
break;
}
else
pkti->ipi6_ifindex = 0;
else
*optlen = sizeof (struct in6_pktinfo);
break;
}
case IPV6_HOPLIMIT:
else
break; /* goto sizeof (int) option return */
case IPV6_NEXTHOP: {
break;
}
break;
*sin6 = sctp_sin6_null;
break;
}
case IPV6_HOPOPTS:
break;
break;
}
break;
case IPV6_RTHDRDSTOPTS:
break;
break;
}
break;
case IPV6_RTHDR:
break;
break;
}
break;
case IPV6_DSTOPTS:
break;
break;
}
break;
case IPV6_V6ONLY:
break;
default:
break;
}
break;
default:
break;
}
return (retval);
}
int
{
/* In all cases, the size of the option must be bigger than int */
}
retval = 0;
switch (level) {
case SOL_SOCKET:
break;
}
switch (name) {
case SO_LINGER: {
break;
}
} else {
sctp->sctp_linger = 0;
sctp->sctp_lingertime = 0;
}
break;
}
case SO_DEBUG:
break;
case SO_KEEPALIVE:
break;
case SO_DONTROUTE:
/*
* SO_DONTROUTE, SO_USELOOPBACK and SO_BROADCAST are
* only of interest to IP. We track them here only so
* that we can report their current value.
*/
break;
case SO_USELOOPBACK:
break;
case SO_BROADCAST:
break;
case SO_REUSEADDR:
break;
case SO_DGRAM_ERRIND:
break;
case SO_SNDBUF:
if (*i1 > sctp_max_buf) {
break;
}
if (*i1 < 0) {
break;
}
if (sctp_snd_lowat_fraction != 0)
break;
case SO_RCVBUF:
if (*i1 > sctp_max_buf) {
break;
}
/* Silently ignore zero */
if (*i1 != 0) {
/*
* Insist on a receive window that is at least
* sctp_recv_hiwat_minmss * MSS (default 4*MSS)
* to avoid funny interactions of Nagle
* algorithm, SWS avoidance and delayed
* acknowledgement.
*/
sizeof (sctp_data_hdr_t)));
}
/*
* XXX should we return the rwnd here
* and sctp_opt_get ?
*/
break;
default:
break;
}
break;
case IPPROTO_SCTP:
break;
}
switch (name) {
case SCTP_RTOINFO:
break;
case SCTP_ASSOCINFO:
break;
case SCTP_INITMSG:
break;
case SCTP_NODELAY:
break;
case SCTP_AUTOCLOSE:
if (SEC_TO_TICK(*i1) < 0) {
break;
}
/* Convert the number of seconds to ticks. */
break;
break;
case SCTP_PRIMARY_ADDR:
break;
case SCTP_ADAPTION_LAYER: {
struct sctp_setadaption *ssb;
if (inlen < sizeof (struct sctp_setadaption)) {
break;
}
break;
}
case SCTP_PEER_ADDR_PARAMS:
inlen);
break;
case SCTP_DEFAULT_SEND_PARAM:
break;
case SCTP_EVENTS: {
struct sctp_event_subscribe *ev;
if (inlen < sizeof (struct sctp_event_subscribe)) {
break;
}
break;
}
case SCTP_ADD_ADDR:
case SCTP_REM_ADDR:
/*
* The sctp_t has to be bound first before
* the address list can be changed.
*/
break;
}
} else {
}
if (name == SCTP_ADD_ADDR) {
B_TRUE);
} else {
B_TRUE);
}
break;
case SCTP_UC_SWAP: {
struct sctp_uc_swap *us;
/*
* Change handle & upcalls.
*/
break;
}
sizeof (sctp_upcalls_t));
break;
}
case SCTP_PRSCTP:
break;
case SCTP_MAXSEG:
case SCTP_DISABLE_FRAGMENTS:
/* Not yet supported. */
default:
break;
}
break;
case IPPROTO_IP:
break;
}
break;
}
switch (name) {
case IP_OPTIONS:
case T_IP_OPTIONS:
break;
case IP_TOS:
case T_IP_TOS:
break;
case IP_TTL:
break;
case IP_SEC_OPT:
/*
* We should not allow policy setting after
* we start listening for connections.
*/
} else {
}
break;
/* IP level options */
case IP_RECVIF:
break;
case IP_RECVSLLA:
break;
case IP_UNSPEC_SRC:
break;
default:
break;
}
break;
case IPPROTO_IPV6: {
break;
}
switch (name) {
case IPV6_UNICAST_HOPS:
break;
}
break;
}
if (*i1 == -1)
else
break;
case IPV6_UNSPEC_SRC:
break;
}
break;
case IPV6_RECVPKTINFO:
break;
}
if (onoff)
else
/* Send it with the next msg */
sctp->sctp_recvifindex = 0;
break;
case IPV6_RECVHOPLIMIT:
break;
}
if (onoff)
else
break;
case IPV6_RECVHOPOPTS:
break;
}
if (onoff)
else
break;
case IPV6_RECVDSTOPTS:
break;
}
if (onoff)
else
break;
case IPV6_RECVRTHDR:
break;
}
if (onoff)
else
break;
case IPV6_RECVRTHDRDSTOPTS:
break;
}
if (onoff)
else
break;
case IPV6_PKTINFO:
if (inlen != 0 &&
inlen != sizeof (struct in6_pktinfo)) {
break;
}
if (inlen == 0) {
} else {
struct in6_pktinfo *pkti;
/* XXX Need to check if the index exists */
if (ipp->ipp_ifindex != 0)
else
else
}
break;
case IPV6_HOPLIMIT:
break;
}
if (inlen == 0) {
} else {
break;
}
if (*i1 == -1)
else
}
break;
case IPV6_NEXTHOP: {
struct sockaddr_in6 *sin6;
break;
}
if (inlen == 0) {
} else {
break;
}
break;
}
} else {
break;
}
}
}
break;
}
case IPV6_HOPOPTS: {
if (inlen != 0 &&
break;
}
if (inlen == 0) {
} else {
&ipp->ipp_hopoptslen);
if (retval != 0)
break;
}
break;
}
case IPV6_RTHDRDSTOPTS: {
if (inlen != 0 &&
break;
}
if (inlen == 0) {
} else {
&ipp->ipp_rtdstoptslen);
if (retval != 0)
break;
}
break;
}
case IPV6_DSTOPTS: {
if (inlen != 0 &&
break;
}
if (inlen == 0) {
} else {
&ipp->ipp_dstoptslen);
if (retval != 0)
break;
}
break;
}
case IPV6_RTHDR: {
if (inlen != 0 &&
break;
}
if (inlen == 0) {
} else {
&ipp->ipp_rthdrlen);
if (retval != 0)
break;
}
break;
}
case IPV6_SEC_OPT:
/*
* We should not allow policy setting after
* we start listening for connections.
*/
} else {
}
break;
case IPV6_V6ONLY:
/*
* After the bound state, setting the v6only option
* is too late.
*/
} else {
}
break;
default:
break;
}
break;
}
default:
break;
}
return (retval);
}
/*
* SCTP exported kernel interface for geting the first source address of
* a sctp_t. The parameter addr is assumed to have enough space to hold
* one socket address.
*/
int
{
int err = 0;
int addrcnt = 1;
switch (sctp->sctp_family) {
case AF_INET:
} else {
if (err != 0) {
*addrlen = 0;
break;
}
}
*addrlen = sizeof (struct sockaddr_in);
break;
case AF_INET6:
} else {
if (err != 0) {
*addrlen = 0;
break;
}
}
*addrlen = sizeof (struct sockaddr_in6);
sin6->sin6_scope_id = 0;
sin6->__sin6_src_id = 0;
break;
}
return (err);
}
/*
* SCTP exported kernel interface for geting the primary peer address of
* a sctp_t. The parameter addr is assumed to have enough space to hold
* one socket address.
*/
int
{
int err = 0;
int addrcnt = 1;
switch (sctp->sctp_family) {
case AF_INET:
if (err != 0) {
*addrlen = 0;
break;
}
*addrlen = sizeof (struct sockaddr_in);
break;
case AF_INET6:
if (err != 0) {
*addrlen = 0;
break;
}
*addrlen = sizeof (struct sockaddr_in6);
sin6->sin6_flowinfo = 0;
sin6->sin6_scope_id = 0;
sin6->__sin6_src_id = 0;
break;
}
return (err);
}
/*
* Return a list of IP addresses of the peer endpoint of this sctp_t.
* The parameter paddrs is supposed to be either (struct sockaddr_in *) or
* (struct sockaddr_in6 *) depending on the address family of the sctp_t.
*/
int
{
int family;
struct sockaddr_in *sin4;
struct sockaddr_in6 *sin6;
int max;
int cnt;
return (ENOTCONN);
/* If we want only one, give the primary */
if (max == 1) {
switch (family) {
case AF_INET:
break;
case AF_INET6:
break;
}
return (0);
}
switch (family) {
case AF_INET:
break;
case AF_INET6:
break;
}
}
return (0);
}