/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" #include #include #include #include #include #include #include #include #include #include #include #include #include #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 /* * 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 lcl_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 lcl_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"}, { 1, 16, 2, "sctp_deferred_acks_max"}, }; /* END CSTYLED */ /* 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; sctp_stack_t *sctps = Q_TO_CONN(q)->conn_netstack->netstack_sctp; for (i = 0; i < sctps->sctps_g_num_epriv_ports; i++) { if (sctps->sctps_g_epriv_ports[i] != 0) (void) mi_mpprintf(mp, "%d ", sctps->sctps_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; sctp_stack_t *sctps = Q_TO_CONN(q)->conn_netstack->netstack_sctp; /* * 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(&sctps->sctps_epriv_port_lock); /* Check if the value is already in the list */ for (i = 0; i < sctps->sctps_g_num_epriv_ports; i++) { if (new_value == sctps->sctps_g_epriv_ports[i]) { mutex_exit(&sctps->sctps_epriv_port_lock); return (EEXIST); } } /* Find an empty slot */ for (i = 0; i < sctps->sctps_g_num_epriv_ports; i++) { if (sctps->sctps_g_epriv_ports[i] == 0) break; } if (i == sctps->sctps_g_num_epriv_ports) { mutex_exit(&sctps->sctps_epriv_port_lock); return (EOVERFLOW); } /* Set the new value */ sctps->sctps_g_epriv_ports[i] = (uint16_t)new_value; mutex_exit(&sctps->sctps_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; sctp_stack_t *sctps = Q_TO_CONN(q)->conn_netstack->netstack_sctp; /* * 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(&sctps->sctps_epriv_port_lock); /* Check that the value is already in the list */ for (i = 0; i < sctps->sctps_g_num_epriv_ports; i++) { if (sctps->sctps_g_epriv_ports[i] == new_value) break; } if (i == sctps->sctps_g_num_epriv_ports) { mutex_exit(&sctps->sctps_epriv_port_lock); return (ESRCH); } /* Clear the value */ sctps->sctps_g_epriv_ports[i] = 0; mutex_exit(&sctps->sctps_epriv_port_lock); return (0); } /* * Walk through the param array specified registering each element with the * named dispatch handler. */ boolean_t sctp_param_register(IDP *ndp, sctpparam_t *sctppa, int cnt, sctp_stack_t *sctps) { if (*ndp != NULL) { return (B_TRUE); } for (; cnt-- > 0; sctppa++) { if (sctppa->sctp_param_name && sctppa->sctp_param_name[0]) { if (!nd_load(ndp, sctppa->sctp_param_name, sctp_param_get, sctp_param_set, (caddr_t)sctppa)) { nd_free(ndp); return (B_FALSE); } } } sctps->sctps_wroff_xtra_param = kmem_zalloc(sizeof (sctpparam_t), KM_SLEEP); bcopy(&lcl_sctp_wroff_xtra_param, sctps->sctps_wroff_xtra_param, sizeof (sctpparam_t)); if (!nd_load(ndp, sctps->sctps_wroff_xtra_param->sctp_param_name, sctp_param_get, sctp_wroff_xtra_set, (caddr_t)sctps->sctps_wroff_xtra_param)) { nd_free(ndp); return (B_FALSE); } if (!nd_load(ndp, "sctp_extra_priv_ports", sctp_extra_priv_ports_get, NULL, NULL)) { nd_free(ndp); return (B_FALSE); } if (!nd_load(ndp, "sctp_extra_priv_ports_add", NULL, sctp_extra_priv_ports_add, NULL)) { nd_free(ndp); return (B_FALSE); } if (!nd_load(ndp, "sctp_extra_priv_ports_del", NULL, sctp_extra_priv_ports_del, NULL)) { nd_free(ndp); return (B_FALSE); } return (B_TRUE); } boolean_t sctp_nd_init(sctp_stack_t *sctps) { sctpparam_t *pa; pa = kmem_alloc(sizeof (lcl_sctp_param_arr), KM_SLEEP); bcopy(lcl_sctp_param_arr, pa, sizeof (lcl_sctp_param_arr)); sctps->sctps_params = pa; return (sctp_param_register(&sctps->sctps_g_nd, pa, A_CNT(lcl_sctp_param_arr), sctps)); } int sctp_nd_getset(queue_t *q, MBLKP mp) { sctp_stack_t *sctps = Q_TO_CONN(q)->conn_netstack->netstack_sctp; return (nd_getset(q, sctps->sctps_g_nd, mp)); } void sctp_nd_free(sctp_stack_t *sctps) { nd_free(&sctps->sctps_g_nd); kmem_free(sctps->sctps_params, sizeof (lcl_sctp_param_arr)); sctps->sctps_params = NULL; kmem_free(sctps->sctps_wroff_xtra_param, sizeof (sctpparam_t)); sctps->sctps_wroff_xtra_param = NULL; }