blob: 79838e40da2bb321ee48afe2b67392fc4e313132 [file] [log] [blame]
/*-
* SPDX-License-Identifier: BSD-3-Clause
*
* Copyright (c) 2001-2008, by Cisco Systems, Inc. All rights reserved.
* Copyright (c) 2008-2012, by Randall Stewart. All rights reserved.
* Copyright (c) 2008-2012, by Michael Tuexen. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* a) Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* b) Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the distribution.
*
* c) Neither the name of Cisco Systems, Inc. nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#if defined(__FreeBSD__) && !defined(__Userspace__)
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#endif
#include <netinet/sctp_os.h>
#include <netinet/sctp_pcb.h>
#include <netinet/sctputil.h>
#include <netinet/sctp_var.h>
#include <netinet/sctp_sysctl.h>
#ifdef INET6
#if defined(__Userspace__) || defined(__FreeBSD__)
#include <netinet6/sctp6_var.h>
#endif
#endif
#include <netinet/sctp_header.h>
#include <netinet/sctp_output.h>
#include <netinet/sctp_uio.h>
#include <netinet/sctp_timer.h>
#include <netinet/sctp_indata.h>
#include <netinet/sctp_auth.h>
#include <netinet/sctp_asconf.h>
#include <netinet/sctp_bsd_addr.h>
#if defined(__Userspace__)
#include <netinet/sctp_constants.h>
#endif
#if defined(__FreeBSD__) && !defined(__Userspace__)
#include <netinet/sctp_kdtrace.h>
#if defined(INET6) || defined(INET)
#include <netinet/tcp_var.h>
#endif
#include <netinet/udp.h>
#include <netinet/udp_var.h>
#include <sys/proc.h>
#ifdef INET6
#include <netinet/icmp6.h>
#endif
#endif
#if defined(_WIN32) && !defined(__Userspace__)
#if !defined(SCTP_LOCAL_TRACE_BUF)
#include "eventrace_netinet.h"
#include "sctputil.tmh" /* this is the file that will be auto generated */
#endif
#else
#ifndef KTR_SCTP
#define KTR_SCTP KTR_SUBSYS
#endif
#endif
extern const struct sctp_cc_functions sctp_cc_functions[];
extern const struct sctp_ss_functions sctp_ss_functions[];
void
sctp_sblog(struct sockbuf *sb, struct sctp_tcb *stcb, int from, int incr)
{
#if defined(SCTP_LOCAL_TRACE_BUF)
struct sctp_cwnd_log sctp_clog;
sctp_clog.x.sb.stcb = stcb;
sctp_clog.x.sb.so_sbcc = sb->sb_cc;
if (stcb)
sctp_clog.x.sb.stcb_sbcc = stcb->asoc.sb_cc;
else
sctp_clog.x.sb.stcb_sbcc = 0;
sctp_clog.x.sb.incr = incr;
SCTP_CTR6(KTR_SCTP, "SCTP:%d[%d]:%x-%x-%x-%x",
SCTP_LOG_EVENT_SB,
from,
sctp_clog.x.misc.log1,
sctp_clog.x.misc.log2,
sctp_clog.x.misc.log3,
sctp_clog.x.misc.log4);
#endif
}
void
sctp_log_closing(struct sctp_inpcb *inp, struct sctp_tcb *stcb, int16_t loc)
{
#if defined(SCTP_LOCAL_TRACE_BUF)
struct sctp_cwnd_log sctp_clog;
sctp_clog.x.close.inp = (void *)inp;
sctp_clog.x.close.sctp_flags = inp->sctp_flags;
if (stcb) {
sctp_clog.x.close.stcb = (void *)stcb;
sctp_clog.x.close.state = (uint16_t)stcb->asoc.state;
} else {
sctp_clog.x.close.stcb = 0;
sctp_clog.x.close.state = 0;
}
sctp_clog.x.close.loc = loc;
SCTP_CTR6(KTR_SCTP, "SCTP:%d[%d]:%x-%x-%x-%x",
SCTP_LOG_EVENT_CLOSE,
0,
sctp_clog.x.misc.log1,
sctp_clog.x.misc.log2,
sctp_clog.x.misc.log3,
sctp_clog.x.misc.log4);
#endif
}
void
rto_logging(struct sctp_nets *net, int from)
{
#if defined(SCTP_LOCAL_TRACE_BUF)
struct sctp_cwnd_log sctp_clog;
memset(&sctp_clog, 0, sizeof(sctp_clog));
sctp_clog.x.rto.net = (void *) net;
sctp_clog.x.rto.rtt = net->rtt / 1000;
SCTP_CTR6(KTR_SCTP, "SCTP:%d[%d]:%x-%x-%x-%x",
SCTP_LOG_EVENT_RTT,
from,
sctp_clog.x.misc.log1,
sctp_clog.x.misc.log2,
sctp_clog.x.misc.log3,
sctp_clog.x.misc.log4);
#endif
}
void
sctp_log_strm_del_alt(struct sctp_tcb *stcb, uint32_t tsn, uint16_t sseq, uint16_t stream, int from)
{
#if defined(SCTP_LOCAL_TRACE_BUF)
struct sctp_cwnd_log sctp_clog;
sctp_clog.x.strlog.stcb = stcb;
sctp_clog.x.strlog.n_tsn = tsn;
sctp_clog.x.strlog.n_sseq = sseq;
sctp_clog.x.strlog.e_tsn = 0;
sctp_clog.x.strlog.e_sseq = 0;
sctp_clog.x.strlog.strm = stream;
SCTP_CTR6(KTR_SCTP, "SCTP:%d[%d]:%x-%x-%x-%x",
SCTP_LOG_EVENT_STRM,
from,
sctp_clog.x.misc.log1,
sctp_clog.x.misc.log2,
sctp_clog.x.misc.log3,
sctp_clog.x.misc.log4);
#endif
}
void
sctp_log_nagle_event(struct sctp_tcb *stcb, int action)
{
#if defined(SCTP_LOCAL_TRACE_BUF)
struct sctp_cwnd_log sctp_clog;
sctp_clog.x.nagle.stcb = (void *)stcb;
sctp_clog.x.nagle.total_flight = stcb->asoc.total_flight;
sctp_clog.x.nagle.total_in_queue = stcb->asoc.total_output_queue_size;
sctp_clog.x.nagle.count_in_queue = stcb->asoc.chunks_on_out_queue;
sctp_clog.x.nagle.count_in_flight = stcb->asoc.total_flight_count;
SCTP_CTR6(KTR_SCTP, "SCTP:%d[%d]:%x-%x-%x-%x",
SCTP_LOG_EVENT_NAGLE,
action,
sctp_clog.x.misc.log1,
sctp_clog.x.misc.log2,
sctp_clog.x.misc.log3,
sctp_clog.x.misc.log4);
#endif
}
void
sctp_log_sack(uint32_t old_cumack, uint32_t cumack, uint32_t tsn, uint16_t gaps, uint16_t dups, int from)
{
#if defined(SCTP_LOCAL_TRACE_BUF)
struct sctp_cwnd_log sctp_clog;
sctp_clog.x.sack.cumack = cumack;
sctp_clog.x.sack.oldcumack = old_cumack;
sctp_clog.x.sack.tsn = tsn;
sctp_clog.x.sack.numGaps = gaps;
sctp_clog.x.sack.numDups = dups;
SCTP_CTR6(KTR_SCTP, "SCTP:%d[%d]:%x-%x-%x-%x",
SCTP_LOG_EVENT_SACK,
from,
sctp_clog.x.misc.log1,
sctp_clog.x.misc.log2,
sctp_clog.x.misc.log3,
sctp_clog.x.misc.log4);
#endif
}
void
sctp_log_map(uint32_t map, uint32_t cum, uint32_t high, int from)
{
#if defined(SCTP_LOCAL_TRACE_BUF)
struct sctp_cwnd_log sctp_clog;
memset(&sctp_clog, 0, sizeof(sctp_clog));
sctp_clog.x.map.base = map;
sctp_clog.x.map.cum = cum;
sctp_clog.x.map.high = high;
SCTP_CTR6(KTR_SCTP, "SCTP:%d[%d]:%x-%x-%x-%x",
SCTP_LOG_EVENT_MAP,
from,
sctp_clog.x.misc.log1,
sctp_clog.x.misc.log2,
sctp_clog.x.misc.log3,
sctp_clog.x.misc.log4);
#endif
}
void
sctp_log_fr(uint32_t biggest_tsn, uint32_t biggest_new_tsn, uint32_t tsn, int from)
{
#if defined(SCTP_LOCAL_TRACE_BUF)
struct sctp_cwnd_log sctp_clog;
memset(&sctp_clog, 0, sizeof(sctp_clog));
sctp_clog.x.fr.largest_tsn = biggest_tsn;
sctp_clog.x.fr.largest_new_tsn = biggest_new_tsn;
sctp_clog.x.fr.tsn = tsn;
SCTP_CTR6(KTR_SCTP, "SCTP:%d[%d]:%x-%x-%x-%x",
SCTP_LOG_EVENT_FR,
from,
sctp_clog.x.misc.log1,
sctp_clog.x.misc.log2,
sctp_clog.x.misc.log3,
sctp_clog.x.misc.log4);
#endif
}
#ifdef SCTP_MBUF_LOGGING
void
sctp_log_mb(struct mbuf *m, int from)
{
#if defined(SCTP_LOCAL_TRACE_BUF)
struct sctp_cwnd_log sctp_clog;
sctp_clog.x.mb.mp = m;
sctp_clog.x.mb.mbuf_flags = (uint8_t)(SCTP_BUF_GET_FLAGS(m));
sctp_clog.x.mb.size = (uint16_t)(SCTP_BUF_LEN(m));
sctp_clog.x.mb.data = SCTP_BUF_AT(m, 0);
if (SCTP_BUF_IS_EXTENDED(m)) {
sctp_clog.x.mb.ext = SCTP_BUF_EXTEND_BASE(m);
#if defined(__APPLE__) && !defined(__Userspace__)
/* APPLE does not use a ref_cnt, but a forward/backward ref queue */
#else
sctp_clog.x.mb.refcnt = (uint8_t)(SCTP_BUF_EXTEND_REFCNT(m));
#endif
} else {
sctp_clog.x.mb.ext = 0;
sctp_clog.x.mb.refcnt = 0;
}
SCTP_CTR6(KTR_SCTP, "SCTP:%d[%d]:%x-%x-%x-%x",
SCTP_LOG_EVENT_MBUF,
from,
sctp_clog.x.misc.log1,
sctp_clog.x.misc.log2,
sctp_clog.x.misc.log3,
sctp_clog.x.misc.log4);
#endif
}
void
sctp_log_mbc(struct mbuf *m, int from)
{
struct mbuf *mat;
for (mat = m; mat; mat = SCTP_BUF_NEXT(mat)) {
sctp_log_mb(mat, from);
}
}
#endif
void
sctp_log_strm_del(struct sctp_queued_to_read *control, struct sctp_queued_to_read *poschk, int from)
{
#if defined(SCTP_LOCAL_TRACE_BUF)
struct sctp_cwnd_log sctp_clog;
if (control == NULL) {
SCTP_PRINTF("Gak log of NULL?\n");
return;
}
sctp_clog.x.strlog.stcb = control->stcb;
sctp_clog.x.strlog.n_tsn = control->sinfo_tsn;
sctp_clog.x.strlog.n_sseq = (uint16_t)control->mid;
sctp_clog.x.strlog.strm = control->sinfo_stream;
if (poschk != NULL) {
sctp_clog.x.strlog.e_tsn = poschk->sinfo_tsn;
sctp_clog.x.strlog.e_sseq = (uint16_t)poschk->mid;
} else {
sctp_clog.x.strlog.e_tsn = 0;
sctp_clog.x.strlog.e_sseq = 0;
}
SCTP_CTR6(KTR_SCTP, "SCTP:%d[%d]:%x-%x-%x-%x",
SCTP_LOG_EVENT_STRM,
from,
sctp_clog.x.misc.log1,
sctp_clog.x.misc.log2,
sctp_clog.x.misc.log3,
sctp_clog.x.misc.log4);
#endif
}
void
sctp_log_cwnd(struct sctp_tcb *stcb, struct sctp_nets *net, int augment, uint8_t from)
{
#if defined(SCTP_LOCAL_TRACE_BUF)
struct sctp_cwnd_log sctp_clog;
sctp_clog.x.cwnd.net = net;
if (stcb->asoc.send_queue_cnt > 255)
sctp_clog.x.cwnd.cnt_in_send = 255;
else
sctp_clog.x.cwnd.cnt_in_send = stcb->asoc.send_queue_cnt;
if (stcb->asoc.stream_queue_cnt > 255)
sctp_clog.x.cwnd.cnt_in_str = 255;
else
sctp_clog.x.cwnd.cnt_in_str = stcb->asoc.stream_queue_cnt;
if (net) {
sctp_clog.x.cwnd.cwnd_new_value = net->cwnd;
sctp_clog.x.cwnd.inflight = net->flight_size;
sctp_clog.x.cwnd.pseudo_cumack = net->pseudo_cumack;
sctp_clog.x.cwnd.meets_pseudo_cumack = net->new_pseudo_cumack;
sctp_clog.x.cwnd.need_new_pseudo_cumack = net->find_pseudo_cumack;
}
if (SCTP_CWNDLOG_PRESEND == from) {
sctp_clog.x.cwnd.meets_pseudo_cumack = stcb->asoc.peers_rwnd;
}
sctp_clog.x.cwnd.cwnd_augment = augment;
SCTP_CTR6(KTR_SCTP, "SCTP:%d[%d]:%x-%x-%x-%x",
SCTP_LOG_EVENT_CWND,
from,
sctp_clog.x.misc.log1,
sctp_clog.x.misc.log2,
sctp_clog.x.misc.log3,
sctp_clog.x.misc.log4);
#endif
}
#if !defined(__APPLE__) && !defined(__Userspace__)
void
sctp_log_lock(struct sctp_inpcb *inp, struct sctp_tcb *stcb, uint8_t from)
{
#if defined(SCTP_LOCAL_TRACE_BUF)
struct sctp_cwnd_log sctp_clog;
memset(&sctp_clog, 0, sizeof(sctp_clog));
if (inp) {
sctp_clog.x.lock.sock = (void *) inp->sctp_socket;
} else {
sctp_clog.x.lock.sock = (void *) NULL;
}
sctp_clog.x.lock.inp = (void *) inp;
#if defined(__FreeBSD__)
if (stcb) {
sctp_clog.x.lock.tcb_lock = mtx_owned(&stcb->tcb_mtx);
} else {
sctp_clog.x.lock.tcb_lock = SCTP_LOCK_UNKNOWN;
}
if (inp) {
sctp_clog.x.lock.inp_lock = mtx_owned(&inp->inp_mtx);
sctp_clog.x.lock.create_lock = mtx_owned(&inp->inp_create_mtx);
} else {
sctp_clog.x.lock.inp_lock = SCTP_LOCK_UNKNOWN;
sctp_clog.x.lock.create_lock = SCTP_LOCK_UNKNOWN;
}
sctp_clog.x.lock.info_lock = rw_wowned(&SCTP_BASE_INFO(ipi_ep_mtx));
if (inp && (inp->sctp_socket)) {
sctp_clog.x.lock.sock_lock = mtx_owned(&(inp->sctp_socket->so_rcv.sb_mtx));
sctp_clog.x.lock.sockrcvbuf_lock = mtx_owned(&(inp->sctp_socket->so_rcv.sb_mtx));
sctp_clog.x.lock.socksndbuf_lock = mtx_owned(&(inp->sctp_socket->so_snd.sb_mtx));
} else {
sctp_clog.x.lock.sock_lock = SCTP_LOCK_UNKNOWN;
sctp_clog.x.lock.sockrcvbuf_lock = SCTP_LOCK_UNKNOWN;
sctp_clog.x.lock.socksndbuf_lock = SCTP_LOCK_UNKNOWN;
}
#endif
SCTP_CTR6(KTR_SCTP, "SCTP:%d[%d]:%x-%x-%x-%x",
SCTP_LOG_LOCK_EVENT,
from,
sctp_clog.x.misc.log1,
sctp_clog.x.misc.log2,
sctp_clog.x.misc.log3,
sctp_clog.x.misc.log4);
#endif
}
#endif
void
sctp_log_maxburst(struct sctp_tcb *stcb, struct sctp_nets *net, int error, int burst, uint8_t from)
{
#if defined(SCTP_LOCAL_TRACE_BUF)
struct sctp_cwnd_log sctp_clog;
memset(&sctp_clog, 0, sizeof(sctp_clog));
sctp_clog.x.cwnd.net = net;
sctp_clog.x.cwnd.cwnd_new_value = error;
sctp_clog.x.cwnd.inflight = net->flight_size;
sctp_clog.x.cwnd.cwnd_augment = burst;
if (stcb->asoc.send_queue_cnt > 255)
sctp_clog.x.cwnd.cnt_in_send = 255;
else
sctp_clog.x.cwnd.cnt_in_send = stcb->asoc.send_queue_cnt;
if (stcb->asoc.stream_queue_cnt > 255)
sctp_clog.x.cwnd.cnt_in_str = 255;
else
sctp_clog.x.cwnd.cnt_in_str = stcb->asoc.stream_queue_cnt;
SCTP_CTR6(KTR_SCTP, "SCTP:%d[%d]:%x-%x-%x-%x",
SCTP_LOG_EVENT_MAXBURST,
from,
sctp_clog.x.misc.log1,
sctp_clog.x.misc.log2,
sctp_clog.x.misc.log3,
sctp_clog.x.misc.log4);
#endif
}
void
sctp_log_rwnd(uint8_t from, uint32_t peers_rwnd, uint32_t snd_size, uint32_t overhead)
{
#if defined(SCTP_LOCAL_TRACE_BUF)
struct sctp_cwnd_log sctp_clog;
sctp_clog.x.rwnd.rwnd = peers_rwnd;
sctp_clog.x.rwnd.send_size = snd_size;
sctp_clog.x.rwnd.overhead = overhead;
sctp_clog.x.rwnd.new_rwnd = 0;
SCTP_CTR6(KTR_SCTP, "SCTP:%d[%d]:%x-%x-%x-%x",
SCTP_LOG_EVENT_RWND,
from,
sctp_clog.x.misc.log1,
sctp_clog.x.misc.log2,
sctp_clog.x.misc.log3,
sctp_clog.x.misc.log4);
#endif
}
void
sctp_log_rwnd_set(uint8_t from, uint32_t peers_rwnd, uint32_t flight_size, uint32_t overhead, uint32_t a_rwndval)
{
#if defined(SCTP_LOCAL_TRACE_BUF)
struct sctp_cwnd_log sctp_clog;
sctp_clog.x.rwnd.rwnd = peers_rwnd;
sctp_clog.x.rwnd.send_size = flight_size;
sctp_clog.x.rwnd.overhead = overhead;
sctp_clog.x.rwnd.new_rwnd = a_rwndval;
SCTP_CTR6(KTR_SCTP, "SCTP:%d[%d]:%x-%x-%x-%x",
SCTP_LOG_EVENT_RWND,
from,
sctp_clog.x.misc.log1,
sctp_clog.x.misc.log2,
sctp_clog.x.misc.log3,
sctp_clog.x.misc.log4);
#endif
}
#ifdef SCTP_MBCNT_LOGGING
static void
sctp_log_mbcnt(uint8_t from, uint32_t total_oq, uint32_t book, uint32_t total_mbcnt_q, uint32_t mbcnt)
{
#if defined(SCTP_LOCAL_TRACE_BUF)
struct sctp_cwnd_log sctp_clog;
sctp_clog.x.mbcnt.total_queue_size = total_oq;
sctp_clog.x.mbcnt.size_change = book;
sctp_clog.x.mbcnt.total_queue_mb_size = total_mbcnt_q;
sctp_clog.x.mbcnt.mbcnt_change = mbcnt;
SCTP_CTR6(KTR_SCTP, "SCTP:%d[%d]:%x-%x-%x-%x",
SCTP_LOG_EVENT_MBCNT,
from,
sctp_clog.x.misc.log1,
sctp_clog.x.misc.log2,
sctp_clog.x.misc.log3,
sctp_clog.x.misc.log4);
#endif
}
#endif
void
sctp_misc_ints(uint8_t from, uint32_t a, uint32_t b, uint32_t c, uint32_t d)
{
#if defined(SCTP_LOCAL_TRACE_BUF)
SCTP_CTR6(KTR_SCTP, "SCTP:%d[%d]:%x-%x-%x-%x",
SCTP_LOG_MISC_EVENT,
from,
a, b, c, d);
#endif
}
void
sctp_wakeup_log(struct sctp_tcb *stcb, uint32_t wake_cnt, int from)
{
#if defined(SCTP_LOCAL_TRACE_BUF)
struct sctp_cwnd_log sctp_clog;
sctp_clog.x.wake.stcb = (void *)stcb;
sctp_clog.x.wake.wake_cnt = wake_cnt;
sctp_clog.x.wake.flight = stcb->asoc.total_flight_count;
sctp_clog.x.wake.send_q = stcb->asoc.send_queue_cnt;
sctp_clog.x.wake.sent_q = stcb->asoc.sent_queue_cnt;
if (stcb->asoc.stream_queue_cnt < 0xff)
sctp_clog.x.wake.stream_qcnt = (uint8_t) stcb->asoc.stream_queue_cnt;
else
sctp_clog.x.wake.stream_qcnt = 0xff;
if (stcb->asoc.chunks_on_out_queue < 0xff)
sctp_clog.x.wake.chunks_on_oque = (uint8_t) stcb->asoc.chunks_on_out_queue;
else
sctp_clog.x.wake.chunks_on_oque = 0xff;
sctp_clog.x.wake.sctpflags = 0;
/* set in the defered mode stuff */
if (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_DONT_WAKE)
sctp_clog.x.wake.sctpflags |= 1;
if (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_WAKEOUTPUT)
sctp_clog.x.wake.sctpflags |= 2;
if (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_WAKEINPUT)
sctp_clog.x.wake.sctpflags |= 4;
/* what about the sb */
if (stcb->sctp_socket) {
struct socket *so = stcb->sctp_socket;
sctp_clog.x.wake.sbflags = (uint8_t)((so->so_snd.sb_flags & 0x00ff));
} else {
sctp_clog.x.wake.sbflags = 0xff;
}
SCTP_CTR6(KTR_SCTP, "SCTP:%d[%d]:%x-%x-%x-%x",
SCTP_LOG_EVENT_WAKE,
from,
sctp_clog.x.misc.log1,
sctp_clog.x.misc.log2,
sctp_clog.x.misc.log3,
sctp_clog.x.misc.log4);
#endif
}
void
sctp_log_block(uint8_t from, struct sctp_association *asoc, ssize_t sendlen)
{
#if defined(SCTP_LOCAL_TRACE_BUF)
struct sctp_cwnd_log sctp_clog;
sctp_clog.x.blk.onsb = asoc->total_output_queue_size;
sctp_clog.x.blk.send_sent_qcnt = (uint16_t) (asoc->send_queue_cnt + asoc->sent_queue_cnt);
sctp_clog.x.blk.peer_rwnd = asoc->peers_rwnd;
sctp_clog.x.blk.stream_qcnt = (uint16_t) asoc->stream_queue_cnt;
sctp_clog.x.blk.chunks_on_oque = (uint16_t) asoc->chunks_on_out_queue;
sctp_clog.x.blk.flight_size = (uint16_t) (asoc->total_flight/1024);
sctp_clog.x.blk.sndlen = (uint32_t)sendlen;
SCTP_CTR6(KTR_SCTP, "SCTP:%d[%d]:%x-%x-%x-%x",
SCTP_LOG_EVENT_BLOCK,
from,
sctp_clog.x.misc.log1,
sctp_clog.x.misc.log2,
sctp_clog.x.misc.log3,
sctp_clog.x.misc.log4);
#endif
}
int
sctp_fill_stat_log(void *optval SCTP_UNUSED, size_t *optsize SCTP_UNUSED)
{
/* May need to fix this if ktrdump does not work */
return (0);
}
#ifdef SCTP_AUDITING_ENABLED
uint8_t sctp_audit_data[SCTP_AUDIT_SIZE][2];
static int sctp_audit_indx = 0;
static
void
sctp_print_audit_report(void)
{
int i;
int cnt;
cnt = 0;
for (i = sctp_audit_indx; i < SCTP_AUDIT_SIZE; i++) {
if ((sctp_audit_data[i][0] == 0xe0) &&
(sctp_audit_data[i][1] == 0x01)) {
cnt = 0;
SCTP_PRINTF("\n");
} else if (sctp_audit_data[i][0] == 0xf0) {
cnt = 0;
SCTP_PRINTF("\n");
} else if ((sctp_audit_data[i][0] == 0xc0) &&
(sctp_audit_data[i][1] == 0x01)) {
SCTP_PRINTF("\n");
cnt = 0;
}
SCTP_PRINTF("%2.2x%2.2x ", (uint32_t) sctp_audit_data[i][0],
(uint32_t) sctp_audit_data[i][1]);
cnt++;
if ((cnt % 14) == 0)
SCTP_PRINTF("\n");
}
for (i = 0; i < sctp_audit_indx; i++) {
if ((sctp_audit_data[i][0] == 0xe0) &&
(sctp_audit_data[i][1] == 0x01)) {
cnt = 0;
SCTP_PRINTF("\n");
} else if (sctp_audit_data[i][0] == 0xf0) {
cnt = 0;
SCTP_PRINTF("\n");
} else if ((sctp_audit_data[i][0] == 0xc0) &&
(sctp_audit_data[i][1] == 0x01)) {
SCTP_PRINTF("\n");
cnt = 0;
}
SCTP_PRINTF("%2.2x%2.2x ", (uint32_t) sctp_audit_data[i][0],
(uint32_t) sctp_audit_data[i][1]);
cnt++;
if ((cnt % 14) == 0)
SCTP_PRINTF("\n");
}
SCTP_PRINTF("\n");
}
void
sctp_auditing(int from, struct sctp_inpcb *inp, struct sctp_tcb *stcb,
struct sctp_nets *net)
{
int resend_cnt, tot_out, rep, tot_book_cnt;
struct sctp_nets *lnet;
struct sctp_tmit_chunk *chk;
sctp_audit_data[sctp_audit_indx][0] = 0xAA;
sctp_audit_data[sctp_audit_indx][1] = 0x000000ff & from;
sctp_audit_indx++;
if (sctp_audit_indx >= SCTP_AUDIT_SIZE) {
sctp_audit_indx = 0;
}
if (inp == NULL) {
sctp_audit_data[sctp_audit_indx][0] = 0xAF;
sctp_audit_data[sctp_audit_indx][1] = 0x01;
sctp_audit_indx++;
if (sctp_audit_indx >= SCTP_AUDIT_SIZE) {
sctp_audit_indx = 0;
}
return;
}
if (stcb == NULL) {
sctp_audit_data[sctp_audit_indx][0] = 0xAF;
sctp_audit_data[sctp_audit_indx][1] = 0x02;
sctp_audit_indx++;
if (sctp_audit_indx >= SCTP_AUDIT_SIZE) {
sctp_audit_indx = 0;
}
return;
}
sctp_audit_data[sctp_audit_indx][0] = 0xA1;
sctp_audit_data[sctp_audit_indx][1] =
(0x000000ff & stcb->asoc.sent_queue_retran_cnt);
sctp_audit_indx++;
if (sctp_audit_indx >= SCTP_AUDIT_SIZE) {
sctp_audit_indx = 0;
}
rep = 0;
tot_book_cnt = 0;
resend_cnt = tot_out = 0;
TAILQ_FOREACH(chk, &stcb->asoc.sent_queue, sctp_next) {
if (chk->sent == SCTP_DATAGRAM_RESEND) {
resend_cnt++;
} else if (chk->sent < SCTP_DATAGRAM_RESEND) {
tot_out += chk->book_size;
tot_book_cnt++;
}
}
if (resend_cnt != stcb->asoc.sent_queue_retran_cnt) {
sctp_audit_data[sctp_audit_indx][0] = 0xAF;
sctp_audit_data[sctp_audit_indx][1] = 0xA1;
sctp_audit_indx++;
if (sctp_audit_indx >= SCTP_AUDIT_SIZE) {
sctp_audit_indx = 0;
}
SCTP_PRINTF("resend_cnt:%d asoc-tot:%d\n",
resend_cnt, stcb->asoc.sent_queue_retran_cnt);
rep = 1;
stcb->asoc.sent_queue_retran_cnt = resend_cnt;
sctp_audit_data[sctp_audit_indx][0] = 0xA2;
sctp_audit_data[sctp_audit_indx][1] =
(0x000000ff & stcb->asoc.sent_queue_retran_cnt);
sctp_audit_indx++;
if (sctp_audit_indx >= SCTP_AUDIT_SIZE) {
sctp_audit_indx = 0;
}
}
if (tot_out != stcb->asoc.total_flight) {
sctp_audit_data[sctp_audit_indx][0] = 0xAF;
sctp_audit_data[sctp_audit_indx][1] = 0xA2;
sctp_audit_indx++;
if (sctp_audit_indx >= SCTP_AUDIT_SIZE) {
sctp_audit_indx = 0;
}
rep = 1;
SCTP_PRINTF("tot_flt:%d asoc_tot:%d\n", tot_out,
(int)stcb->asoc.total_flight);
stcb->asoc.total_flight = tot_out;
}
if (tot_book_cnt != stcb->asoc.total_flight_count) {
sctp_audit_data[sctp_audit_indx][0] = 0xAF;
sctp_audit_data[sctp_audit_indx][1] = 0xA5;
sctp_audit_indx++;
if (sctp_audit_indx >= SCTP_AUDIT_SIZE) {
sctp_audit_indx = 0;
}
rep = 1;
SCTP_PRINTF("tot_flt_book:%d\n", tot_book_cnt);
stcb->asoc.total_flight_count = tot_book_cnt;
}
tot_out = 0;
TAILQ_FOREACH(lnet, &stcb->asoc.nets, sctp_next) {
tot_out += lnet->flight_size;
}
if (tot_out != stcb->asoc.total_flight) {
sctp_audit_data[sctp_audit_indx][0] = 0xAF;
sctp_audit_data[sctp_audit_indx][1] = 0xA3;
sctp_audit_indx++;
if (sctp_audit_indx >= SCTP_AUDIT_SIZE) {
sctp_audit_indx = 0;
}
rep = 1;
SCTP_PRINTF("real flight:%d net total was %d\n",
stcb->asoc.total_flight, tot_out);
/* now corrective action */
TAILQ_FOREACH(lnet, &stcb->asoc.nets, sctp_next) {
tot_out = 0;
TAILQ_FOREACH(chk, &stcb->asoc.sent_queue, sctp_next) {
if ((chk->whoTo == lnet) &&
(chk->sent < SCTP_DATAGRAM_RESEND)) {
tot_out += chk->book_size;
}
}
if (lnet->flight_size != tot_out) {
SCTP_PRINTF("net:%p flight was %d corrected to %d\n",
(void *)lnet, lnet->flight_size,
tot_out);
lnet->flight_size = tot_out;
}
}
}
if (rep) {
sctp_print_audit_report();
}
}
void
sctp_audit_log(uint8_t ev, uint8_t fd)
{
sctp_audit_data[sctp_audit_indx][0] = ev;
sctp_audit_data[sctp_audit_indx][1] = fd;
sctp_audit_indx++;
if (sctp_audit_indx >= SCTP_AUDIT_SIZE) {
sctp_audit_indx = 0;
}
}
#endif
/*
* The conversion from time to ticks and vice versa is done by rounding
* upwards. This way we can test in the code the time to be positive and
* know that this corresponds to a positive number of ticks.
*/
uint32_t
sctp_msecs_to_ticks(uint32_t msecs)
{
uint64_t temp;
uint32_t ticks;
if (hz == 1000) {
ticks = msecs;
} else {
temp = (((uint64_t)msecs * hz) + 999) / 1000;
if (temp > UINT32_MAX) {
ticks = UINT32_MAX;
} else {
ticks = (uint32_t)temp;
}
}
return (ticks);
}
uint32_t
sctp_ticks_to_msecs(uint32_t ticks)
{
uint64_t temp;
uint32_t msecs;
if (hz == 1000) {
msecs = ticks;
} else {
temp = (((uint64_t)ticks * 1000) + (hz - 1)) / hz;
if (temp > UINT32_MAX) {
msecs = UINT32_MAX;
} else {
msecs = (uint32_t)temp;
}
}
return (msecs);
}
uint32_t
sctp_secs_to_ticks(uint32_t secs)
{
uint64_t temp;
uint32_t ticks;
temp = (uint64_t)secs * hz;
if (temp > UINT32_MAX) {
ticks = UINT32_MAX;
} else {
ticks = (uint32_t)temp;
}
return (ticks);
}
uint32_t
sctp_ticks_to_secs(uint32_t ticks)
{
uint64_t temp;
uint32_t secs;
temp = ((uint64_t)ticks + (hz - 1)) / hz;
if (temp > UINT32_MAX) {
secs = UINT32_MAX;
} else {
secs = (uint32_t)temp;
}
return (secs);
}
/*
* sctp_stop_timers_for_shutdown() should be called
* when entering the SHUTDOWN_SENT or SHUTDOWN_ACK_SENT
* state to make sure that all timers are stopped.
*/
void
sctp_stop_timers_for_shutdown(struct sctp_tcb *stcb)
{
struct sctp_inpcb *inp;
struct sctp_nets *net;
inp = stcb->sctp_ep;
sctp_timer_stop(SCTP_TIMER_TYPE_RECV, inp, stcb, NULL,
SCTP_FROM_SCTPUTIL + SCTP_LOC_12);
sctp_timer_stop(SCTP_TIMER_TYPE_STRRESET, inp, stcb, NULL,
SCTP_FROM_SCTPUTIL + SCTP_LOC_13);
sctp_timer_stop(SCTP_TIMER_TYPE_ASCONF, inp, stcb, NULL,
SCTP_FROM_SCTPUTIL + SCTP_LOC_14);
sctp_timer_stop(SCTP_TIMER_TYPE_AUTOCLOSE, inp, stcb, NULL,
SCTP_FROM_SCTPUTIL + SCTP_LOC_15);
TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) {
sctp_timer_stop(SCTP_TIMER_TYPE_PATHMTURAISE, inp, stcb, net,
SCTP_FROM_SCTPUTIL + SCTP_LOC_16);
sctp_timer_stop(SCTP_TIMER_TYPE_HEARTBEAT, inp, stcb, net,
SCTP_FROM_SCTPUTIL + SCTP_LOC_17);
}
}
void
sctp_stop_association_timers(struct sctp_tcb *stcb, bool stop_assoc_kill_timer)
{
struct sctp_inpcb *inp;
struct sctp_nets *net;
inp = stcb->sctp_ep;
sctp_timer_stop(SCTP_TIMER_TYPE_RECV, inp, stcb, NULL,
SCTP_FROM_SCTPUTIL + SCTP_LOC_18);
sctp_timer_stop(SCTP_TIMER_TYPE_STRRESET, inp, stcb, NULL,
SCTP_FROM_SCTPUTIL + SCTP_LOC_19);
if (stop_assoc_kill_timer) {
sctp_timer_stop(SCTP_TIMER_TYPE_ASOCKILL, inp, stcb, NULL,
SCTP_FROM_SCTPUTIL + SCTP_LOC_20);
}
sctp_timer_stop(SCTP_TIMER_TYPE_ASCONF, inp, stcb, NULL,
SCTP_FROM_SCTPUTIL + SCTP_LOC_21);
sctp_timer_stop(SCTP_TIMER_TYPE_AUTOCLOSE, inp, stcb, NULL,
SCTP_FROM_SCTPUTIL + SCTP_LOC_22);
sctp_timer_stop(SCTP_TIMER_TYPE_SHUTDOWNGUARD, inp, stcb, NULL,
SCTP_FROM_SCTPUTIL + SCTP_LOC_23);
/* Mobility adaptation */
sctp_timer_stop(SCTP_TIMER_TYPE_PRIM_DELETED, inp, stcb, NULL,
SCTP_FROM_SCTPUTIL + SCTP_LOC_24);
TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) {
sctp_timer_stop(SCTP_TIMER_TYPE_SEND, inp, stcb, net,
SCTP_FROM_SCTPUTIL + SCTP_LOC_25);
sctp_timer_stop(SCTP_TIMER_TYPE_INIT, inp, stcb, net,
SCTP_FROM_SCTPUTIL + SCTP_LOC_26);
sctp_timer_stop(SCTP_TIMER_TYPE_SHUTDOWN, inp, stcb, net,
SCTP_FROM_SCTPUTIL + SCTP_LOC_27);
sctp_timer_stop(SCTP_TIMER_TYPE_COOKIE, inp, stcb, net,
SCTP_FROM_SCTPUTIL + SCTP_LOC_28);
sctp_timer_stop(SCTP_TIMER_TYPE_SHUTDOWNACK, inp, stcb, net,
SCTP_FROM_SCTPUTIL + SCTP_LOC_29);
sctp_timer_stop(SCTP_TIMER_TYPE_PATHMTURAISE, inp, stcb, net,
SCTP_FROM_SCTPUTIL + SCTP_LOC_30);
sctp_timer_stop(SCTP_TIMER_TYPE_HEARTBEAT, inp, stcb, net,
SCTP_FROM_SCTPUTIL + SCTP_LOC_31);
}
}
/*
* A list of sizes based on typical mtu's, used only if next hop size not
* returned. These values MUST be multiples of 4 and MUST be ordered.
*/
static uint32_t sctp_mtu_sizes[] = {
68,
296,
508,
512,
544,
576,
1004,
1492,
1500,
1536,
2000,
2048,
4352,
4464,
8168,
17912,
32000,
65532
};
/*
* Return the largest MTU in sctp_mtu_sizes smaller than val.
* If val is smaller than the minimum, just return the largest
* multiple of 4 smaller or equal to val.
* Ensure that the result is a multiple of 4.
*/
uint32_t
sctp_get_prev_mtu(uint32_t val)
{
uint32_t i;
val &= 0xfffffffc;
if (val <= sctp_mtu_sizes[0]) {
return (val);
}
for (i = 1; i < (sizeof(sctp_mtu_sizes) / sizeof(uint32_t)); i++) {
if (val <= sctp_mtu_sizes[i]) {
break;
}
}
KASSERT((sctp_mtu_sizes[i - 1] & 0x00000003) == 0,
("sctp_mtu_sizes[%u] not a multiple of 4", i - 1));
return (sctp_mtu_sizes[i - 1]);
}
/*
* Return the smallest MTU in sctp_mtu_sizes larger than val.
* If val is larger than the maximum, just return the largest multiple of 4 smaller
* or equal to val.
* Ensure that the result is a multiple of 4.
*/
uint32_t
sctp_get_next_mtu(uint32_t val)
{
/* select another MTU that is just bigger than this one */
uint32_t i;
val &= 0xfffffffc;
for (i = 0; i < (sizeof(sctp_mtu_sizes) / sizeof(uint32_t)); i++) {
if (val < sctp_mtu_sizes[i]) {
KASSERT((sctp_mtu_sizes[i] & 0x00000003) == 0,
("sctp_mtu_sizes[%u] not a multiple of 4", i));
return (sctp_mtu_sizes[i]);
}
}
return (val);
}
void
sctp_fill_random_store(struct sctp_pcb *m)
{
/*
* Here we use the MD5/SHA-1 to hash with our good randomNumbers and
* our counter. The result becomes our good random numbers and we
* then setup to give these out. Note that we do no locking to
* protect this. This is ok, since if competing folks call this we
* will get more gobbled gook in the random store which is what we
* want. There is a danger that two guys will use the same random
* numbers, but thats ok too since that is random as well :->
*/
m->store_at = 0;
#if defined(__Userspace__) && defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION)
for (int i = 0; i < (int) (sizeof(m->random_store) / sizeof(m->random_store[0])); i++) {
m->random_store[i] = (uint8_t) rand();
}
#else
(void)sctp_hmac(SCTP_HMAC, (uint8_t *)m->random_numbers,
sizeof(m->random_numbers), (uint8_t *)&m->random_counter,
sizeof(m->random_counter), (uint8_t *)m->random_store);
#endif
m->random_counter++;
}
uint32_t
sctp_select_initial_TSN(struct sctp_pcb *inp)
{
/*
* A true implementation should use random selection process to get
* the initial stream sequence number, using RFC1750 as a good
* guideline
*/
uint32_t x, *xp;
uint8_t *p;
int store_at, new_store;
if (inp->initial_sequence_debug != 0) {
uint32_t ret;
ret = inp->initial_sequence_debug;
inp->initial_sequence_debug++;
return (ret);
}
retry:
store_at = inp->store_at;
new_store = store_at + sizeof(uint32_t);
if (new_store >= (SCTP_SIGNATURE_SIZE-3)) {
new_store = 0;
}
if (!atomic_cmpset_int(&inp->store_at, store_at, new_store)) {
goto retry;
}
if (new_store == 0) {
/* Refill the random store */
sctp_fill_random_store(inp);
}
p = &inp->random_store[store_at];
xp = (uint32_t *)p;
x = *xp;
return (x);
}
uint32_t
sctp_select_a_tag(struct sctp_inpcb *inp, uint16_t lport, uint16_t rport, int check)
{
uint32_t x;
struct timeval now;
if (check) {
(void)SCTP_GETTIME_TIMEVAL(&now);
}
for (;;) {
x = sctp_select_initial_TSN(&inp->sctp_ep);
if (x == 0) {
/* we never use 0 */
continue;
}
if (!check || sctp_is_vtag_good(x, lport, rport, &now)) {
break;
}
}
return (x);
}
int32_t
sctp_map_assoc_state(int kernel_state)
{
int32_t user_state;
if (kernel_state & SCTP_STATE_WAS_ABORTED) {
user_state = SCTP_CLOSED;
} else if (kernel_state & SCTP_STATE_SHUTDOWN_PENDING) {
user_state = SCTP_SHUTDOWN_PENDING;
} else {
switch (kernel_state & SCTP_STATE_MASK) {
case SCTP_STATE_EMPTY:
user_state = SCTP_CLOSED;
break;
case SCTP_STATE_INUSE:
user_state = SCTP_CLOSED;
break;
case SCTP_STATE_COOKIE_WAIT:
user_state = SCTP_COOKIE_WAIT;
break;
case SCTP_STATE_COOKIE_ECHOED:
user_state = SCTP_COOKIE_ECHOED;
break;
case SCTP_STATE_OPEN:
user_state = SCTP_ESTABLISHED;
break;
case SCTP_STATE_SHUTDOWN_SENT:
user_state = SCTP_SHUTDOWN_SENT;
break;
case SCTP_STATE_SHUTDOWN_RECEIVED:
user_state = SCTP_SHUTDOWN_RECEIVED;
break;
case SCTP_STATE_SHUTDOWN_ACK_SENT:
user_state = SCTP_SHUTDOWN_ACK_SENT;
break;
default:
user_state = SCTP_CLOSED;
break;
}
}
return (user_state);
}
int
sctp_init_asoc(struct sctp_inpcb *inp, struct sctp_tcb *stcb,
uint32_t override_tag, uint32_t vrf_id, uint16_t o_strms)
{
struct sctp_association *asoc;
/*
* Anything set to zero is taken care of by the allocation routine's
* bzero
*/
/*
* Up front select what scoping to apply on addresses I tell my peer
* Not sure what to do with these right now, we will need to come up
* with a way to set them. We may need to pass them through from the
* caller in the sctp_aloc_assoc() function.
*/
int i;
#if defined(SCTP_DETAILED_STR_STATS)
int j;
#endif
asoc = &stcb->asoc;
/* init all variables to a known value. */
SCTP_SET_STATE(stcb, SCTP_STATE_INUSE);
asoc->max_burst = inp->sctp_ep.max_burst;
asoc->fr_max_burst = inp->sctp_ep.fr_max_burst;
asoc->heart_beat_delay = sctp_ticks_to_msecs(inp->sctp_ep.sctp_timeoutticks[SCTP_TIMER_HEARTBEAT]);
asoc->cookie_life = inp->sctp_ep.def_cookie_life;
asoc->sctp_cmt_on_off = inp->sctp_cmt_on_off;
asoc->ecn_supported = inp->ecn_supported;
asoc->prsctp_supported = inp->prsctp_supported;
asoc->auth_supported = inp->auth_supported;
asoc->asconf_supported = inp->asconf_supported;
asoc->reconfig_supported = inp->reconfig_supported;
asoc->nrsack_supported = inp->nrsack_supported;
asoc->pktdrop_supported = inp->pktdrop_supported;
asoc->idata_supported = inp->idata_supported;
asoc->sctp_cmt_pf = (uint8_t)0;
asoc->sctp_frag_point = inp->sctp_frag_point;
asoc->sctp_features = inp->sctp_features;
asoc->default_dscp = inp->sctp_ep.default_dscp;
asoc->max_cwnd = inp->max_cwnd;
#ifdef INET6
if (inp->sctp_ep.default_flowlabel) {
asoc->default_flowlabel = inp->sctp_ep.default_flowlabel;
} else {
if (inp->ip_inp.inp.inp_flags & IN6P_AUTOFLOWLABEL) {
asoc->default_flowlabel = sctp_select_initial_TSN(&inp->sctp_ep);
asoc->default_flowlabel &= 0x000fffff;
asoc->default_flowlabel |= 0x80000000;
} else {
asoc->default_flowlabel = 0;
}
}
#endif
asoc->sb_send_resv = 0;
if (override_tag) {
asoc->my_vtag = override_tag;
} else {
asoc->my_vtag = sctp_select_a_tag(inp, stcb->sctp_ep->sctp_lport, stcb->rport, 1);
}
/* Get the nonce tags */
asoc->my_vtag_nonce = sctp_select_a_tag(inp, stcb->sctp_ep->sctp_lport, stcb->rport, 0);
asoc->peer_vtag_nonce = sctp_select_a_tag(inp, stcb->sctp_ep->sctp_lport, stcb->rport, 0);
asoc->vrf_id = vrf_id;
#ifdef SCTP_ASOCLOG_OF_TSNS
asoc->tsn_in_at = 0;
asoc->tsn_out_at = 0;
asoc->tsn_in_wrapped = 0;
asoc->tsn_out_wrapped = 0;
asoc->cumack_log_at = 0;
asoc->cumack_log_atsnt = 0;
#endif
#ifdef SCTP_FS_SPEC_LOG
asoc->fs_index = 0;
#endif
asoc->refcnt = 0;
asoc->assoc_up_sent = 0;
asoc->asconf_seq_out = asoc->str_reset_seq_out = asoc->init_seq_number = asoc->sending_seq =
sctp_select_initial_TSN(&inp->sctp_ep);
asoc->asconf_seq_out_acked = asoc->asconf_seq_out - 1;
/* we are optimisitic here */
asoc->peer_supports_nat = 0;
asoc->sent_queue_retran_cnt = 0;
/* for CMT */
asoc->last_net_cmt_send_started = NULL;
/* This will need to be adjusted */
asoc->last_acked_seq = asoc->init_seq_number - 1;
asoc->advanced_peer_ack_point = asoc->last_acked_seq;
asoc->asconf_seq_in = asoc->last_acked_seq;
/* here we are different, we hold the next one we expect */
asoc->str_reset_seq_in = asoc->last_acked_seq + 1;
asoc->initial_init_rto_max = inp->sctp_ep.initial_init_rto_max;
asoc->initial_rto = inp->sctp_ep.initial_rto;
asoc->default_mtu = inp->sctp_ep.default_mtu;
asoc->max_init_times = inp->sctp_ep.max_init_times;
asoc->max_send_times = inp->sctp_ep.max_send_times;
asoc->def_net_failure = inp->sctp_ep.def_net_failure;
asoc->def_net_pf_threshold = inp->sctp_ep.def_net_pf_threshold;
asoc->free_chunk_cnt = 0;
asoc->iam_blocking = 0;
asoc->context = inp->sctp_context;
asoc->local_strreset_support = inp->local_strreset_support;
asoc->def_send = inp->def_send;
asoc->delayed_ack = sctp_ticks_to_msecs(inp->sctp_ep.sctp_timeoutticks[SCTP_TIMER_RECV]);
asoc->sack_freq = inp->sctp_ep.sctp_sack_freq;
asoc->pr_sctp_cnt = 0;
asoc->total_output_queue_size = 0;
if (inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) {
asoc->scope.ipv6_addr_legal = 1;
if (SCTP_IPV6_V6ONLY(inp) == 0) {
asoc->scope.ipv4_addr_legal = 1;
} else {
asoc->scope.ipv4_addr_legal = 0;
}
#if defined(__Userspace__)
asoc->scope.conn_addr_legal = 0;
#endif
} else {
asoc->scope.ipv6_addr_legal = 0;
#if defined(__Userspace__)
if (inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_CONN) {
asoc->scope.conn_addr_legal = 1;
asoc->scope.ipv4_addr_legal = 0;
} else {
asoc->scope.conn_addr_legal = 0;
asoc->scope.ipv4_addr_legal = 1;
}
#else
asoc->scope.ipv4_addr_legal = 1;
#endif
}
asoc->my_rwnd = max(SCTP_SB_LIMIT_RCV(inp->sctp_socket), SCTP_MINIMAL_RWND);
asoc->peers_rwnd = SCTP_SB_LIMIT_RCV(inp->sctp_socket);
asoc->smallest_mtu = inp->sctp_frag_point;
asoc->minrto = inp->sctp_ep.sctp_minrto;
asoc->maxrto = inp->sctp_ep.sctp_maxrto;
asoc->stream_locked_on = 0;
asoc->ecn_echo_cnt_onq = 0;
asoc->stream_locked = 0;
asoc->send_sack = 1;
LIST_INIT(&asoc->sctp_restricted_addrs);
TAILQ_INIT(&asoc->nets);
TAILQ_INIT(&asoc->pending_reply_queue);
TAILQ_INIT(&asoc->asconf_ack_sent);
/* Setup to fill the hb random cache at first HB */
asoc->hb_random_idx = 4;
asoc->sctp_autoclose_ticks = inp->sctp_ep.auto_close_time;
stcb->asoc.congestion_control_module = inp->sctp_ep.sctp_default_cc_module;
stcb->asoc.cc_functions = sctp_cc_functions[inp->sctp_ep.sctp_default_cc_module];
stcb->asoc.stream_scheduling_module = inp->sctp_ep.sctp_default_ss_module;
stcb->asoc.ss_functions = sctp_ss_functions[inp->sctp_ep.sctp_default_ss_module];
/*
* Now the stream parameters, here we allocate space for all streams
* that we request by default.
*/
asoc->strm_realoutsize = asoc->streamoutcnt = asoc->pre_open_streams =
o_strms;
SCTP_MALLOC(asoc->strmout, struct sctp_stream_out *,
asoc->streamoutcnt * sizeof(struct sctp_stream_out),
SCTP_M_STRMO);
if (asoc->strmout == NULL) {
/* big trouble no memory */
SCTP_LTRACE_ERR_RET(NULL, stcb, NULL, SCTP_FROM_SCTPUTIL, ENOMEM);
return (ENOMEM);
}
for (i = 0; i < asoc->streamoutcnt; i++) {
/*
* inbound side must be set to 0xffff, also NOTE when we get
* the INIT-ACK back (for INIT sender) we MUST reduce the
* count (streamoutcnt) but first check if we sent to any of
* the upper streams that were dropped (if some were). Those
* that were dropped must be notified to the upper layer as
* failed to send.
*/
TAILQ_INIT(&asoc->strmout[i].outqueue);
asoc->ss_functions.sctp_ss_init_stream(stcb, &asoc->strmout[i], NULL);
asoc->strmout[i].chunks_on_queues = 0;
#if defined(SCTP_DETAILED_STR_STATS)
for (j = 0; j < SCTP_PR_SCTP_MAX + 1; j++) {
asoc->strmout[i].abandoned_sent[j] = 0;
asoc->strmout[i].abandoned_unsent[j] = 0;
}
#else
asoc->strmout[i].abandoned_sent[0] = 0;
asoc->strmout[i].abandoned_unsent[0] = 0;
#endif
asoc->strmout[i].next_mid_ordered = 0;
asoc->strmout[i].next_mid_unordered = 0;
asoc->strmout[i].sid = i;
asoc->strmout[i].last_msg_incomplete = 0;
asoc->strmout[i].state = SCTP_STREAM_OPENING;
}
asoc->ss_functions.sctp_ss_init(stcb, asoc, 0);
/* Now the mapping array */
asoc->mapping_array_size = SCTP_INITIAL_MAPPING_ARRAY;
SCTP_MALLOC(asoc->mapping_array, uint8_t *, asoc->mapping_array_size,
SCTP_M_MAP);
if (asoc->mapping_array == NULL) {
SCTP_FREE(asoc->strmout, SCTP_M_STRMO);
SCTP_LTRACE_ERR_RET(NULL, stcb, NULL, SCTP_FROM_SCTPUTIL, ENOMEM);
return (ENOMEM);
}
memset(asoc->mapping_array, 0, asoc->mapping_array_size);
SCTP_MALLOC(asoc->nr_mapping_array, uint8_t *, asoc->mapping_array_size,
SCTP_M_MAP);
if (asoc->nr_mapping_array == NULL) {
SCTP_FREE(asoc->strmout, SCTP_M_STRMO);
SCTP_FREE(asoc->mapping_array, SCTP_M_MAP);
SCTP_LTRACE_ERR_RET(NULL, stcb, NULL, SCTP_FROM_SCTPUTIL, ENOMEM);
return (ENOMEM);
}
memset(asoc->nr_mapping_array, 0, asoc->mapping_array_size);
/* Now the init of the other outqueues */
TAILQ_INIT(&asoc->free_chunks);
TAILQ_INIT(&asoc->control_send_queue);
TAILQ_INIT(&asoc->asconf_send_queue);
TAILQ_INIT(&asoc->send_queue);
TAILQ_INIT(&asoc->sent_queue);
TAILQ_INIT(&asoc->resetHead);
asoc->max_inbound_streams = inp->sctp_ep.max_open_streams_intome;
TAILQ_INIT(&asoc->asconf_queue);
/* authentication fields */
asoc->authinfo.random = NULL;
asoc->authinfo.active_keyid = 0;
asoc->authinfo.assoc_key = NULL;
asoc->authinfo.assoc_keyid = 0;
asoc->authinfo.recv_key = NULL;
asoc->authinfo.recv_keyid = 0;
LIST_INIT(&asoc->shared_keys);
asoc->marked_retrans = 0;
asoc->port = inp->sctp_ep.port;
asoc->timoinit = 0;
asoc->timodata = 0;
asoc->timosack = 0;
asoc->timoshutdown = 0;
asoc->timoheartbeat = 0;
asoc->timocookie = 0;
asoc->timoshutdownack = 0;
(void)SCTP_GETTIME_TIMEVAL(&asoc->start_time);
asoc->discontinuity_time = asoc->start_time;
for (i = 0; i < SCTP_PR_SCTP_MAX + 1; i++) {
asoc->abandoned_unsent[i] = 0;
asoc->abandoned_sent[i] = 0;
}
/* sa_ignore MEMLEAK {memory is put in the assoc mapping array and freed later when
* the association is freed.
*/
return (0);
}
void
sctp_print_mapping_array(struct sctp_association *asoc)
{
unsigned int i, limit;
SCTP_PRINTF("Mapping array size: %d, baseTSN: %8.8x, cumAck: %8.8x, highestTSN: (%8.8x, %8.8x).\n",
asoc->mapping_array_size,
asoc->mapping_array_base_tsn,
asoc->cumulative_tsn,
asoc->highest_tsn_inside_map,
asoc->highest_tsn_inside_nr_map);
for (limit = asoc->mapping_array_size; limit > 1; limit--) {
if (asoc->mapping_array[limit - 1] != 0) {
break;
}
}
SCTP_PRINTF("Renegable mapping array (last %d entries are zero):\n", asoc->mapping_array_size - limit);
for (i = 0; i < limit; i++) {
SCTP_PRINTF("%2.2x%c", asoc->mapping_array[i], ((i + 1) % 16) ? ' ' : '\n');
}
if (limit % 16)
SCTP_PRINTF("\n");
for (limit = asoc->mapping_array_size; limit > 1; limit--) {
if (asoc->nr_mapping_array[limit - 1]) {
break;
}
}
SCTP_PRINTF("Non renegable mapping array (last %d entries are zero):\n", asoc->mapping_array_size - limit);
for (i = 0; i < limit; i++) {
SCTP_PRINTF("%2.2x%c", asoc->nr_mapping_array[i], ((i + 1) % 16) ? ' ': '\n');
}
if (limit % 16)
SCTP_PRINTF("\n");
}
int
sctp_expand_mapping_array(struct sctp_association *asoc, uint32_t needed)
{
/* mapping array needs to grow */
uint8_t *new_array1, *new_array2;
uint32_t new_size;
new_size = asoc->mapping_array_size + ((needed+7)/8 + SCTP_MAPPING_ARRAY_INCR);
SCTP_MALLOC(new_array1, uint8_t *, new_size, SCTP_M_MAP);
SCTP_MALLOC(new_array2, uint8_t *, new_size, SCTP_M_MAP);
if ((new_array1 == NULL) || (new_array2 == NULL)) {
/* can't get more, forget it */
SCTP_PRINTF("No memory for expansion of SCTP mapping array %d\n", new_size);
if (new_array1) {
SCTP_FREE(new_array1, SCTP_M_MAP);
}
if (new_array2) {
SCTP_FREE(new_array2, SCTP_M_MAP);
}
return (-1);
}
memset(new_array1, 0, new_size);
memset(new_array2, 0, new_size);
memcpy(new_array1, asoc->mapping_array, asoc->mapping_array_size);
memcpy(new_array2, asoc->nr_mapping_array, asoc->mapping_array_size);
SCTP_FREE(asoc->mapping_array, SCTP_M_MAP);
SCTP_FREE(asoc->nr_mapping_array, SCTP_M_MAP);
asoc->mapping_array = new_array1;
asoc->nr_mapping_array = new_array2;
asoc->mapping_array_size = new_size;
return (0);
}
static void
sctp_iterator_work(struct sctp_iterator *it)
{
#if defined(__FreeBSD__) && !defined(__Userspace__)
struct epoch_tracker et;
#endif
struct sctp_inpcb *tinp;
int iteration_count = 0;
int inp_skip = 0;
int first_in = 1;
#if defined(__FreeBSD__) && !defined(__Userspace__)
NET_EPOCH_ENTER(et);
#endif
SCTP_INP_INFO_RLOCK();
SCTP_ITERATOR_LOCK();
sctp_it_ctl.cur_it = it;
if (it->inp) {
SCTP_INP_RLOCK(it->inp);
SCTP_INP_DECR_REF(it->inp);
}
if (it->inp == NULL) {
/* iterator is complete */
done_with_iterator:
sctp_it_ctl.cur_it = NULL;
SCTP_ITERATOR_UNLOCK();
SCTP_INP_INFO_RUNLOCK();
if (it->function_atend != NULL) {
(*it->function_atend) (it->pointer, it->val);
}
SCTP_FREE(it, SCTP_M_ITER);
#if defined(__FreeBSD__) && !defined(__Userspace__)
NET_EPOCH_EXIT(et);
#endif
return;
}
select_a_new_ep:
if (first_in) {
first_in = 0;
} else {
SCTP_INP_RLOCK(it->inp);
}
while (((it->pcb_flags) &&
((it->inp->sctp_flags & it->pcb_flags) != it->pcb_flags)) ||
((it->pcb_features) &&
((it->inp->sctp_features & it->pcb_features) != it->pcb_features))) {
/* endpoint flags or features don't match, so keep looking */
if (it->iterator_flags & SCTP_ITERATOR_DO_SINGLE_INP) {
SCTP_INP_RUNLOCK(it->inp);
goto done_with_iterator;
}
tinp = it->inp;
it->inp = LIST_NEXT(it->inp, sctp_list);
it->stcb = NULL;
SCTP_INP_RUNLOCK(tinp);
if (it->inp == NULL) {
goto done_with_iterator;
}
SCTP_INP_RLOCK(it->inp);
}
/* now go through each assoc which is in the desired state */
if (it->done_current_ep == 0) {
if (it->function_inp != NULL)
inp_skip = (*it->function_inp)(it->inp, it->pointer, it->val);
it->done_current_ep = 1;
}
if (it->stcb == NULL) {
/* run the per instance function */
it->stcb = LIST_FIRST(&it->inp->sctp_asoc_list);
}
if ((inp_skip) || it->stcb == NULL) {
if (it->function_inp_end != NULL) {
inp_skip = (*it->function_inp_end)(it->inp,
it->pointer,
it->val);
}
SCTP_INP_RUNLOCK(it->inp);
goto no_stcb;
}
while (it->stcb) {
SCTP_TCB_LOCK(it->stcb);
if (it->asoc_state && ((it->stcb->asoc.state & it->asoc_state) != it->asoc_state)) {
/* not in the right state... keep looking */
SCTP_TCB_UNLOCK(it->stcb);
goto next_assoc;
}
/* see if we have limited out the iterator loop */
iteration_count++;
if (iteration_count > SCTP_ITERATOR_MAX_AT_ONCE) {
/* Pause to let others grab the lock */
atomic_add_int(&it->stcb->asoc.refcnt, 1);
SCTP_TCB_UNLOCK(it->stcb);
SCTP_INP_INCR_REF(it->inp);
SCTP_INP_RUNLOCK(it->inp);
SCTP_ITERATOR_UNLOCK();
SCTP_INP_INFO_RUNLOCK();
SCTP_INP_INFO_RLOCK();
SCTP_ITERATOR_LOCK();
if (sctp_it_ctl.iterator_flags) {
/* We won't be staying here */
SCTP_INP_DECR_REF(it->inp);
atomic_add_int(&it->stcb->asoc.refcnt, -1);
#if !(defined(__FreeBSD__) && !defined(__Userspace__))
if (sctp_it_ctl.iterator_flags &
SCTP_ITERATOR_MUST_EXIT) {
goto done_with_iterator;
}
#endif
if (sctp_it_ctl.iterator_flags &
SCTP_ITERATOR_STOP_CUR_IT) {
sctp_it_ctl.iterator_flags &= ~SCTP_ITERATOR_STOP_CUR_IT;
goto done_with_iterator;
}
if (sctp_it_ctl.iterator_flags &
SCTP_ITERATOR_STOP_CUR_INP) {
sctp_it_ctl.iterator_flags &= ~SCTP_ITERATOR_STOP_CUR_INP;
goto no_stcb;
}
/* If we reach here huh? */
SCTP_PRINTF("Unknown it ctl flag %x\n",
sctp_it_ctl.iterator_flags);
sctp_it_ctl.iterator_flags = 0;
}
SCTP_INP_RLOCK(it->inp);
SCTP_INP_DECR_REF(it->inp);
SCTP_TCB_LOCK(it->stcb);
atomic_add_int(&it->stcb->asoc.refcnt, -1);
iteration_count = 0;
}
KASSERT(it->inp == it->stcb->sctp_ep,
("%s: stcb %p does not belong to inp %p, but inp %p",
__func__, it->stcb, it->inp, it->stcb->sctp_ep));
/* run function on this one */
(*it->function_assoc)(it->inp, it->stcb, it->pointer, it->val);
/*
* we lie here, it really needs to have its own type but
* first I must verify that this won't effect things :-0
*/
if (it->no_chunk_output == 0)
sctp_chunk_output(it->inp, it->stcb, SCTP_OUTPUT_FROM_T3, SCTP_SO_NOT_LOCKED);
SCTP_TCB_UNLOCK(it->stcb);
next_assoc:
it->stcb = LIST_NEXT(it->stcb, sctp_tcblist);
if (it->stcb == NULL) {
/* Run last function */
if (it->function_inp_end != NULL) {
inp_skip = (*it->function_inp_end)(it->inp,
it->pointer,
it->val);
}
}
}
SCTP_INP_RUNLOCK(it->inp);
no_stcb:
/* done with all assocs on this endpoint, move on to next endpoint */
it->done_current_ep = 0;
if (it->iterator_flags & SCTP_ITERATOR_DO_SINGLE_INP) {
it->inp = NULL;
} else {
it->inp = LIST_NEXT(it->inp, sctp_list);
}
it->stcb = NULL;
if (it->inp == NULL) {
goto done_with_iterator;
}
goto select_a_new_ep;
}
void
sctp_iterator_worker(void)
{
struct sctp_iterator *it;
/* This function is called with the WQ lock in place */
sctp_it_ctl.iterator_running = 1;
while ((it = TAILQ_FIRST(&sctp_it_ctl.iteratorhead)) != NULL) {
/* now lets work on this one */
TAILQ_REMOVE(&sctp_it_ctl.iteratorhead, it, sctp_nxt_itr);
SCTP_IPI_ITERATOR_WQ_UNLOCK();
#if defined(__FreeBSD__) && !defined(__Userspace__)
CURVNET_SET(it->vn);
#endif
sctp_iterator_work(it);
#if defined(__FreeBSD__) && !defined(__Userspace__)
CURVNET_RESTORE();
#endif
SCTP_IPI_ITERATOR_WQ_LOCK();
#if !defined(__FreeBSD__) && !defined(__Userspace__)
if (sctp_it_ctl.iterator_flags & SCTP_ITERATOR_MUST_EXIT) {
break;
}
#endif
/*sa_ignore FREED_MEMORY*/
}
sctp_it_ctl.iterator_running = 0;
return;
}
static void
sctp_handle_addr_wq(void)
{
/* deal with the ADDR wq from the rtsock calls */
struct sctp_laddr *wi, *nwi;
struct sctp_asconf_iterator *asc;
SCTP_MALLOC(asc, struct sctp_asconf_iterator *,
sizeof(struct sctp_asconf_iterator), SCTP_M_ASC_IT);
if (asc == NULL) {
/* Try later, no memory */
sctp_timer_start(SCTP_TIMER_TYPE_ADDR_WQ,
(struct sctp_inpcb *)NULL,
(struct sctp_tcb *)NULL,
(struct sctp_nets *)NULL);
return;
}
LIST_INIT(&asc->list_of_work);
asc->cnt = 0;
LIST_FOREACH_SAFE(wi, &SCTP_BASE_INFO(addr_wq), sctp_nxt_addr, nwi) {
LIST_REMOVE(wi, sctp_nxt_addr);
LIST_INSERT_HEAD(&asc->list_of_work, wi, sctp_nxt_addr);
asc->cnt++;
}
if (asc->cnt == 0) {
SCTP_FREE(asc, SCTP_M_ASC_IT);
} else {
int ret;
ret = sctp_initiate_iterator(sctp_asconf_iterator_ep,
sctp_asconf_iterator_stcb,
NULL, /* No ep end for boundall */
SCTP_PCB_FLAGS_BOUNDALL,
SCTP_PCB_ANY_FEATURES,
SCTP_ASOC_ANY_STATE,
(void *)asc, 0,
sctp_asconf_iterator_end, NULL, 0);
if (ret) {
SCTP_PRINTF("Failed to initiate iterator for handle_addr_wq\n");
/* Freeing if we are stopping or put back on the addr_wq. */
if (SCTP_BASE_VAR(sctp_pcb_initialized) == 0) {
sctp_asconf_iterator_end(asc, 0);
} else {
LIST_FOREACH(wi, &asc->list_of_work, sctp_nxt_addr) {
LIST_INSERT_HEAD(&SCTP_BASE_INFO(addr_wq), wi, sctp_nxt_addr);
}
SCTP_FREE(asc, SCTP_M_ASC_IT);
}
}
}
}
/*-
* The following table shows which pointers for the inp, stcb, or net are
* stored for each timer after it was started.
*
*|Name |Timer |inp |stcb|net |
*|-----------------------------|-----------------------------|----|----|----|
*|SCTP_TIMER_TYPE_SEND |net->rxt_timer |Yes |Yes |Yes |
*|SCTP_TIMER_TYPE_INIT |net->rxt_timer |Yes |Yes |Yes |
*|SCTP_TIMER_TYPE_RECV |stcb->asoc.dack_timer |Yes |Yes |No |
*|SCTP_TIMER_TYPE_SHUTDOWN |net->rxt_timer |Yes |Yes |Yes |
*|SCTP_TIMER_TYPE_HEARTBEAT |net->hb_timer |Yes |Yes |Yes |
*|SCTP_TIMER_TYPE_COOKIE |net->rxt_timer |Yes |Yes |Yes |
*|SCTP_TIMER_TYPE_NEWCOOKIE |inp->sctp_ep.signature_change|Yes |No |No |
*|SCTP_TIMER_TYPE_PATHMTURAISE |net->pmtu_timer |Yes |Yes |Yes |
*|SCTP_TIMER_TYPE_SHUTDOWNACK |net->rxt_timer |Yes |Yes |Yes |
*|SCTP_TIMER_TYPE_ASCONF |stcb->asoc.asconf_timer |Yes |Yes |Yes |
*|SCTP_TIMER_TYPE_SHUTDOWNGUARD|stcb->asoc.shut_guard_timer |Yes |Yes |No |
*|SCTP_TIMER_TYPE_AUTOCLOSE |stcb->asoc.autoclose_timer |Yes |Yes |No |
*|SCTP_TIMER_TYPE_STRRESET |stcb->asoc.strreset_timer |Yes |Yes |No |
*|SCTP_TIMER_TYPE_INPKILL |inp->sctp_ep.signature_change|Yes |No |No |
*|SCTP_TIMER_TYPE_ASOCKILL |stcb->asoc.strreset_timer |Yes |Yes |No |
*|SCTP_TIMER_TYPE_ADDR_WQ |SCTP_BASE_INFO(addr_wq_timer)|No |No |No |
*|SCTP_TIMER_TYPE_PRIM_DELETED |stcb->asoc.delete_prim_timer |Yes |Yes |No |
*/
void
sctp_timeout_handler(void *t)
{
#if defined(__FreeBSD__) && !defined(__Userspace__)
struct epoch_tracker et;
#endif
struct timeval tv;
struct sctp_inpcb *inp;
struct sctp_tcb *stcb;
struct sctp_nets *net;
struct sctp_timer *tmr;
struct mbuf *op_err;
#if defined(__APPLE__) && !defined(__Userspace__)
struct socket *so;
#endif
#if defined(__Userspace__)
struct socket *upcall_socket = NULL;
#endif
int type;
int i, secret;
bool did_output, released_asoc_reference;
/*
* If inp, stcb or net are not NULL, then references to these were
* added when the timer was started, and must be released before this
* function returns.
*/
tmr = (struct sctp_timer *)t;
inp = (struct sctp_inpcb *)tmr->ep;
stcb = (struct sctp_tcb *)tmr->tcb;
net = (struct sctp_nets *)tmr->net;
#if defined(__FreeBSD__) && !defined(__Userspace__)
CURVNET_SET((struct vnet *)tmr->vnet);
NET_EPOCH_ENTER(et);
#endif
released_asoc_reference = false;
#ifdef SCTP_AUDITING_ENABLED
sctp_audit_log(0xF0, (uint8_t) tmr->type);
sctp_auditing(3, inp, stcb, net);
#endif
/* sanity checks... */
KASSERT(tmr->self == NULL || tmr->self == tmr,
("sctp_timeout_handler: tmr->self corrupted"));
KASSERT(SCTP_IS_TIMER_TYPE_VALID(tmr->type),
("sctp_timeout_handler: invalid timer type %d", tmr->type));
type = tmr->type;
KASSERT(stcb == NULL || stcb->sctp_ep == inp,
("sctp_timeout_handler of type %d: inp = %p, stcb->sctp_ep %p",
type, stcb, stcb->sctp_ep));
tmr->stopped_from = 0xa001;
if ((stcb != NULL) && (stcb->asoc.state == SCTP_STATE_EMPTY)) {
SCTPDBG(SCTP_DEBUG_TIMER2,
"Timer type %d handler exiting due to CLOSED association.\n",
type);
goto out_decr;
}
tmr->stopped_from = 0xa002;
SCTPDBG(SCTP_DEBUG_TIMER2, "Timer type %d goes off.\n", type);
if (!SCTP_OS_TIMER_ACTIVE(&tmr->timer)) {
SCTPDBG(SCTP_DEBUG_TIMER2,
"Timer type %d handler exiting due to not being active.\n",
type);
goto out_decr;
}
tmr->stopped_from = 0xa003;
if (stcb) {
SCTP_TCB_LOCK(stcb);
/*
* Release reference so that association can be freed if
* necessary below.
* This is safe now that we have acquired the lock.
*/
atomic_add_int(&stcb->asoc.refcnt, -1);
released_asoc_reference = true;
if ((type != SCTP_TIMER_TYPE_ASOCKILL) &&
((stcb->asoc.state == SCTP_STATE_EMPTY) ||
(stcb->asoc.state & SCTP_STATE_ABOUT_TO_BE_FREED))) {
SCTPDBG(SCTP_DEBUG_TIMER2,
"Timer type %d handler exiting due to CLOSED association.\n",
type);
goto out;
}
} else if (inp != NULL) {
SCTP_INP_WLOCK(inp);
} else {
SCTP_WQ_ADDR_LOCK();
}
/* Record in stopped_from which timeout occurred. */
tmr->stopped_from = type;
/* mark as being serviced now */
if (SCTP_OS_TIMER_PENDING(&tmr->timer)) {
/*
* Callout has been rescheduled.
*/
goto out;
}
if (!SCTP_OS_TIMER_ACTIVE(&tmr->timer)) {
/*
* Not active, so no action.
*/
goto out;
}
SCTP_OS_TIMER_DEACTIVATE(&tmr->timer);
#if defined(__Userspace__)
if ((stcb != NULL) &&
!(stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) &&
(stcb->sctp_socket != NULL)) {
upcall_socket = stcb->sctp_socket;
SOCK_LOCK(upcall_socket);
soref(upcall_socket);
SOCK_UNLOCK(upcall_socket);
}
#endif
/* call the handler for the appropriate timer type */
switch (type) {
case SCTP_TIMER_TYPE_SEND:
KASSERT(inp != NULL && stcb != NULL && net != NULL,
("timeout of type %d: inp = %p, stcb = %p, net = %p",
type, inp, stcb, net));
SCTP_STAT_INCR(sctps_timodata);
stcb->asoc.timodata++;
stcb->asoc.num_send_timers_up--;
if (stcb->asoc.num_send_timers_up < 0) {
stcb->asoc.num_send_timers_up = 0;
}
SCTP_TCB_LOCK_ASSERT(stcb);
if (sctp_t3rxt_timer(inp, stcb, net)) {
/* no need to unlock on tcb its gone */
goto out_decr;
}
SCTP_TCB_LOCK_ASSERT(stcb);
#ifdef SCTP_AUDITING_ENABLED
sctp_auditing(4, inp, stcb, net);
#endif
sctp_chunk_output(inp, stcb, SCTP_OUTPUT_FROM_T3, SCTP_SO_NOT_LOCKED);
did_output = true;
if ((stcb->asoc.num_send_timers_up == 0) &&
(stcb->asoc.sent_queue_cnt > 0)) {
struct sctp_tmit_chunk *chk;
/*
* Safeguard. If there on some on the sent queue
* somewhere but no timers running something is
* wrong... so we start a timer on the first chunk
* on the send queue on whatever net it is sent to.
*/
TAILQ_FOREACH(chk, &stcb->asoc.sent_queue, sctp_next) {
if (chk->whoTo != NULL) {
break;
}
}
if (chk != NULL) {
sctp_timer_start(SCTP_TIMER_TYPE_SEND, stcb->sctp_ep, stcb, chk->whoTo);
}
}
break;
case SCTP_TIMER_TYPE_INIT:
KASSERT(inp != NULL && stcb != NULL && net != NULL,
("timeout of type %d: inp = %p, stcb = %p, net = %p",
type, inp, stcb, net));
SCTP_STAT_INCR(sctps_timoinit);
stcb->asoc.timoinit++;
if (sctp_t1init_timer(inp, stcb, net)) {
/* no need to unlock on tcb its gone */
goto out_decr;
}
did_output = false;
break;
case SCTP_TIMER_TYPE_RECV:
KASSERT(inp != NULL && stcb != NULL && net == NULL,
("timeout of type %d: inp = %p, stcb = %p, net = %p",
type, inp, stcb, net));
SCTP_STAT_INCR(sctps_timosack);
stcb->asoc.timosack++;
sctp_send_sack(stcb, SCTP_SO_NOT_LOCKED);
#ifdef SCTP_AUDITING_ENABLED
sctp_auditing(4, inp, stcb, NULL);
#endif
sctp_chunk_output(inp, stcb, SCTP_OUTPUT_FROM_SACK_TMR, SCTP_SO_NOT_LOCKED);
did_output = true;
break;
case SCTP_TIMER_TYPE_SHUTDOWN:
KASSERT(inp != NULL && stcb != NULL && net != NULL,
("timeout of type %d: inp = %p, stcb = %p, net = %p",
type, inp, stcb, net));
SCTP_STAT_INCR(sctps_timoshutdown);
stcb->asoc.timoshutdown++;
if (sctp_shutdown_timer(inp, stcb, net)) {
/* no need to unlock on tcb its gone */
goto out_decr;
}
#ifdef SCTP_AUDITING_ENABLED
sctp_auditing(4, inp, stcb, net);
#endif
sctp_chunk_output(inp, stcb, SCTP_OUTPUT_FROM_SHUT_TMR, SCTP_SO_NOT_LOCKED);
did_output = true;
break;
case SCTP_TIMER_TYPE_HEARTBEAT:
KASSERT(inp != NULL && stcb != NULL && net != NULL,
("timeout of type %d: inp = %p, stcb = %p, net = %p",
type, inp, stcb, net));
SCTP_STAT_INCR(sctps_timoheartbeat);
stcb->asoc.timoheartbeat++;
if (sctp_heartbeat_timer(inp, stcb, net)) {
/* no need to unlock on tcb its gone */
goto out_decr;
}
#ifdef SCTP_AUDITING_ENABLED
sctp_auditing(4, inp, stcb, net);
#endif
if (!(net->dest_state & SCTP_ADDR_NOHB)) {
sctp_timer_start(SCTP_TIMER_TYPE_HEARTBEAT, inp, stcb, net);
sctp_chunk_output(inp, stcb, SCTP_OUTPUT_FROM_HB_TMR, SCTP_SO_NOT_LOCKED);
did_output = true;
} else {
did_output = false;
}
break;
case SCTP_TIMER_TYPE_COOKIE:
KASSERT(inp != NULL && stcb != NULL && net != NULL,
("timeout of type %d: inp = %p, stcb = %p, net = %p",
type, inp, stcb, net));
SCTP_STAT_INCR(sctps_timocookie);
stcb->asoc.timocookie++;
if (sctp_cookie_timer(inp, stcb, net)) {
/* no need to unlock on tcb its gone */
goto out_decr;
}
#ifdef SCTP_AUDITING_ENABLED
sctp_auditing(4, inp, stcb, net);
#endif
/*
* We consider T3 and Cookie timer pretty much the same with
* respect to where from in chunk_output.
*/
sctp_chunk_output(inp, stcb, SCTP_OUTPUT_FROM_T3, SCTP_SO_NOT_LOCKED);
did_output = true;
break;
case SCTP_TIMER_TYPE_NEWCOOKIE:
KASSERT(inp != NULL && stcb == NULL && net == NULL,
("timeout of type %d: inp = %p, stcb = %p, net = %p",
type, inp, stcb, net));
SCTP_STAT_INCR(sctps_timosecret);
(void)SCTP_GETTIME_TIMEVAL(&tv);
inp->sctp_ep.time_of_secret_change = tv.tv_sec;
inp->sctp_ep.last_secret_number =
inp->sctp_ep.current_secret_number;
inp->sctp_ep.current_secret_number++;
if (inp->sctp_ep.current_secret_number >=
SCTP_HOW_MANY_SECRETS) {
inp->sctp_ep.current_secret_number = 0;
}
secret = (int)inp->sctp_ep.current_secret_number;
for (i = 0; i < SCTP_NUMBER_OF_SECRETS; i++) {
inp->sctp_ep.secret_key[secret][i] =
sctp_select_initial_TSN(&inp->sctp_ep);
}
sctp_timer_start(SCTP_TIMER_TYPE_NEWCOOKIE, inp, NULL, NULL);
did_output = false;
break;
case SCTP_TIMER_TYPE_PATHMTURAISE:
KASSERT(inp != NULL && stcb != NULL && net != NULL,
("timeout of type %d: inp = %p, stcb = %p, net = %p",
type, inp, stcb, net));
SCTP_STAT_INCR(sctps_timopathmtu);
sctp_pathmtu_timer(inp, stcb, net);
did_output = false;
break;
case SCTP_TIMER_TYPE_SHUTDOWNACK:
KASSERT(inp != NULL && stcb != NULL && net != NULL,
("timeout of type %d: inp = %p, stcb = %p, net = %p",
type, inp, stcb, net));
if (sctp_shutdownack_timer(inp, stcb, net)) {
/* no need to unlock on tcb its gone */
goto out_decr;
}
SCTP_STAT_INCR(sctps_timoshutdownack);
stcb->asoc.timoshutdownack++;
#ifdef SCTP_AUDITING_ENABLED
sctp_auditing(4, inp, stcb, net);
#endif
sctp_chunk_output(inp, stcb, SCTP_OUTPUT_FROM_SHUT_ACK_TMR, SCTP_SO_NOT_LOCKED);
did_output = true;
break;
case SCTP_TIMER_TYPE_ASCONF:
KASSERT(inp != NULL && stcb != NULL && net != NULL,
("timeout of type %d: inp = %p, stcb = %p, net = %p",
type, inp, stcb, net));
SCTP_STAT_INCR(sctps_timoasconf);
if (sctp_asconf_timer(inp, stcb, net)) {
/* no need to unlock on tcb its gone */
goto out_decr;
}
#ifdef SCTP_AUDITING_ENABLED
sctp_auditing(4, inp, stcb, net);
#endif
sctp_chunk_output(inp, stcb, SCTP_OUTPUT_FROM_ASCONF_TMR, SCTP_SO_NOT_LOCKED);
did_output = true;
break;
case SCTP_TIMER_TYPE_SHUTDOWNGUARD:
KASSERT(inp != NULL && stcb != NULL && net == NULL,
("timeout of type %d: inp = %p, stcb = %p, net = %p",
type, inp, stcb, net));
SCTP_STAT_INCR(sctps_timoshutdownguard);
op_err = sctp_generate_cause(SCTP_BASE_SYSCTL(sctp_diag_info_code),
"Shutdown guard timer expired");
sctp_abort_an_association(inp, stcb, op_err, SCTP_SO_NOT_LOCKED);
/* no need to unlock on tcb its gone */
goto out_decr;
case SCTP_TIMER_TYPE_AUTOCLOSE:
KASSERT(inp != NULL && stcb != NULL && net == NULL,
("timeout of type %d: inp = %p, stcb = %p, net = %p",
type, inp, stcb, net));
SCTP_STAT_INCR(sctps_timoautoclose);
sctp_autoclose_timer(inp, stcb);
sctp_chunk_output(inp, stcb, SCTP_OUTPUT_FROM_AUTOCLOSE_TMR, SCTP_SO_NOT_LOCKED);
did_output = true;
break;
case SCTP_TIMER_TYPE_STRRESET:
KASSERT(inp != NULL && stcb != NULL && net == NULL,
("timeout of type %d: inp = %p, stcb = %p, net = %p",
type, inp, stcb, net));
SCTP_STAT_INCR(sctps_timostrmrst);
if (sctp_strreset_timer(inp, stcb)) {
/* no need to unlock on tcb its gone */
goto out_decr;
}
sctp_chunk_output(inp, stcb, SCTP_OUTPUT_FROM_STRRST_TMR, SCTP_SO_NOT_LOCKED);
did_output = true;
break;
case SCTP_TIMER_TYPE_INPKILL:
KASSERT(inp != NULL && stcb == NULL && net == NULL,
("timeout of type %d: inp = %p, stcb = %p, net = %p",
type, inp, stcb, net));
SCTP_STAT_INCR(sctps_timoinpkill);
/*
* special case, take away our increment since WE are the
* killer
*/
sctp_timer_stop(SCTP_TIMER_TYPE_INPKILL, inp, NULL, NULL,
SCTP_FROM_SCTPUTIL + SCTP_LOC_3);
#if defined(__APPLE__) && !defined(__Userspace__)
SCTP_SOCKET_LOCK(SCTP_INP_SO(inp), 1);
#endif
SCTP_INP_DECR_REF(inp);
SCTP_INP_WUNLOCK(inp);
sctp_inpcb_free(inp, SCTP_FREE_SHOULD_USE_ABORT,
SCTP_CALLED_FROM_INPKILL_TIMER);
#if defined(__APPLE__) && !defined(__Userspace__)
SCTP_SOCKET_UNLOCK(SCTP_INP_SO(inp), 1);
#endif
inp = NULL;
goto out_decr;
case SCTP_TIMER_TYPE_ASOCKILL:
KASSERT(inp != NULL && stcb != NULL && net == NULL,
("timeout of type %d: inp = %p, stcb = %p, net = %p",
type, inp, stcb, net));
SCTP_STAT_INCR(sctps_timoassockill);
/* Can we free it yet? */
sctp_timer_stop(SCTP_TIMER_TYPE_ASOCKILL, inp, stcb, NULL,
SCTP_FROM_SCTPUTIL + SCTP_LOC_1);
#if defined(__APPLE__) && !defined(__Userspace__)
so = SCTP_INP_SO(inp);
atomic_add_int(&stcb->asoc.refcnt, 1);
SCTP_TCB_UNLOCK(stcb);
SCTP_SOCKET_LOCK(so, 1);
SCTP_TCB_LOCK(stcb);
atomic_subtract_int(&stcb->asoc.refcnt, 1);
#endif
(void)sctp_free_assoc(inp, stcb, SCTP_NORMAL_PROC,
SCTP_FROM_SCTPUTIL + SCTP_LOC_2);
#if defined(__APPLE__) && !defined(__Userspace__)
SCTP_SOCKET_UNLOCK(so, 1);
#endif
/*
* free asoc, always unlocks (or destroy's) so prevent
* duplicate unlock or unlock of a free mtx :-0
*/
stcb = NULL;
goto out_decr;
case SCTP_TIMER_TYPE_ADDR_WQ:
KASSERT(inp == NULL && stcb == NULL && net == NULL,
("timeout of type %d: inp = %p, stcb = %p, net = %p",
type, inp, stcb, net));
sctp_handle_addr_wq();
did_output = true;
break;
case SCTP_TIMER_TYPE_PRIM_DELETED:
KASSERT(inp != NULL && stcb != NULL && net == NULL,
("timeout of type %d: inp = %p, stcb = %p, net = %p",
type, inp, stcb, net));
SCTP_STAT_INCR(sctps_timodelprim);
sctp_delete_prim_timer(inp, stcb);
did_output = false;
break;
default:
#ifdef INVARIANTS
panic("Unknown timer type %d", type);
#else
goto out;
#endif
}
#ifdef SCTP_AUDITING_ENABLED
sctp_audit_log(0xF1, (uint8_t) type);
if (inp != NULL)
sctp_auditing(5, inp, stcb, net);
#endif
if (did_output && (stcb != NULL)) {
/*
* Now we need to clean up the control chunk chain if an
* ECNE is on it. It must be marked as UNSENT again so next
* call will continue to send it until such time that we get
* a CWR, to remove it. It is, however, less likely that we
* will find a ecn echo on the chain though.
*/
sctp_fix_ecn_echo(&stcb->asoc);
}
out:
if (stcb != NULL) {
SCTP_TCB_UNLOCK(stcb);
} else if (inp != NULL) {
SCTP_INP_WUNLOCK(inp);
} else {
SCTP_WQ_ADDR_UNLOCK();
}
out_decr:
#if defined(__Userspace__)
if (upcall_socket != NULL) {
if ((upcall_socket->so_upcall != NULL) &&
(upcall_socket->so_error != 0)) {
(*upcall_socket->so_upcall)(upcall_socket, upcall_socket->so_upcallarg, M_NOWAIT);
}
ACCEPT_LOCK();
SOCK_LOCK(upcall_socket);
sorele(upcall_socket);
}
#endif
/* These reference counts were incremented in sctp_timer_start(). */
if (inp != NULL) {
SCTP_INP_DECR_REF(inp);
}
if ((stcb != NULL) && !released_asoc_reference) {
atomic_add_int(&stcb->asoc.refcnt, -1);
}
if (net != NULL) {
sctp_free_remote_addr(net);
}
SCTPDBG(SCTP_DEBUG_TIMER2, "Timer type %d handler finished.\n", type);
#if defined(__FreeBSD__) && !defined(__Userspace__)
CURVNET_RESTORE();
NET_EPOCH_EXIT(et);
#endif
}
/*-
* The following table shows which parameters must be provided
* when calling sctp_timer_start(). For parameters not being
* provided, NULL must be used.
*
* |Name |inp |stcb|net |
* |-----------------------------|----|----|----|
* |SCTP_TIMER_TYPE_SEND |Yes |Yes |Yes |
* |SCTP_TIMER_TYPE_INIT |Yes |Yes |Yes |
* |SCTP_TIMER_TYPE_RECV |Yes |Yes |No |
* |SCTP_TIMER_TYPE_SHUTDOWN |Yes |Yes |Yes |
* |SCTP_TIMER_TYPE_HEARTBEAT |Yes |Yes |Yes |
* |SCTP_TIMER_TYPE_COOKIE |Yes |Yes |Yes |
* |SCTP_TIMER_TYPE_NEWCOOKIE |Yes |No |No |
* |SCTP_TIMER_TYPE_PATHMTURAISE |Yes |Yes |Yes |
* |SCTP_TIMER_TYPE_SHUTDOWNACK |Yes |Yes |Yes |
* |SCTP_TIMER_TYPE_ASCONF |Yes |Yes |Yes |
* |SCTP_TIMER_TYPE_SHUTDOWNGUARD|Yes |Yes |No |
* |SCTP_TIMER_TYPE_AUTOCLOSE |Yes |Yes |No |
* |SCTP_TIMER_TYPE_STRRESET |Yes |Yes |Yes |
* |SCTP_TIMER_TYPE_INPKILL |Yes |No |No |
* |SCTP_TIMER_TYPE_ASOCKILL |Yes |Yes |No |
* |SCTP_TIMER_TYPE_ADDR_WQ |No |No |No |
* |SCTP_TIMER_TYPE_PRIM_DELETED |Yes |Yes |No |
*
*/
void
sctp_timer_start(int t_type, struct sctp_inpcb *inp, struct sctp_tcb *stcb,
struct sctp_nets *net)
{
struct sctp_timer *tmr;
uint32_t to_ticks;
uint32_t rndval, jitter;
KASSERT(stcb == NULL || stcb->sctp_ep == inp,
("sctp_timer_start of type %d: inp = %p, stcb->sctp_ep %p",
t_type, stcb, stcb->sctp_ep));
tmr = NULL;
if (stcb != NULL) {
SCTP_TCB_LOCK_ASSERT(stcb);
} else if (inp != NULL) {
SCTP_INP_WLOCK_ASSERT(inp);
} else {
SCTP_WQ_ADDR_LOCK_ASSERT();
}
if (stcb != NULL) {
/* Don't restart timer on association that's about to be killed. */
if ((stcb->asoc.state & SCTP_STATE_ABOUT_TO_BE_FREED) &&
(t_type != SCTP_TIMER_TYPE_ASOCKILL)) {
SCTPDBG(SCTP_DEBUG_TIMER2,
"Timer type %d not started: inp=%p, stcb=%p, net=%p (stcb deleted).\n",
t_type, inp, stcb, net);
return;
}
/* Don't restart timer on net that's been removed. */
if (net != NULL && (net->dest_state & SCTP_ADDR_BEING_DELETED)) {
SCTPDBG(SCTP_DEBUG_TIMER2,
"Timer type %d not started: inp=%p, stcb=%p, net=%p (net deleted).\n",
t_type, inp, stcb, net);
return;
}
}
switch (t_type) {
case SCTP_TIMER_TYPE_SEND:
/* Here we use the RTO timer. */
if ((inp == NULL) || (stcb == NULL) || (net == NULL)) {
#ifdef INVARIANTS
panic("sctp_timer_start of type %d: inp = %p, stcb = %p, net = %p",
t_type, inp, stcb, net);
#else
return;
#endif
}
tmr = &net->rxt_timer;
if (net->RTO == 0) {
to_ticks = sctp_msecs_to_ticks(stcb->asoc.initial_rto);
} else {
to_ticks = sctp_msecs_to_ticks(net->RTO);
}
break;
case SCTP_TIMER_TYPE_INIT:
/*
* Here we use the INIT timer default usually about 1
* second.
*/
if ((inp == NULL) || (stcb == NULL) || (net == NULL)) {
#ifdef INVARIANTS
panic("sctp_timer_start of type %d: inp = %p, stcb = %p, net = %p",
t_type, inp, stcb, net);
#else
return;
#endif
}
tmr = &net->rxt_timer;
if (net->RTO == 0) {
to_ticks = sctp_msecs_to_ticks(stcb->asoc.initial_rto);
} else {
to_ticks = sctp_msecs_to_ticks(net->RTO);
}
break;
case SCTP_TIMER_TYPE_RECV:
/*
* Here we use the Delayed-Ack timer value from the inp,
* ususually about 200ms.
*/
if ((inp == NULL) || (stcb == NULL) || (net != NULL)) {
#ifdef INVARIANTS
panic("sctp_timer_start of type %d: inp = %p, stcb = %p, net = %p",
t_type, inp, stcb, net);
#else
return;
#endif
}
tmr = &stcb->asoc.dack_timer;
to_ticks = sctp_msecs_to_ticks(stcb->asoc.delayed_ack);
break;
case SCTP_TIMER_TYPE_SHUTDOWN:
/* Here we use the RTO of the destination. */
if ((inp == NULL) || (stcb == NULL) || (net == NULL)) {
#ifdef INVARIANTS
panic("sctp_timer_start of type %d: inp = %p, stcb = %p, net = %p",
t_type, inp, stcb, net);
#else
return;
#endif
}
tmr = &net->rxt_timer;
if (net->RTO == 0) {
to_ticks = sctp_msecs_to_ticks(stcb->asoc.initial_rto);
} else {
to_ticks = sctp_msecs_to_ticks(net->RTO);
}
break;
case SCTP_TIMER_TYPE_HEARTBEAT:
/*
* The net is used here so that we can add in the RTO. Even
* though we use a different timer. We also add the HB timer
* PLUS a random jitter.
*/
if ((inp == NULL) || (stcb == NULL) || (net == NULL)) {
#ifdef INVARIANTS
panic("sctp_timer_start of type %d: inp = %p, stcb = %p, net = %p",
t_type, inp, stcb, net);
#else
return;
#endif
}
if ((net->dest_state & SCTP_ADDR_NOHB) &&
!(net->dest_state & SCTP_ADDR_UNCONFIRMED)) {
SCTPDBG(SCTP_DEBUG_TIMER2,
"Timer type %d not started: inp=%p, stcb=%p, net=%p.\n",
t_type, inp, stcb, net);
return;
}
tmr = &net->hb_timer;
if (net->RTO == 0) {
to_ticks = stcb->asoc.initial_rto;
} else {
to_ticks = net->RTO;
}
rndval = sctp_select_initial_TSN(&inp->sctp_ep);
jitter = rndval % to_ticks;
if (to_ticks > 1) {
to_ticks >>= 1;
}
if (jitter < (UINT32_MAX - to_ticks)) {
to_ticks += jitter;
} else {
to_ticks = UINT32_MAX;
}
if (!(net->dest_state & SCTP_ADDR_UNCONFIRMED) &&
!(net->dest_state & SCTP_ADDR_PF)) {
if (net->heart_beat_delay < (UINT32_MAX - to_ticks)) {
to_ticks += net->heart_beat_delay;
} else {
to_ticks = UINT32_MAX;
}
}
/*
* Now we must convert the to_ticks that are now in
* ms to ticks.
*/
to_ticks = sctp_msecs_to_ticks(to_ticks);
break;
case SCTP_TIMER_TYPE_COOKIE:
/*
* Here we can use the RTO timer from the network since one
* RTT was complete. If a retransmission happened then we will
* be using the RTO initial value.
*/
if ((inp == NULL) || (stcb == NULL) || (net == NULL)) {
#ifdef INVARIANTS
panic("sctp_timer_start of type %d: inp = %p, stcb = %p, net = %p",
t_type, inp, stcb, net);
#else
return;
#endif
}
tmr = &net->rxt_timer;
if (net->RTO == 0) {
to_ticks = sctp_msecs_to_ticks(stcb->asoc.initial_rto);
} else {
to_ticks = sctp_msecs_to_ticks(net->RTO);
}
break;
case SCTP_TIMER_TYPE_NEWCOOKIE:
/*
* Nothing needed but the endpoint here ususually about 60
* minutes.
*/
if ((inp == NULL) || (stcb != NULL) || (net != NULL)) {
#ifdef INVARIANTS
panic("sctp_timer_start of type %d: inp = %p, stcb = %p, net = %p",
t_type, inp, stcb, net);
#else
return;
#endif
}
tmr = &inp->sctp_ep.signature_change;
to_ticks = inp->sctp_ep.sctp_timeoutticks[SCTP_TIMER_SIGNATURE];
break;
case SCTP_TIMER_TYPE_PATHMTURAISE:
/*
* Here we use the value found in the EP for PMTUD, ususually
* about 10 minutes.
*/
if ((inp == NULL) || (stcb == NULL) || (net == NULL)) {
#ifdef INVARIANTS
panic("sctp_timer_start of type %d: inp = %p, stcb = %p, net = %p",
t_type, inp, stcb, net);
#else
return;
#endif
}
if (net->dest_state & SCTP_ADDR_NO_PMTUD) {
SCTPDBG(SCTP_DEBUG_TIMER2,
"Timer type %d not started: inp=%p, stcb=%p, net=%p.\n",
t_type, inp, stcb, net);
return;
}
tmr = &net->pmtu_timer;
to_ticks = inp->sctp_ep.sctp_timeoutticks[SCTP_TIMER_PMTU];
break;
case SCTP_TIMER_TYPE_SHUTDOWNACK:
/* Here we use the RTO of the destination. */
if ((inp == NULL) || (stcb == NULL) || (net == NULL)) {
#ifdef INVARIANTS
panic("sctp_timer_start of type %d: inp = %p, stcb = %p, net = %p",
t_type, inp, stcb, net);
#else
return;
#endif
}
tmr = &net->rxt_timer;
if (net->RTO == 0) {
to_ticks = sctp_msecs_to_ticks(stcb->asoc.initial_rto);
} else {
to_ticks = sctp_msecs_to_ticks(net->RTO);
}
break;
case SCTP_TIMER_TYPE_ASCONF:
/*
* Here the timer comes from the stcb but its value is from
* the net's RTO.
*/
if ((inp == NULL) || (stcb == NULL) || (net == NULL)) {
#ifdef INVARIANTS
panic("sctp_timer_start of type %d: inp = %p, stcb = %p, net = %p",
t_type, inp, stcb, net);
#else
return;
#endif
}
tmr = &stcb->asoc.asconf_timer;
if (net->RTO == 0) {
to_ticks = sctp_msecs_to_ticks(stcb->asoc.initial_rto);
} else {
to_ticks = sctp_msecs_to_ticks(net->RTO);
}
break;
case SCTP_TIMER_TYPE_SHUTDOWNGUARD:
/*
* Here we use the endpoints shutdown guard timer usually
* about 3 minutes.
*/
if ((inp == NULL) || (stcb == NULL) || (net != NULL)) {
#ifdef INVARIANTS
panic("sctp_timer_start of type %d: inp = %p, stcb = %p, net = %p",
t_type, inp, stcb, net);
#else
return;
#endif
}
tmr = &stcb->asoc.shut_guard_timer;
if (inp->sctp_ep.sctp_timeoutticks[SCTP_TIMER_MAXSHUTDOWN] == 0) {
if (stcb->asoc.maxrto < UINT32_MAX / 5) {
to_ticks = sctp_msecs_to_ticks(5 * stcb->asoc.maxrto);
} else {
to_ticks = sctp_msecs_to_ticks(UINT32_MAX);
}
} else {
to_ticks = inp->sctp_ep.sctp_timeoutticks[SCTP_TIMER_MAXSHUTDOWN];
}
break;
case SCTP_TIMER_TYPE_AUTOCLOSE:
if ((inp == NULL) || (stcb == NULL) || (net != NULL)) {
#ifdef INVARIANTS
panic("sctp_timer_start of type %d: inp = %p, stcb = %p, net = %p",
t_type, inp, stcb, net);
#else
return;
#endif
}
tmr = &stcb->asoc.autoclose_timer;
to_ticks = stcb->asoc.sctp_autoclose_ticks;
break;
case SCTP_TIMER_TYPE_STRRESET:
/*
* Here the timer comes from the stcb but its value is from
* the net's RTO.
*/
if ((inp == NULL) || (stcb == NULL) || (net == NULL)) {
#ifdef INVARIANTS
panic("sctp_timer_start of type %d: inp = %p, stcb = %p, net = %p",
t_type, inp, stcb, net);
#else
return;
#endif
}
tmr = &stcb->asoc.strreset_timer;
if (net->RTO == 0) {
to_ticks = sctp_msecs_to_ticks(stcb->asoc.initial_rto);
} else {
to_ticks = sctp_msecs_to_ticks(net->RTO);
}
break;
case SCTP_TIMER_TYPE_INPKILL:
/*
* The inp is setup to die. We re-use the signature_chage
* timer since that has stopped and we are in the GONE
* state.
*/
if ((inp == NULL) || (stcb != NULL) || (net != NULL)) {
#ifdef INVARIANTS
panic("sctp_timer_start of type %d: inp = %p, stcb = %p, net = %p",
t_type, inp, stcb, net);
#else
return;
#endif
}
tmr = &inp->sctp_ep.signature_change;
to_ticks = sctp_msecs_to_ticks(SCTP_INP_KILL_TIMEOUT);
break;
case SCTP_TIMER_TYPE_ASOCKILL:
if ((inp == NULL) || (stcb == NULL) || (net != NULL)) {
#ifdef INVARIANTS
panic("sctp_timer_start of type %d: inp = %p, stcb = %p, net = %p",
t_type, inp, stcb, net);
#else
return;
#endif
}
tmr = &stcb->asoc.strreset_timer;
to_ticks = sctp_msecs_to_ticks(SCTP_ASOC_KILL_TIMEOUT);
break;
case SCTP_TIMER_TYPE_ADDR_WQ:
if ((inp != NULL) || (stcb != NULL) || (net != NULL)) {
#ifdef INVARIANTS
panic("sctp_timer_start of type %d: inp = %p, stcb = %p, net = %p",
t_type, inp, stcb, net);
#else
return;
#endif
}
/* Only 1 tick away :-) */
tmr = &SCTP_BASE_INFO(addr_wq_timer);
to_ticks = SCTP_ADDRESS_TICK_DELAY;
break;
case SCTP_TIMER_TYPE_PRIM_DELETED:
if ((inp == NULL) || (stcb == NULL) || (net != NULL)) {
#ifdef INVARIANTS
panic("sctp_timer_start of type %d: inp = %p, stcb = %p, net = %p",
t_type, inp, stcb, net);
#else
return;
#endif
}
tmr = &stcb->asoc.delete_prim_timer;
to_ticks = sctp_msecs_to_ticks(stcb->asoc.initial_rto);
break;
default:
#ifdef INVARIANTS
panic("Unknown timer type %d", t_type);
#else
return;
#endif
}
KASSERT(tmr != NULL, ("tmr is NULL for timer type %d", t_type));
KASSERT(to_ticks > 0, ("to_ticks == 0 for timer type %d", t_type));
if (SCTP_OS_TIMER_PENDING(&tmr->timer)) {
/*
* We do NOT allow you to have it already running. If it is,
* we leave the current one up unchanged.
*/
SCTPDBG(SCTP_DEBUG_TIMER2,
"Timer type %d already running: inp=%p, stcb=%p, net=%p.\n",
t_type, inp, stcb, net);
return;
}
/* At this point we can proceed. */
if (t_type == SCTP_TIMER_TYPE_SEND) {
stcb->asoc.num_send_timers_up++;
}
tmr->stopped_from = 0;
tmr->type = t_type;
tmr->ep = (void *)inp;
tmr->tcb = (void *)stcb;
if (t_type == SCTP_TIMER_TYPE_STRRESET) {
tmr->net = NULL;