sctp_param.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
* or http://www.opensolaris.org/os/licensing.
* 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"
#include <sys/stream.h>
#include <sys/socket.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <netinet/in.h>
#include <netinet/ip6.h>
#include <inet/common.h>
#include <inet/ip.h>
#include <inet/ip6.h>
#include <inet/mi.h>
#include <inet/mib2.h>
#include <inet/nd.h>
#include "sctp_impl.h"
#include "sctp_addr.h"
#define MS 1L
#define SECONDS (1000 * MS)
#define MINUTES (60 * SECONDS)
#define HOURS (60 * MINUTES)
#define DAYS (24 * HOURS)
#define PARAM_MAX (~(uint32_t)0)
/* Max size IP datagram is 64k - 1 */
#define SCTP_MSS_MAX_IPV4 (IP_MAXPACKET - (sizeof (ipha_t) + \
sizeof (sctp_hdr_t)))
#define SCTP_MSS_MAX_IPV6 (IP_MAXPACKET - (sizeof (ip6_t) + \
sizeof (sctp_hdr_t)))
/* Max of the above */
#define SCTP_MSS_MAX SCTP_MSS_MAX_IPV4
/* Largest SCTP port number */
#define SCTP_MAX_PORT (64 * 1024 - 1)
/*
* Extra privileged ports. In host byte order.
* Protected by sctp_epriv_port_lock.
*/
#define SCTP_NUM_EPRIV_PORTS 64
int sctp_g_num_epriv_ports = SCTP_NUM_EPRIV_PORTS;
uint16_t sctp_g_epriv_ports[SCTP_NUM_EPRIV_PORTS] = { 2049, 4045 };
kmutex_t sctp_epriv_port_lock;
/*
* sctp_wroff_xtra is the extra space in front of SCTP/IP header for link
* layer header. It has to be a multiple of 4.
* Also there has to be enough space to stash in information passed between
* IP and SCTP.
*/
sctpparam_t sctp_wroff_xtra_param = { sizeof (conn_t *) + sizeof (ire_t *),
256, 32, "sctp_wroff_xtra" };
/*
* All of these are alterable, within the min/max values given, at run time.
* Note that the default value of "sctp_time_wait_interval" is four minutes,
* per the SCTP spec.
*/
/* BEGIN CSTYLED */
sctpparam_t sctp_param_arr[] = {
/*min max value name */
{ 0, 128, 8, "sctp_max_init_retr"},
{ 1, 128, 10, "sctp_pa_max_retr"},
{ 1, 128, 5, "sctp_pp_max_retr" },
{ 128, (1<<30), 1024*1024, "sctp_cwnd_max" },
{ 0, 10, 0, "sctp_debug" },
{ 1024, (32*1024), 1024, "sctp_smallest_nonpriv_port"},
{ 1, 255, 64, "sctp_ipv4_ttl"},
{ 0, 1*DAYS, 30*SECONDS, "sctp_heartbeat_interval"},
{ 68, 65535, 1500, "sctp_initial_mtu" },
{ 0, 1*DAYS, 10*MINUTES, "sctp_mtu_probe_interval"},
{ 0, 1*DAYS, 2*MINUTES, "sctp_new_secret_interval"},
{ 10*MS, 1*MINUTES, 100*MS, "sctp_deferred_ack_interval" },
{ 0, 16, 0, "sctp_snd_lowat_fraction" },
{ 0, 1, 0, "sctp_ignore_path_mtu" },
{ 1024, PARAM_MAX, SCTP_RECV_HIWATER,"sctp_initial_ssthresh" },
{ 1024, SCTP_MAX_PORT, 32*1024, "sctp_smallest_anon_port"},
{ 1024, SCTP_MAX_PORT, SCTP_MAX_PORT, "sctp_largest_anon_port"},
{ SCTP_XMIT_LOWATER, (1<<30), SCTP_XMIT_HIWATER,"sctp_xmit_hiwat"},
{ SCTP_XMIT_LOWATER, (1<<30), SCTP_XMIT_LOWATER,"sctp_xmit_lowat"},
{ SCTP_RECV_LOWATER, (1<<30), SCTP_RECV_HIWATER,"sctp_recv_hiwat"},
{ 8192, (1<<30), 1024*1024, "sctp_max_buf"},
{ 0, 65536, 20, "sctp_rtt_updates"},
{ 0, IPV6_MAX_HOPS, IPV6_DEFAULT_HOPS, "sctp_ipv6_hoplimit"},
{ 500*MS, 60*SECONDS, 1*SECONDS, "sctp_rto_min"},
{ 1*SECONDS, 60000*SECONDS, 60*SECONDS, "sctp_rto_max"},
{ 1*SECONDS, 60000*SECONDS, 3*SECONDS, "sctp_rto_initial"},
{ 10*MS, 60000*SECONDS, 60*SECONDS, "sctp_cookie_life"},
{ 1, UINT16_MAX, 32, "sctp_max_in_streams"},
{ 1, UINT16_MAX, 32, "sctp_initial_out_streams"},
{ 0, 300*SECONDS, 60*SECONDS, "sctp_shutack_wait_bound" },
{ 2, 8, 4, "sctp_maxburst" },
{ 0, 1, 0, "sctp_addip_enabled" },
{ 1, 65536, 4, "sctp_recv_hiwat_minmss" },
{ 1, 16, 4, "sctp_slow_start_initial"},
{ 1, 16384, 4, "sctp_slow_start_after_idle"},
{ 0, 1, 1, "sctp_prsctp_enabled"},
{ 1, 10000, 3, "sctp_fast_rxt_thresh"},
};
/* END CSTYLED */
/* Only modified during _init and _fini thus no locking is needed. */
static caddr_t sctp_g_nd; /* Head of 'named dispatch' variable list */
/* Get callback routine passed to nd_load by sctp_param_register */
/* ARGSUSED */
static int
sctp_param_get(queue_t *q, mblk_t *mp, caddr_t cp, cred_t *cr)
{
sctpparam_t *sctppa = (sctpparam_t *)cp;
(void) mi_mpprintf(mp, "%u", sctppa->sctp_param_val);
return (0);
}
/* Set callback routine passed to nd_load by sctp_param_register */
/* ARGSUSED */
static int
sctp_param_set(queue_t *q, mblk_t *mp, char *value, caddr_t cp, cred_t *cr)
{
long new_value;
sctpparam_t *sctppa = (sctpparam_t *)cp;
if (ddi_strtol(value, NULL, 10, &new_value) != 0 ||
new_value < sctppa->sctp_param_min ||
new_value > sctppa->sctp_param_max) {
return (EINVAL);
}
sctppa->sctp_param_val = new_value;
return (0);
}
/* ndd set routine for sctp_wroff_xtra. */
/* ARGSUSED */
static int
sctp_wroff_xtra_set(queue_t *q, mblk_t *mp, char *value, caddr_t cp,
cred_t *cr)
{
long new_value;
sctpparam_t *sctppa = (sctpparam_t *)cp;
if (ddi_strtol(value, NULL, 10, &new_value) != 0 ||
new_value < sctppa->sctp_param_min ||
new_value > sctppa->sctp_param_max) {
return (EINVAL);
}
/*
* Need to make sure new_value is a multiple of 8. If it is not,
* round it up.
*/
if (new_value & 0x7) {
new_value = (new_value & ~0x7) + 0x8;
}
sctppa->sctp_param_val = new_value;
return (0);
}
/*
* Note: No locks are held when inspecting sctp_g_*epriv_ports
* but instead the code relies on:
* - the fact that the address of the array and its size never changes
* - the atomic assignment of the elements of the array
*/
/* ARGSUSED */
static int
sctp_extra_priv_ports_get(queue_t *q, mblk_t *mp, caddr_t cp, cred_t *cr)
{
int i;
for (i = 0; i < sctp_g_num_epriv_ports; i++) {
if (sctp_g_epriv_ports[i] != 0)
(void) mi_mpprintf(mp, "%d ", sctp_g_epriv_ports[i]);
}
return (0);
}
/*
* Hold a lock while changing sctp_g_epriv_ports to prevent multiple
* threads from changing it at the same time.
*/
/* ARGSUSED */
static int
sctp_extra_priv_ports_add(queue_t *q, mblk_t *mp, char *value, caddr_t cp,
cred_t *cr)
{
long new_value;
int i;
/*
* Fail the request if the new value does not lie within the
* port number limits.
*/
if (ddi_strtol(value, NULL, 10, &new_value) != 0 ||
new_value <= 0 || new_value >= 65536) {
return (EINVAL);
}
mutex_enter(&sctp_epriv_port_lock);
/* Check if the value is already in the list */
for (i = 0; i < sctp_g_num_epriv_ports; i++) {
if (new_value == sctp_g_epriv_ports[i]) {
mutex_exit(&sctp_epriv_port_lock);
return (EEXIST);
}
}
/* Find an empty slot */
for (i = 0; i < sctp_g_num_epriv_ports; i++) {
if (sctp_g_epriv_ports[i] == 0)
break;
}
if (i == sctp_g_num_epriv_ports) {
mutex_exit(&sctp_epriv_port_lock);
return (EOVERFLOW);
}
/* Set the new value */
sctp_g_epriv_ports[i] = (uint16_t)new_value;
mutex_exit(&sctp_epriv_port_lock);
return (0);
}
/*
* Hold a lock while changing sctp_g_epriv_ports to prevent multiple
* threads from changing it at the same time.
*/
/* ARGSUSED */
static int
sctp_extra_priv_ports_del(queue_t *q, mblk_t *mp, char *value, caddr_t cp,
cred_t *cr)
{
long new_value;
int i;
/*
* Fail the request if the new value does not lie within the
* port number limits.
*/
if (ddi_strtol(value, NULL, 10, &new_value) != 0 ||
new_value <= 0 || new_value >= 65536) {
return (EINVAL);
}
mutex_enter(&sctp_epriv_port_lock);
/* Check that the value is already in the list */
for (i = 0; i < sctp_g_num_epriv_ports; i++) {
if (sctp_g_epriv_ports[i] == new_value)
break;
}
if (i == sctp_g_num_epriv_ports) {
mutex_exit(&sctp_epriv_port_lock);
return (ESRCH);
}
/* Clear the value */
sctp_g_epriv_ports[i] = 0;
mutex_exit(&sctp_epriv_port_lock);
return (0);
}
/*
* Walk through the param array specified registering each element with the
* named dispatch handler.
*/
boolean_t
sctp_param_register(sctpparam_t *sctppa, int cnt)
{
if (sctp_g_nd != NULL) {
return (B_TRUE);
}
for (; cnt-- > 0; sctppa++) {
if (sctppa->sctp_param_name && sctppa->sctp_param_name[0]) {
if (!nd_load(&sctp_g_nd, sctppa->sctp_param_name,
sctp_param_get, sctp_param_set,
(caddr_t)sctppa)) {
nd_free(&sctp_g_nd);
return (B_FALSE);
}
}
}
if (!nd_load(&sctp_g_nd, sctp_wroff_xtra_param.sctp_param_name,
sctp_param_get, sctp_wroff_xtra_set,
(caddr_t)&sctp_wroff_xtra_param)) {
nd_free(&sctp_g_nd);
return (B_FALSE);
}
if (!nd_load(&sctp_g_nd, "sctp_extra_priv_ports",
sctp_extra_priv_ports_get, NULL, NULL)) {
nd_free(&sctp_g_nd);
return (B_FALSE);
}
if (!nd_load(&sctp_g_nd, "sctp_extra_priv_ports_add",
NULL, sctp_extra_priv_ports_add, NULL)) {
nd_free(&sctp_g_nd);
return (B_FALSE);
}
if (!nd_load(&sctp_g_nd, "sctp_extra_priv_ports_del",
NULL, sctp_extra_priv_ports_del, NULL)) {
nd_free(&sctp_g_nd);
return (B_FALSE);
}
return (B_TRUE);
}
boolean_t
sctp_nd_init()
{
return (sctp_param_register(sctp_param_arr, A_CNT(sctp_param_arr)));
}
/* Accessors to keep the static sctp_g_nd local */
int
sctp_nd_getset(queue_t *q, MBLKP mp)
{
return (nd_getset(q, sctp_g_nd, mp));
}
void
sctp_nd_free()
{
nd_free(&sctp_g_nd);
}