/*- * 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.
*/
#ifdefined(_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
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;
/* * 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.
*/
/* * 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;
/* * 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; #ifdefined(__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);
}
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);
}
if (kernel_state & SCTP_STATE_WAS_ABORTED) {
user_state = SCTP_CLOSED;
} elseif (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 initial_tsn, 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; #ifdefined(SCTP_DETAILED_STR_STATS) int j; #endif
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;
/* * 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);
}
SCTP_TCB_LOCK(stcb); 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; #ifdefined(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);
SCTP_TCB_UNLOCK(stcb);
staticvoid
sctp_iterator_work(struct sctp_iterator *it)
{ #ifdefined(__FreeBSD__) && !defined(__Userspace__) struct epoch_tracker et; #endif struct sctp_inpcb *tinp; int iteration_count = 0; int inp_skip = 0; int first_in = 1;
#ifdefined(__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); #ifdefined(__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 != NULL) {
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_subtract_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_subtract_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));
SCTP_INP_RLOCK_ASSERT(it->inp);
SCTP_TCB_LOCK_ASSERT(it->stcb);
/* run function on this one */
(*it->function_assoc)(it->inp, it->stcb, it->pointer, it->val);
SCTP_INP_RLOCK_ASSERT(it->inp);
SCTP_TCB_LOCK_ASSERT(it->stcb);
/* * 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_INP_RLOCK_ASSERT(it->inp);
SCTP_TCB_LOCK_ASSERT(it->stcb);
}
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;
}
/* 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(); #ifdefined(__FreeBSD__) && !defined(__Userspace__)
CURVNET_SET(it->vn); #endif
sctp_iterator_work(it); #ifdefined(__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;
}
staticvoid
sctp_handle_addr_wq(void)
{ /* deal with the ADDR wq from the rtsock calls */ struct sctp_laddr *wi, *nwi; struct sctp_asconf_iterator *asc;
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 |
*/
/* * 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; #ifdefined(__FreeBSD__) && !defined(__Userspace__)
CURVNET_SET((struct vnet *)tmr->vnet);
NET_EPOCH_ENTER(et); #endif
released_asoc_reference = false;
/* 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_subtract_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;
}
} elseif (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);
#ifdefined(__Userspace__) if ((stcb != NULL) &&
((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) == 0) &&
(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 */
tlen = 0; for (at = m; at != NULL; at = SCTP_BUF_NEXT(at)) {
tlen += SCTP_BUF_LEN(at);
} return (tlen);
}
/* * Given an association and starting time of the current RTT period, update * RTO in number of msecs. net should point to the current network. * Return 1, if an RTO update was performed, return 0 if no update was * performed due to invalid starting point.
*/
int
sctp_calculate_rto(struct sctp_tcb *stcb, struct sctp_association *asoc, struct sctp_nets *net, struct timeval *old, int rtt_from_sack)
{ struct timeval now;
uint64_t rtt_us; /* RTT in us */
int32_t rtt; /* RTT in ms */
uint32_t new_rto; int first_measure = 0;
/************************/ /* 1. calculate new RTT */ /************************/ /* get the current time */ if (stcb->asoc.use_precise_time) {
(void)SCTP_GETPTIME_TIMEVAL(&now);
} else {
(void)SCTP_GETTIME_TIMEVAL(&now);
} if ((old->tv_sec > now.tv_sec) ||
((old->tv_sec == now.tv_sec) && (old->tv_usec > now.tv_usec))) { /* The starting point is in the future. */ return (0);
}
timevalsub(&now, old);
rtt_us = (uint64_t)1000000 * (uint64_t)now.tv_sec + (uint64_t)now.tv_usec; if (rtt_us > SCTP_RTO_UPPER_BOUND * 1000) { /* The RTT is larger than a sane value. */ return (0);
} /* store the current RTT in us */
net->rtt = rtt_us; /* compute rtt in ms */
rtt = (int32_t)(net->rtt / 1000); if ((asoc->cc_functions.sctp_rtt_calculated) && (rtt_from_sack == SCTP_RTT_FROM_DATA)) { /* Tell the CC module that a new update has just occurred from a sack */
(*asoc->cc_functions.sctp_rtt_calculated)(stcb, net, &now);
} /* Do we need to determine the lan? We do this only * on sacks i.e. RTT being determined from data not * non-data (HB/INIT->INITACK).
*/ if ((rtt_from_sack == SCTP_RTT_FROM_DATA) &&
(net->lan_type == SCTP_LAN_UNKNOWN)) { if (net->rtt > SCTP_LOCAL_LAN_RTT) {
net->lan_type = SCTP_LAN_INTERNET;
} else {
net->lan_type = SCTP_LAN_LOCAL;
}
}
/***************************/ /* 2. update RTTVAR & SRTT */ /***************************/ /*- * Compute the scaled average lastsa and the * scaled variance lastsv as described in van Jacobson * Paper "Congestion Avoidance and Control", Annex A. * * (net->lastsa >> SCTP_RTT_SHIFT) is the srtt * (net->lastsv >> SCTP_RTT_VAR_SHIFT) is the rttvar
*/ if (net->RTO_measured) {
rtt -= (net->lastsa >> SCTP_RTT_SHIFT);
net->lastsa += rtt; if (rtt < 0) {
rtt = -rtt;
}
rtt -= (net->lastsv >> SCTP_RTT_VAR_SHIFT);
net->lastsv += rtt; if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_RTTVAR_LOGGING_ENABLE) {
rto_logging(net, SCTP_LOG_RTTVAR);
}
} else { /* First RTO measurement */
net->RTO_measured = 1;
first_measure = 1;
net->lastsa = rtt << SCTP_RTT_SHIFT;
net->lastsv = (rtt / 2) << SCTP_RTT_VAR_SHIFT; if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_RTTVAR_LOGGING_ENABLE) {
rto_logging(net, SCTP_LOG_INITIAL_RTT);
}
} if (net->lastsv == 0) {
net->lastsv = SCTP_CLOCK_GRANULARITY;
}
new_rto = (net->lastsa >> SCTP_RTT_SHIFT) + net->lastsv; if ((new_rto > SCTP_SAT_NETWORK_MIN) &&
(stcb->asoc.sat_network_lockout == 0)) {
stcb->asoc.sat_network = 1;
} elseif ((!first_measure) && stcb->asoc.sat_network) {
stcb->asoc.sat_network = 0;
stcb->asoc.sat_network_lockout = 1;
} /* bound it, per C6/C7 in Section 5.3.1 */ if (new_rto < stcb->asoc.minrto) {
new_rto = stcb->asoc.minrto;
} if (new_rto > stcb->asoc.maxrto) {
new_rto = stcb->asoc.maxrto;
}
net->RTO = new_rto; return (1);
}
/* * return a pointer to a contiguous piece of data from the given mbuf chain * starting at 'off' for 'len' bytes. If the desired piece spans more than * one mbuf, a copy is made at 'ptr'. caller must ensure that the buffer size * is >= 'len' returns NULL if there there isn't 'len' bytes in the chain.
*/
caddr_t
sctp_m_getptr(struct mbuf *m, int off, int len, uint8_t * in_ptr)
{
uint32_t count;
uint8_t *ptr;
if (padlen > 3) { return (NULL);
} if (padlen <= M_TRAILINGSPACE(m)) { /* * The easy way. We hope the majority of the time we hit * here :)
*/
m_last = m;
} else { /* Hard way we must grow the mbuf chain */
m_last = sctp_get_mbuf_for_msg(padlen, 0, M_NOWAIT, 1, MT_DATA); if (m_last == NULL) { return (NULL);
}
SCTP_BUF_LEN(m_last) = 0;
SCTP_BUF_NEXT(m_last) = NULL;
SCTP_BUF_NEXT(m) = m_last;
}
dp = mtod(m_last, caddr_t) + SCTP_BUF_LEN(m_last);
SCTP_BUF_LEN(m_last) += padlen;
memset(dp, 0, padlen); return (m_last);
}
struct mbuf *
sctp_pad_lastmbuf(struct mbuf *m, int padval, struct mbuf *last_mbuf)
{ /* find the last mbuf in chain and pad it */ struct mbuf *m_at;
if (last_mbuf != NULL) { return (sctp_add_pad_tombuf(last_mbuf, padval));
} else { for (m_at = m; m_at; m_at = SCTP_BUF_NEXT(m_at)) { if (SCTP_BUF_NEXT(m_at) == NULL) { return (sctp_add_pad_tombuf(m_at, padval));
}
}
} return (NULL);
}
if (sctp_stcb_is_feature_off(stcb->sctp_ep, stcb, SCTP_PCB_FLAGS_RECVSENDFAILEVNT) &&
sctp_stcb_is_feature_off(stcb->sctp_ep, stcb, SCTP_PCB_FLAGS_RECVNSENDFAILEVNT)) { /* event not enabled */ return;
}
if (sctp_stcb_is_feature_on(stcb->sctp_ep, stcb, SCTP_PCB_FLAGS_RECVNSENDFAILEVNT)) {
notifhdr_len = sizeof(struct sctp_send_failed_event);
} else {
notifhdr_len = sizeof(struct sctp_send_failed);
}
m_notify = sctp_get_mbuf_for_msg(notifhdr_len, 0, M_NOWAIT, 1, MT_DATA); if (m_notify == NULL) { /* no space left */ return;
}
SCTP_BUF_LEN(m_notify) = notifhdr_len; if (sctp_stcb_is_feature_on(stcb->sctp_ep, stcb, SCTP_PCB_FLAGS_RECVNSENDFAILEVNT)) {
ssfe = mtod(m_notify, struct sctp_send_failed_event *);
memset(ssfe, 0, notifhdr_len);
ssfe->ssfe_type = SCTP_SEND_FAILED_EVENT;
ssfe->ssfe_flags = SCTP_DATA_UNSENT;
ssfe->ssfe_length = (uint32_t)(notifhdr_len + sp->length);
ssfe->ssfe_error = error; /* not exactly what the user sent in, but should be close :) */
ssfe->ssfe_info.snd_sid = sp->sid; if (sp->some_taken) {
ssfe->ssfe_info.snd_flags = SCTP_DATA_LAST_FRAG;
} else {
ssfe->ssfe_info.snd_flags = SCTP_DATA_NOT_FRAG;
}
ssfe->ssfe_info.snd_ppid = sp->ppid;
ssfe->ssfe_info.snd_context = sp->context;
ssfe->ssfe_info.snd_assoc_id = sctp_get_associd(stcb);
ssfe->ssfe_assoc_id = sctp_get_associd(stcb);
} else {
ssf = mtod(m_notify, struct sctp_send_failed *);
memset(ssf, 0, notifhdr_len);
ssf->ssf_type = SCTP_SEND_FAILED;
ssf->ssf_flags = SCTP_DATA_UNSENT;
ssf->ssf_length = (uint32_t)(notifhdr_len + sp->length);
ssf->ssf_error = error; /* not exactly what the user sent in, but should be close :) */
ssf->ssf_info.sinfo_stream = sp->sid;
ssf->ssf_info.sinfo_ssn = 0; if (sp->some_taken) {
ssf->ssf_info.sinfo_flags = SCTP_DATA_LAST_FRAG;
} else {
ssf->ssf_info.sinfo_flags = SCTP_DATA_NOT_FRAG;
}
ssf->ssf_info.sinfo_ppid = sp->ppid;
ssf->ssf_info.sinfo_context = sp->context;
ssf->ssf_info.sinfo_assoc_id = sctp_get_associd(stcb);
ssf->ssf_assoc_id = sctp_get_associd(stcb);
}
SCTP_BUF_NEXT(m_notify) = sp->data;
/* Steal off the mbuf */
sp->data = NULL; /* * For this case, we check the actual socket buffer, since the assoc * is going away we don't want to overfill the socket buffer for a * non-reader
*/ if (sctp_sbspace_failedmsgs(&stcb->sctp_socket->so_rcv) < SCTP_BUF_LEN(m_notify)) {
sctp_m_freem(m_notify); return;
} /* append to socket */
control = sctp_build_readq_entry(stcb, stcb->asoc.primary_destination,
0, 0, stcb->asoc.context, 0, 0, 0,
m_notify); if (control == NULL) { /* no memory */
sctp_m_freem(m_notify); return;
}
control->length = SCTP_BUF_LEN(m_notify);
control->spec_flags = M_NOTIFICATION; /* not that we need this */
control->tail_mbuf = m_notify;
sctp_add_to_readq(stcb->sctp_ep, stcb, control,
&stcb->sctp_socket->so_rcv, 1,
SCTP_READ_LOCK_HELD, so_locked);
}
/* * For TCP model AND UDP connected sockets we will send an error up * when an SHUTDOWN completes
*/ if ((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) ||
(stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL)) { /* mark socket closed for read/write and wakeup! */ #ifdefined(__APPLE__) && !defined(__Userspace__) struct socket *so;
SCTP_STAT_INCR_COUNTER32(sctps_outoftheblue); /* Generate a TO address for future reference */ if (inp && (inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE)) { if (LIST_EMPTY(&inp->sctp_asoc_list)) { #ifdefined(__APPLE__) && !defined(__Userspace__)
SCTP_SOCKET_LOCK(SCTP_INP_SO(inp), 1); #endif
sctp_inpcb_free(inp, SCTP_FREE_SHOULD_USE_ABORT,
SCTP_CALLED_DIRECTLY_NOCMPSET); #ifdefined(__APPLE__) && !defined(__Userspace__)
SCTP_SOCKET_UNLOCK(SCTP_INP_SO(inp), 1); #endif
}
}
contains_init_chunk = 0;
ch = (struct sctp_chunkhdr *)sctp_m_getptr(m, offset, sizeof(*ch), (uint8_t *) & chunk_buf); while (ch != NULL) {
chk_length = ntohs(ch->chunk_length); if (chk_length < sizeof(*ch)) { /* break to abort land */ break;
} switch (ch->chunk_type) { case SCTP_INIT:
contains_init_chunk = 1; break; case SCTP_PACKET_DROPPED: /* we don't respond to pkt-dropped */ return; case SCTP_ABORT_ASSOCIATION: /* we don't respond with an ABORT to an ABORT */ return; case SCTP_SHUTDOWN_COMPLETE: /* * we ignore it since we are not waiting for it and * peer is gone
*/ return; case SCTP_SHUTDOWN_ACK:
sctp_send_shutdown_complete2(src, dst, sh, #ifdefined(__FreeBSD__) && !defined(__Userspace__)
mflowtype, mflowid, fibnum, #endif
vrf_id, port); return; default: break;
}
offset += SCTP_SIZE32(chk_length);
ch = (struct sctp_chunkhdr *)sctp_m_getptr(m, offset, sizeof(*ch), (uint8_t *) & chunk_buf);
} if ((SCTP_BASE_SYSCTL(sctp_blackhole) == 0) ||
((SCTP_BASE_SYSCTL(sctp_blackhole) == 1) &&
(contains_init_chunk == 0))) {
sctp_send_abort(m, iphlen, src, dst, sh, 0, cause, #ifdefined(__FreeBSD__) && !defined(__Userspace__)
mflowtype, mflowid, fibnum, #endif
vrf_id, port);
}
}
/* * check the inbound datagram to make sure there is not an abort inside it, * if there is return 1, else return 0.
*/ int
sctp_is_there_an_abort_here(struct mbuf *m, int iphlen, uint32_t *vtag)
{ struct sctp_chunkhdr *ch; struct sctp_init_chunk *init_chk, chunk_buf; int offset; unsignedint chk_length;
offset = iphlen + sizeof(struct sctphdr);
ch = (struct sctp_chunkhdr *)sctp_m_getptr(m, offset, sizeof(*ch),
(uint8_t *) & chunk_buf); while (ch != NULL) {
chk_length = ntohs(ch->chunk_length); if (chk_length < sizeof(*ch)) { /* packet is probably corrupt */ break;
} /* we seem to be ok, is it an abort? */ if (ch->chunk_type == SCTP_ABORT_ASSOCIATION) { /* yep, tell them */ return (1);
} if ((ch->chunk_type == SCTP_INITIATION) ||
(ch->chunk_type == SCTP_INITIATION_ACK)) { /* need to update the Vtag */
init_chk = (struct sctp_init_chunk *)sctp_m_getptr(m,
offset, sizeof(struct sctp_init_chunk), (uint8_t *) & chunk_buf); if (init_chk != NULL) {
*vtag = ntohl(init_chk->init.initiate_tag);
}
} /* Nope, move to the next chunk */
offset += SCTP_SIZE32(chk_length);
ch = (struct sctp_chunkhdr *)sctp_m_getptr(m, offset, sizeof(*ch), (uint8_t *) & chunk_buf);
} return (0);
}
/* * currently (2/02), ifa_addr embeds scope_id's and don't have sin6_scope_id * set (i.e. it's 0) so, create this function to compare link local scopes
*/ #ifdef INET6
uint32_t
sctp_is_same_scope(struct sockaddr_in6 *addr1, struct sockaddr_in6 *addr2)
{ #ifdefined(__Userspace__) /*__Userspace__ Returning 1 here always */ #endif #ifdefined(SCTP_EMBEDDED_V6_SCOPE) struct sockaddr_in6 a, b;
/* save copies */
a = *addr1;
b = *addr2;
if (a.sin6_scope_id == 0) #ifdef SCTP_KAME if (sa6_recoverscope(&a)) { #else if (in6_recoverscope(&a, &a.sin6_addr, NULL)) { #endif/* SCTP_KAME */ /* can't get scope, so can't match */ return (0);
} if (b.sin6_scope_id == 0) #ifdef SCTP_KAME if (sa6_recoverscope(&b)) { #else if (in6_recoverscope(&b, &b.sin6_addr, NULL)) { #endif/* SCTP_KAME */ /* can't get scope, so can't match */ return (0);
} if (a.sin6_scope_id != b.sin6_scope_id) return (0); #else if (addr1->sin6_scope_id != addr2->sin6_scope_id) return (0); #endif/* SCTP_EMBEDDED_V6_SCOPE */
return (1);
}
#ifdefined(SCTP_EMBEDDED_V6_SCOPE) /* * returns a sockaddr_in6 with embedded scope recovered and removed
*/ struct sockaddr_in6 *
sctp_recover_scope(struct sockaddr_in6 *addr, struct sockaddr_in6 *store)
{ /* check and strip embedded scope junk */ if (addr->sin6_family == AF_INET6) { if (IN6_IS_SCOPE_LINKLOCAL(&addr->sin6_addr)) { if (addr->sin6_scope_id == 0) {
*store = *addr; #ifdef SCTP_KAME if (!sa6_recoverscope(store)) { #else if (!in6_recoverscope(store, &store->sin6_addr,
NULL)) { #endif/* SCTP_KAME */ /* use the recovered scope */
addr = store;
}
} else { /* else, return the original "to" addr */
in6_clearscope(&addr->sin6_addr);
}
}
} return (addr);
} #endif/* SCTP_EMBEDDED_V6_SCOPE */ #endif
/* * are the two addresses the same? currently a "scopeless" check returns: 1 * if same, 0 if not
*/ int
sctp_cmpaddr(struct sockaddr *sa1, struct sockaddr *sa2)
{
/* must be valid */ if (sa1 == NULL || sa2 == NULL) return (0);
/* must be the same family */ if (sa1->sa_family != sa2->sa_family) return (0);
void
sctp_pull_off_control_to_new_inp(struct sctp_inpcb *old_inp, struct sctp_inpcb *new_inp, struct sctp_tcb *stcb, int waitflags)
{ /* * go through our old INP and pull off any control structures that * belong to stcb and move then to the new inp.
*/ struct socket *old_so, *new_so; struct sctp_queued_to_read *control, *nctl; struct sctp_readhead tmp_queue; struct mbuf *m; #if (defined(__FreeBSD__) || defined(__APPLE__)) && !defined(__Userspace__) int error = 0; #endif
old_so = old_inp->sctp_socket;
new_so = new_inp->sctp_socket;
TAILQ_INIT(&tmp_queue); #if (defined(__FreeBSD__) || defined(__APPLE__)) && !defined(__Userspace__) #ifdefined(__FreeBSD__)
error = SOCK_IO_RECV_LOCK(old_so, waitflags); #else
error = sblock(&old_so->so_rcv, waitflags); #endif if (error) { /* Gak, can't get I/O lock, we have a problem. * data will be left stranded.. and we * don't dare look at it since the * other thread may be reading something. * Oh well, its a screwed up app that does * a peeloff OR a accept while reading * from the main socket... actually its * only the peeloff() case, since I think * read will fail on a listening socket..
*/ return;
} #endif /* lock the socket buffers */
SCTP_INP_READ_LOCK(old_inp);
TAILQ_FOREACH_SAFE(control, &old_inp->read_queue, next, nctl) { /* Pull off all for out target stcb */ if (control->stcb == stcb) { /* remove it we want it */
TAILQ_REMOVE(&old_inp->read_queue, control, next);
TAILQ_INSERT_TAIL(&tmp_queue, control, next);
m = control->data; while (m) { if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_SB_LOGGING_ENABLE) {
sctp_sblog(&old_so->so_rcv, control->do_not_ref_stcb?NULL:stcb, SCTP_LOG_SBFREE,SCTP_BUF_LEN(m));
}
sctp_sbfree(control, stcb, &old_so->so_rcv, m); if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_SB_LOGGING_ENABLE) {
sctp_sblog(&old_so->so_rcv, control->do_not_ref_stcb?NULL:stcb, SCTP_LOG_SBRESULT, 0);
}
m = SCTP_BUF_NEXT(m);
}
}
}
SCTP_INP_READ_UNLOCK(old_inp); /* Remove the recv-lock on the old socket */ #ifdefined(__APPLE__) && !defined(__Userspace__)
sbunlock(&old_so->so_rcv, 1); #endif #ifdefined(__FreeBSD__) && !defined(__Userspace__)
SOCK_IO_RECV_UNLOCK(old_so); #endif /* Now we move them over to the new socket buffer */
SCTP_INP_READ_LOCK(new_inp);
TAILQ_FOREACH_SAFE(control, &tmp_queue, next, nctl) {
TAILQ_INSERT_TAIL(&new_inp->read_queue, control, next);
m = control->data; while (m) { if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_SB_LOGGING_ENABLE) {
sctp_sblog(&new_so->so_rcv, control->do_not_ref_stcb?NULL:stcb, SCTP_LOG_SBALLOC, SCTP_BUF_LEN(m));
}
sctp_sballoc(stcb, &new_so->so_rcv, m); if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_SB_LOGGING_ENABLE) {
sctp_sblog(&new_so->so_rcv, control->do_not_ref_stcb?NULL:stcb, SCTP_LOG_SBRESULT, 0);
}
m = SCTP_BUF_NEXT(m);
}
}
SCTP_INP_READ_UNLOCK(new_inp);
}
void
sctp_add_to_readq(struct sctp_inpcb *inp, struct sctp_tcb *stcb, struct sctp_queued_to_read *control, struct sockbuf *sb, int end, int inp_read_lock_held, int so_locked)
{ /* * Here we must place the control on the end of the socket read * queue AND increment sb_cc so that select will work properly on * read.
*/ struct mbuf *m, *prev = NULL;
if (inp == NULL) { /* Gak, TSNH!! */ #ifdef INVARIANTS
panic("Gak, inp NULL on add_to_readq"); #endif return;
} #ifdefined(__APPLE__) && !defined(__Userspace__) if (so_locked) {
sctp_lock_assert(SCTP_INP_SO(inp));
} else {
sctp_unlock_assert(SCTP_INP_SO(inp));
} #endif if (inp_read_lock_held == SCTP_READ_LOCK_NOT_HELD) {
SCTP_INP_READ_LOCK(inp);
} if (inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_CANT_READ) { if (!control->on_strm_q) {
sctp_free_remote_addr(control->whoFrom); if (control->data) {
sctp_m_freem(control->data);
control->data = NULL;
}
sctp_free_a_readq(stcb, control);
} if (inp_read_lock_held == SCTP_READ_LOCK_NOT_HELD) {
SCTP_INP_READ_UNLOCK(inp);
} return;
} if ((control->spec_flags & M_NOTIFICATION) == 0) {
atomic_add_int(&inp->total_recvs, 1); if (!control->do_not_ref_stcb) {
atomic_add_int(&stcb->total_recvs, 1);
}
}
m = control->data;
control->held_length = 0;
control->length = 0; while (m != NULL) { if (SCTP_BUF_LEN(m) == 0) { /* Skip mbufs with NO length */ if (prev == NULL) { /* First one */
control->data = sctp_m_free(m);
m = control->data;
} else {
SCTP_BUF_NEXT(prev) = sctp_m_free(m);
m = SCTP_BUF_NEXT(prev);
} if (m == NULL) {
control->tail_mbuf = prev;
} continue;
}
prev = m; if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_SB_LOGGING_ENABLE) {
sctp_sblog(sb, control->do_not_ref_stcb?NULL:stcb, SCTP_LOG_SBALLOC, SCTP_BUF_LEN(m));
}
sctp_sballoc(stcb, sb, m); if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_SB_LOGGING_ENABLE) {
sctp_sblog(sb, control->do_not_ref_stcb?NULL:stcb, SCTP_LOG_SBRESULT, 0);
}
atomic_add_int(&control->length, SCTP_BUF_LEN(m));
m = SCTP_BUF_NEXT(m);
} if (prev != NULL) {
control->tail_mbuf = prev;
} else { /* Everything got collapsed out?? */ if (!control->on_strm_q) {
sctp_free_remote_addr(control->whoFrom);
sctp_free_a_readq(stcb, control);
} if (inp_read_lock_held == 0)
SCTP_INP_READ_UNLOCK(inp); return;
} if (end) {
control->end_added = 1;
}
TAILQ_INSERT_TAIL(&inp->read_queue, control, next);
control->on_read_q = 1; #ifdefined(__Userspace__)
sctp_invoke_recv_callback(inp, stcb, control, SCTP_READ_LOCK_HELD); #endif if ((inp != NULL) && (inp->sctp_socket != NULL)) {
sctp_wakeup_the_read_socket(inp, stcb, so_locked);
} if (inp_read_lock_held == SCTP_READ_LOCK_NOT_HELD) {
SCTP_INP_READ_UNLOCK(inp);
}
}
/*************HOLD THIS COMMENT FOR PATCH FILE OF *************ALTERNATE ROUTING CODE
*/
/*************HOLD THIS COMMENT FOR END OF PATCH FILE OF *************ALTERNATE ROUTING CODE
*/
sid = tp1->rec.data.sid;
mid = tp1->rec.data.mid; if (sent || ((tp1->rec.data.rcv_flags & SCTP_DATA_FIRST_FRAG) == 0)) {
stcb->asoc.abandoned_sent[0]++;
stcb->asoc.abandoned_sent[PR_SCTP_POLICY(tp1->flags)]++;
stcb->asoc.strmout[sid].abandoned_sent[0]++; #ifdefined(SCTP_DETAILED_STR_STATS)
stcb->asoc.strmout[sid].abandoned_sent[PR_SCTP_POLICY(tp1->flags)]++; #endif
} else {
stcb->asoc.abandoned_unsent[0]++;
stcb->asoc.abandoned_unsent[PR_SCTP_POLICY(tp1->flags)]++;
stcb->asoc.strmout[sid].abandoned_unsent[0]++; #ifdefined(SCTP_DETAILED_STR_STATS)
stcb->asoc.strmout[sid].abandoned_unsent[PR_SCTP_POLICY(tp1->flags)]++; #endif
} do {
ret_sz += tp1->book_size; if (tp1->data != NULL) { if (tp1->sent < SCTP_DATAGRAM_RESEND) {
sctp_flight_size_decrease(tp1);
sctp_total_flight_decrease(stcb, tp1);
}
sctp_free_bufspace(stcb, &stcb->asoc, tp1, 1);
stcb->asoc.peers_rwnd += tp1->send_size;
stcb->asoc.peers_rwnd += SCTP_BASE_SYSCTL(sctp_peer_chunk_oh); if (sent) {
sctp_ulp_notify(SCTP_NOTIFY_SENT_DG_FAIL, stcb, 0, tp1, so_locked);
} else {
sctp_ulp_notify(SCTP_NOTIFY_UNSENT_DG_FAIL, stcb, 0, tp1, so_locked);
} if (tp1->data) {
sctp_m_freem(tp1->data);
tp1->data = NULL;
}
do_wakeup_routine = 1; if (PR_SCTP_BUF_ENABLED(tp1->flags)) {
stcb->asoc.sent_queue_cnt_removeable--;
}
}
tp1->sent = SCTP_FORWARD_TSN_SKIP; if ((tp1->rec.data.rcv_flags & SCTP_DATA_NOT_FRAG) ==
SCTP_DATA_NOT_FRAG) { /* not frag'ed we ae done */
notdone = 0;
foundeom = 1;
} elseif (tp1->rec.data.rcv_flags & SCTP_DATA_LAST_FRAG) { /* end of frag, we are done */
notdone = 0;
foundeom = 1;
} else { /* * Its a begin or middle piece, we must mark all of * it
*/
notdone = 1;
tp1 = TAILQ_NEXT(tp1, sctp_next);
}
} while (tp1 && notdone); if (foundeom == 0) { /* * The multi-part message was scattered across the send and * sent queue.
*/
TAILQ_FOREACH_SAFE(tp1, &stcb->asoc.send_queue, sctp_next, tp2) { if ((tp1->rec.data.sid != sid) ||
(!SCTP_MID_EQ(stcb->asoc.idata_supported, tp1->rec.data.mid, mid))) { break;
} /* save to chk in case we have some on stream out * queue. If so and we have an un-transmitted one * we don't have to fudge the TSN.
*/
chk = tp1;
ret_sz += tp1->book_size;
sctp_free_bufspace(stcb, &stcb->asoc, tp1, 1); if (sent) {
sctp_ulp_notify(SCTP_NOTIFY_SENT_DG_FAIL, stcb, 0, tp1, so_locked);
} else {
sctp_ulp_notify(SCTP_NOTIFY_UNSENT_DG_FAIL, stcb, 0, tp1, so_locked);
} if (tp1->data) {
sctp_m_freem(tp1->data);
tp1->data = NULL;
} /* No flight involved here book the size to 0 */
tp1->book_size = 0; if (tp1->rec.data.rcv_flags & SCTP_DATA_LAST_FRAG) {
foundeom = 1;
}
do_wakeup_routine = 1;
tp1->sent = SCTP_FORWARD_TSN_SKIP;
TAILQ_REMOVE(&stcb->asoc.send_queue, tp1, sctp_next); /* on to the sent queue so we can wait for it to be passed by. */
TAILQ_INSERT_TAIL(&stcb->asoc.sent_queue, tp1,
sctp_next);
stcb->asoc.send_queue_cnt--;
stcb->asoc.sent_queue_cnt++;
}
} if (foundeom == 0) { /* * Still no eom found. That means there * is stuff left on the stream out queue.. yuck.
*/
strq = &stcb->asoc.strmout[sid];
sp = TAILQ_FIRST(&strq->outqueue); if (sp != NULL) {
sp->discard_rest = 1; /* * We may need to put a chunk on the * queue that holds the TSN that * would have been sent with the LAST * bit.
*/ if (chk == NULL) { /* Yep, we have to */
sctp_alloc_a_chunk(stcb, chk); if (chk == NULL) { /* we are hosed. All we can * do is nothing.. which will * cause an abort if the peer is * paying attention.
*/ goto oh_well;
}
memset(chk, 0, sizeof(*chk));
chk->rec.data.rcv_flags = 0;
chk->sent = SCTP_FORWARD_TSN_SKIP;
chk->asoc = &stcb->asoc; if (stcb->asoc.idata_supported == 0) { if (sp->sinfo_flags & SCTP_UNORDERED) {
chk->rec.data.mid = 0;
} else {
chk->rec.data.mid = strq->next_mid_ordered;
}
} else { if (sp->sinfo_flags & SCTP_UNORDERED) {
chk->rec.data.mid = strq->next_mid_unordered;
} else {
chk->rec.data.mid = strq->next_mid_ordered;
}
}
chk->rec.data.sid = sp->sid;
chk->rec.data.ppid = sp->ppid;
chk->rec.data.context = sp->context;
chk->flags = sp->act_flags;
chk->whoTo = NULL; #ifdefined(__FreeBSD__) && !defined(__Userspace__)
chk->rec.data.tsn = atomic_fetchadd_int(&stcb->asoc.sending_seq, 1); #else
chk->rec.data.tsn = stcb->asoc.sending_seq++; #endif
strq->chunks_on_queues++;
TAILQ_INSERT_TAIL(&stcb->asoc.sent_queue, chk, sctp_next);
stcb->asoc.sent_queue_cnt++;
stcb->asoc.pr_sctp_cnt++;
}
chk->rec.data.rcv_flags |= SCTP_DATA_LAST_FRAG; if (sp->sinfo_flags & SCTP_UNORDERED) {
chk->rec.data.rcv_flags |= SCTP_DATA_UNORDERED;
} if (stcb->asoc.idata_supported == 0) { if ((sp->sinfo_flags & SCTP_UNORDERED) == 0) {
strq->next_mid_ordered++;
}
} else { if (sp->sinfo_flags & SCTP_UNORDERED) {
strq->next_mid_unordered++;
} else {
strq->next_mid_ordered++;
}
}
oh_well: if (sp->data) { /* Pull any data to free up the SB and * allow sender to "add more" while we * will throw away :-)
*/
sctp_free_spbufspace(stcb, &stcb->asoc, sp);
ret_sz += sp->length;
do_wakeup_routine = 1;
sp->some_taken = 1;
sctp_m_freem(sp->data);
sp->data = NULL;
sp->tail_mbuf = NULL;
sp->length = 0;
}
}
} if (do_wakeup_routine) { #ifdefined(__APPLE__) && !defined(__Userspace__) struct socket *so;
so = SCTP_INP_SO(stcb->sctp_ep); if (!so_locked) {
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); if (stcb->asoc.state & SCTP_STATE_CLOSED_SOCKET) { /* assoc was freed while we were unlocked */
SCTP_SOCKET_UNLOCK(so, 1); return (ret_sz);
}
} #endif
sctp_sowwakeup(stcb->sctp_ep, stcb->sctp_socket); #ifdefined(__APPLE__) && !defined(__Userspace__) if (!so_locked) {
SCTP_SOCKET_UNLOCK(so, 1);
} #endif
} return (ret_sz);
}
/* * checks to see if the given address, sa, is one that is currently known by * the kernel note: can't distinguish the same address on multiple interfaces * and doesn't handle multiple addresses with different zone/scope id's note: * ifa_ifwithaddr() compares the entire sockaddr struct
*/ struct sctp_ifa *
sctp_find_ifa_in_ep(struct sctp_inpcb *inp, struct sockaddr *addr, int holds_lock)
{ struct sctp_laddr *laddr;
if (holds_lock == 0) {
SCTP_INP_RLOCK(inp);
}
LIST_FOREACH(laddr, &inp->sctp_addr_list, sctp_nxt_addr) { if (laddr->ifa == NULL) continue; if (addr->sa_family != laddr->ifa->address.sa.sa_family) continue; #ifdef INET if (addr->sa_family == AF_INET) { if (((struct sockaddr_in *)addr)->sin_addr.s_addr ==
laddr->ifa->address.sin.sin_addr.s_addr) { /* found him. */ break;
}
} #endif #ifdef INET6 if (addr->sa_family == AF_INET6) { if (SCTP6_ARE_ADDR_EQUAL((struct sockaddr_in6 *)addr,
&laddr->ifa->address.sin6)) { /* found him. */ break;
}
} #endif #ifdefined(__Userspace__) if (addr->sa_family == AF_CONN) { if (((struct sockaddr_conn *)addr)->sconn_addr == laddr->ifa->address.sconn.sconn_addr) { /* found him. */ break;
}
} #endif
} if (holds_lock == 0) {
SCTP_INP_RUNLOCK(inp);
} if (laddr != NULL) { return (laddr->ifa);
} else { return (NULL);
}
}
vrf = sctp_find_vrf(vrf_id); if (vrf == NULL) { if (holds_lock == 0)
SCTP_IPI_ADDR_RUNLOCK(); return (NULL);
}
hash_of_addr = sctp_get_ifa_hash_val(addr);
hash_head = &vrf->vrf_addr_hash[(hash_of_addr & vrf->vrf_addr_hashmark)]; if (hash_head == NULL) {
SCTP_PRINTF("hash_of_addr:%x mask:%x table:%x - ",
hash_of_addr, (uint32_t)vrf->vrf_addr_hashmark,
(uint32_t)(hash_of_addr & vrf->vrf_addr_hashmark));
sctp_print_address(addr);
SCTP_PRINTF("No such bucket for address\n"); if (holds_lock == 0)
SCTP_IPI_ADDR_RUNLOCK();
return (NULL);
}
LIST_FOREACH(sctp_ifap, hash_head, next_bucket) { if (addr->sa_family != sctp_ifap->address.sa.sa_family) continue; #ifdef INET if (addr->sa_family == AF_INET) { if (((struct sockaddr_in *)addr)->sin_addr.s_addr ==
sctp_ifap->address.sin.sin_addr.s_addr) { /* found him. */ break;
}
} #endif #ifdef INET6 if (addr->sa_family == AF_INET6) { if (SCTP6_ARE_ADDR_EQUAL((struct sockaddr_in6 *)addr,
&sctp_ifap->address.sin6)) { /* found him. */ break;
}
} #endif #ifdefined(__Userspace__) if (addr->sa_family == AF_CONN) { if (((struct sockaddr_conn *)addr)->sconn_addr == sctp_ifap->address.sconn.sconn_addr) { /* found him. */ break;
}
} #endif
} if (holds_lock == 0)
SCTP_IPI_ADDR_RUNLOCK(); return (sctp_ifap);
}
staticvoid
sctp_user_rcvd(struct sctp_tcb *stcb, uint32_t *freed_so_far, int hold_rlock,
uint32_t rwnd_req)
{ /* User pulled some data, do we need a rwnd update? */ #ifdefined(__FreeBSD__) && !defined(__Userspace__) struct epoch_tracker et; #endif int r_unlocked = 0;
uint32_t dif, rwnd; struct socket *so = NULL;
if (stcb == NULL) return;
atomic_add_int(&stcb->asoc.refcnt, 1);
if ((SCTP_GET_STATE(stcb) == SCTP_STATE_SHUTDOWN_ACK_SENT) ||
(stcb->asoc.state & (SCTP_STATE_ABOUT_TO_BE_FREED | SCTP_STATE_SHUTDOWN_RECEIVED))) { /* Pre-check If we are freeing no update */ goto no_lock;
}
SCTP_INP_INCR_REF(stcb->sctp_ep); if ((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) ||
(stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_SOCKET_ALLGONE)) { goto out;
}
so = stcb->sctp_socket; if (so == NULL) { goto out;
}
atomic_add_int(&stcb->freed_by_sorcv_sincelast, *freed_so_far); /* Have you have freed enough to look */
*freed_so_far = 0; /* Yep, its worth a look and the lock overhead */
/* Figure out what the rwnd would be */
rwnd = sctp_calc_rwnd(stcb, &stcb->asoc); if (rwnd >= stcb->asoc.my_last_reported_rwnd) {
dif = rwnd - stcb->asoc.my_last_reported_rwnd;
} else {
dif = 0;
} if (dif >= rwnd_req) { if (hold_rlock) {
SCTP_INP_READ_UNLOCK(stcb->sctp_ep);
r_unlocked = 1;
} if (stcb->asoc.state & SCTP_STATE_ABOUT_TO_BE_FREED) { /* * One last check before we allow the guy possibly * to get in. There is a race, where the guy has not * reached the gate. In that case
*/ goto out;
}
SCTP_TCB_LOCK(stcb); if (stcb->asoc.state & SCTP_STATE_ABOUT_TO_BE_FREED) { /* No reports here */
SCTP_TCB_UNLOCK(stcb); goto out;
}
SCTP_STAT_INCR(sctps_wu_sacks_sent); #ifdefined(__FreeBSD__) && !defined(__Userspace__)
NET_EPOCH_ENTER(et); #endif
sctp_send_sack(stcb, SCTP_SO_LOCKED);
sctp_chunk_output(stcb->sctp_ep, stcb,
SCTP_OUTPUT_FROM_USR_RCVD, SCTP_SO_LOCKED); /* make sure no timer is running */ #ifdefined(__FreeBSD__) && !defined(__Userspace__)
NET_EPOCH_EXIT(et); #endif
sctp_timer_stop(SCTP_TIMER_TYPE_RECV, stcb->sctp_ep, stcb, NULL,
SCTP_FROM_SCTPUTIL + SCTP_LOC_6);
SCTP_TCB_UNLOCK(stcb);
} else { /* Update how much we have pending */
stcb->freed_by_sorcv_sincelast = dif;
}
out: if (so && r_unlocked && hold_rlock) {
SCTP_INP_READ_LOCK(stcb->sctp_ep);
}
restart_nosblocks: if (hold_sblock == 0) {
SOCKBUF_LOCK(&so->so_rcv);
hold_sblock = 1;
} if ((inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) ||
(inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_ALLGONE)) { goto out;
} #if (defined(__FreeBSD__) || defined(_WIN32)) && !defined(__Userspace__) if ((so->so_rcv.sb_state & SBS_CANTRCVMORE) && SCTP_SBAVAIL(&so->so_rcv) == 0) { #else if ((so->so_state & SS_CANTRCVMORE) && SCTP_SBAVAIL(&so->so_rcv) == 0) { #endif if (so->so_error) {
error = so->so_error; if ((in_flags & MSG_PEEK) == 0)
so->so_error = 0; goto out;
} else { if (SCTP_SBAVAIL(&so->so_rcv) == 0) { /* indicate EOF */
error = 0; goto out;
}
}
} if (SCTP_SBAVAIL(&so->so_rcv) <= held_length) { if (so->so_error) {
error = so->so_error; if ((in_flags & MSG_PEEK) == 0) {
so->so_error = 0;
} goto out;
} if ((SCTP_SBAVAIL(&so->so_rcv) == 0) &&
((inp->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) ||
(inp->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL))) { if ((inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) == 0) { /* For active open side clear flags for re-use * passive open is blocked by connect.
*/ if (inp->sctp_flags & SCTP_PCB_FLAGS_WAS_ABORTED) { /* You were aborted, passive side always hits here */
SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTPUTIL, ECONNRESET);
error = ECONNRESET;
}
so->so_state &= ~(SS_ISCONNECTING |
SS_ISDISCONNECTING | #if !(defined(__FreeBSD__) && !defined(__Userspace__))
SS_ISCONFIRMING | #endif
SS_ISCONNECTED); if (error == 0) { if ((inp->sctp_flags & SCTP_PCB_FLAGS_WAS_CONNECTED) == 0) {
SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTPUTIL, ENOTCONN);
error = ENOTCONN;
}
} goto out;
}
} if (block_allowed) { #ifdefined(__FreeBSD__) && !defined(__Userspace__)
error = sbwait(so, SO_RCV); #else
error = sbwait(&so->so_rcv); #endif if (error) { goto out;
}
held_length = 0; goto restart_nosblocks;
} else {
SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTPUTIL, EWOULDBLOCK);
error = EWOULDBLOCK; goto out;
}
} if (hold_sblock == 1) {
SOCKBUF_UNLOCK(&so->so_rcv);
hold_sblock = 0;
} #ifdefined(__APPLE__) && !defined(__Userspace__)
error = sblock(&so->so_rcv, SBLOCKWAIT(in_flags)); #endif /* we possibly have data we can read */ /*sa_ignore FREED_MEMORY*/
control = TAILQ_FIRST(&inp->read_queue); if (control == NULL) { /* This could be happening since * the appender did the increment but as not * yet did the tailq insert onto the read_queue
*/ if (hold_rlock == 0) {
SCTP_INP_READ_LOCK(inp);
}
control = TAILQ_FIRST(&inp->read_queue); if ((control == NULL) && (SCTP_SBAVAIL(&so->so_rcv) > 0)) { #ifdef INVARIANTS
panic("Huh, its non zero and nothing on control?"); #endif
SCTP_SB_CLEAR(so->so_rcv);
}
SCTP_INP_READ_UNLOCK(inp);
hold_rlock = 0; goto restart;
}
if ((control->length == 0) &&
(control->do_not_ref_stcb)) { /* Clean up code for freeing assoc that left behind a pdapi.. * maybe a peer in EEOR that just closed after sending and * never indicated a EOR.
*/ if (hold_rlock == 0) {
hold_rlock = 1;
SCTP_INP_READ_LOCK(inp);
}
control->held_length = 0; if (control->data) { /* Hmm there is data here .. fix */ struct mbuf *m_tmp; int cnt = 0;
m_tmp = control->data; while (m_tmp) {
cnt += SCTP_BUF_LEN(m_tmp); if (SCTP_BUF_NEXT(m_tmp) == NULL) {
control->tail_mbuf = m_tmp;
control->end_added = 1;
}
m_tmp = SCTP_BUF_NEXT(m_tmp);
}
control->length = cnt;
} else { /* remove it */
TAILQ_REMOVE(&inp->read_queue, control, next); /* Add back any hidden data */
sctp_free_remote_addr(control->whoFrom);
sctp_free_a_readq(stcb, control);
} if (hold_rlock) {
hold_rlock = 0;
SCTP_INP_READ_UNLOCK(inp);
} goto restart;
} if ((control->length == 0) &&
(control->end_added == 1)) { /* Do we also need to check for (control->pdapi_aborted == 1)? */ if (hold_rlock == 0) {
hold_rlock = 1;
SCTP_INP_READ_LOCK(inp);
}
TAILQ_REMOVE(&inp->read_queue, control, next); if (control->data) { #ifdef INVARIANTS
panic("control->data not null but control->length == 0"); #else
SCTP_PRINTF("Strange, data left in the control buffer. Cleaning up.\n");
sctp_m_freem(control->data);
control->data = NULL; #endif
} if (control->aux_data) {
sctp_m_free (control->aux_data);
control->aux_data = NULL;
} #ifdef INVARIANTS if (control->on_strm_q) {
panic("About to free ctl:%p so:%p and its in %d",
control, so, control->on_strm_q);
} #endif
sctp_free_remote_addr(control->whoFrom);
sctp_free_a_readq(stcb, control); if (hold_rlock) {
hold_rlock = 0;
SCTP_INP_READ_UNLOCK(inp);
} goto restart;
} if (control->length == 0) { if ((sctp_is_feature_on(inp, SCTP_PCB_FLAGS_FRAG_INTERLEAVE)) &&
(filling_sinfo)) { /* find a more suitable one then this */
ctl = TAILQ_NEXT(control, next); while (ctl) { if ((ctl->stcb != control->stcb) && (ctl->length) &&
(ctl->some_taken ||
(ctl->spec_flags & M_NOTIFICATION) ||
((ctl->do_not_ref_stcb == 0) &&
(ctl->stcb->asoc.strmin[ctl->sinfo_stream].delivery_started == 0)))
) { /*- * If we have a different TCB next, and there is data * present. If we have already taken some (pdapi), OR we can * ref the tcb and no delivery as started on this stream, we * take it. Note we allow a notification on a different * assoc to be delivered..
*/
control = ctl; goto found_one;
} elseif ((sctp_is_feature_on(inp, SCTP_PCB_FLAGS_INTERLEAVE_STRMS)) &&
(ctl->length) &&
((ctl->some_taken) ||
((ctl->do_not_ref_stcb == 0) &&
((ctl->spec_flags & M_NOTIFICATION) == 0) &&
(ctl->stcb->asoc.strmin[ctl->sinfo_stream].delivery_started == 0)))) { /*- * If we have the same tcb, and there is data present, and we * have the strm interleave feature present. Then if we have * taken some (pdapi) or we can refer to tht tcb AND we have * not started a delivery for this stream, we can take it. * Note we do NOT allow a notification on the same assoc to * be delivered.
*/
control = ctl; goto found_one;
}
ctl = TAILQ_NEXT(ctl, next);
}
} /* * if we reach here, not suitable replacement is available * <or> fragment interleave is NOT on. So stuff the sb_cc * into the our held count, and its time to sleep again.
*/
held_length = SCTP_SBAVAIL(&so->so_rcv);
control->held_length = SCTP_SBAVAIL(&so->so_rcv); goto restart;
} /* Clear the held length since there is something to read */
control->held_length = 0;
found_one: /* * If we reach here, control has a some data for us to read off. * Note that stcb COULD be NULL.
*/ if (hold_rlock == 0) {
hold_rlock = 1;
SCTP_INP_READ_LOCK(inp);
}
control->some_taken++;
stcb = control->stcb; if (stcb) { if ((control->do_not_ref_stcb == 0) &&
(stcb->asoc.state & SCTP_STATE_ABOUT_TO_BE_FREED)) { if (freecnt_applied == 0)
stcb = NULL;
} elseif (control->do_not_ref_stcb == 0) { /* you can't free it on me please */ /* * The lock on the socket buffer protects us so the * free code will stop. But since we used the socketbuf * lock and the sender uses the tcb_lock to increment, * we need to use the atomic add to the refcnt
*/ if (freecnt_applied) { #ifdef INVARIANTS
panic("refcnt already incremented"); #else
SCTP_PRINTF("refcnt already incremented?\n"); #endif
} else {
atomic_add_int(&stcb->asoc.refcnt, 1);
freecnt_applied = 1;
} /* * Setup to remember how much we have not yet told * the peer our rwnd has opened up. Note we grab * the value from the tcb from last time. * Note too that sack sending clears this when a sack * is sent, which is fine. Once we hit the rwnd_req, * we then will go to the sctp_user_rcvd() that will * not lock until it KNOWs it MUST send a WUP-SACK.
*/
freed_so_far = (uint32_t)stcb->freed_by_sorcv_sincelast;
stcb->freed_by_sorcv_sincelast = 0;
}
} if (stcb &&
((control->spec_flags & M_NOTIFICATION) == 0) &&
control->do_not_ref_stcb == 0) {
stcb->asoc.strmin[control->sinfo_stream].delivery_started = 1;
}
/* First lets get off the sinfo and sockaddr info */ if ((sinfo != NULL) && (filling_sinfo != 0)) {
sinfo->sinfo_stream = control->sinfo_stream;
sinfo->sinfo_ssn = (uint16_t)control->mid;
sinfo->sinfo_flags = control->sinfo_flags;
sinfo->sinfo_ppid = control->sinfo_ppid;
sinfo->sinfo_context =control->sinfo_context;
sinfo->sinfo_timetolive = control->sinfo_timetolive;
sinfo->sinfo_tsn = control->sinfo_tsn;
sinfo->sinfo_cumtsn = control->sinfo_cumtsn;
sinfo->sinfo_assoc_id = control->sinfo_assoc_id;
nxt = TAILQ_NEXT(control, next); if (sctp_is_feature_on(inp, SCTP_PCB_FLAGS_EXT_RCVINFO) ||
sctp_is_feature_on(inp, SCTP_PCB_FLAGS_RECVNXTINFO)) { struct sctp_extrcvinfo *s_extra;
s_extra = (struct sctp_extrcvinfo *)sinfo; if ((nxt) &&
(nxt->length)) {
s_extra->serinfo_next_flags = SCTP_NEXT_MSG_AVAIL; if (nxt->sinfo_flags & SCTP_UNORDERED) {
s_extra->serinfo_next_flags |= SCTP_NEXT_MSG_IS_UNORDERED;
} if (nxt->spec_flags & M_NOTIFICATION) {
s_extra->serinfo_next_flags |= SCTP_NEXT_MSG_IS_NOTIFICATION;
}
s_extra->serinfo_next_aid = nxt->sinfo_assoc_id;
s_extra->serinfo_next_length = nxt->length;
s_extra->serinfo_next_ppid = nxt->sinfo_ppid;
s_extra->serinfo_next_stream = nxt->sinfo_stream; if (nxt->tail_mbuf != NULL) { if (nxt->end_added) {
s_extra->serinfo_next_flags |= SCTP_NEXT_MSG_ISCOMPLETE;
}
}
} else { /* we explicitly 0 this, since the memcpy got * some other things beyond the older sinfo_ * that is on the control's structure :-D
*/
nxt = NULL;
s_extra->serinfo_next_flags = SCTP_NO_NEXT_MSG;
s_extra->serinfo_next_aid = 0;
s_extra->serinfo_next_length = 0;
s_extra->serinfo_next_ppid = 0;
s_extra->serinfo_next_stream = 0;
}
} /* * update off the real current cum-ack, if we have an stcb.
*/ if ((control->do_not_ref_stcb == 0) && stcb)
sinfo->sinfo_cumtsn = stcb->asoc.cumulative_tsn; /* * mask off the high bits, we keep the actual chunk bits in * there.
*/
sinfo->sinfo_flags &= 0x00ff; if ((control->sinfo_flags >> 8) & SCTP_DATA_UNORDERED) {
sinfo->sinfo_flags |= SCTP_UNORDERED;
}
} #ifdef SCTP_ASOCLOG_OF_TSNS
{ int index, newindex; struct sctp_pcbtsn_rlog *entry; do {
index = inp->readlog_index;
newindex = index + 1; if (newindex >= SCTP_READ_LOG_SIZE) {
newindex = 0;
}
} while (atomic_cmpset_int(&inp->readlog_index, index, newindex) == 0);
entry = &inp->readlog[index];
entry->vtag = control->sinfo_assoc_id;
entry->strm = control->sinfo_stream;
entry->seq = (uint16_t)control->mid;
entry->sz = control->length;
entry->flgs = control->sinfo_flags;
} #endif if ((fromlen > 0) && (from != NULL)) { union sctp_sockstore store;
size_t len;
switch (control->whoFrom->ro._l_addr.sa.sa_family) { #ifdef INET6 case AF_INET6:
len = sizeof(struct sockaddr_in6);
store.sin6 = control->whoFrom->ro._l_addr.sin6;
store.sin6.sin6_port = control->port_from; break; #endif #ifdef INET case AF_INET: #ifdef INET6 if (sctp_is_feature_on(inp, SCTP_PCB_FLAGS_NEEDS_MAPPED_V4)) {
len = sizeof(struct sockaddr_in6);
in6_sin_2_v4mapsin6(&control->whoFrom->ro._l_addr.sin,
&store.sin6);
store.sin6.sin6_port = control->port_from;
} else {
len = sizeof(struct sockaddr_in);
store.sin = control->whoFrom->ro._l_addr.sin;
store.sin.sin_port = control->port_from;
} #else
len = sizeof(struct sockaddr_in);
store.sin = control->whoFrom->ro._l_addr.sin;
store.sin.sin_port = control->port_from; #endif break; #endif #ifdefined(__Userspace__) case AF_CONN:
len = sizeof(struct sockaddr_conn);
store.sconn = control->whoFrom->ro._l_addr.sconn;
store.sconn.sconn_port = control->port_from; break; #endif default:
len = 0; break;
}
memcpy(from, &store, min((size_t)fromlen, len)); #ifdefined(SCTP_EMBEDDED_V6_SCOPE) #ifdef INET6
{ struct sockaddr_in6 lsa6, *from6;
from6 = (struct sockaddr_in6 *)from;
sctp_recover_scope_mac(from6, (&lsa6));
} #endif #endif
} if (hold_rlock) {
SCTP_INP_READ_UNLOCK(inp);
hold_rlock = 0;
} if (hold_sblock) {
SOCKBUF_UNLOCK(&so->so_rcv);
hold_sblock = 0;
} /* now copy out what data we can */ if (mp == NULL) { /* copy out each mbuf in the chain up to length */
get_more_data:
m = control->data; while (m) { /* Move out all we can */ #ifdefined(__APPLE__) && !defined(__Userspace__) #ifdefined(APPLE_LEOPARD)
cp_len = uio->uio_resid; #else
cp_len = uio_resid(uio); #endif #else
cp_len = uio->uio_resid; #endif
my_len = SCTP_BUF_LEN(m); if (cp_len > my_len) { /* not enough in this buf */
cp_len = my_len;
} if (hold_rlock) {
SCTP_INP_READ_UNLOCK(inp);
hold_rlock = 0;
} #ifdefined(__APPLE__) && !defined(__Userspace__)
SCTP_SOCKET_UNLOCK(so, 0); #endif if (cp_len > 0)
error = uiomove(mtod(m, char *), (int)cp_len, uio); #ifdefined(__APPLE__) && !defined(__Userspace__)
SCTP_SOCKET_LOCK(so, 0); #endif /* re-read */ if (inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) { goto release;
}
if ((control->do_not_ref_stcb == 0) && stcb &&
stcb->asoc.state & SCTP_STATE_ABOUT_TO_BE_FREED) {
no_rcv_needed = 1;
} if (error) { /* error we are out of here */ goto release;
}
SCTP_INP_READ_LOCK(inp);
hold_rlock = 1; if (cp_len == SCTP_BUF_LEN(m)) { if ((SCTP_BUF_NEXT(m)== NULL) &&
(control->end_added)) {
out_flags |= MSG_EOR; if ((control->do_not_ref_stcb == 0) &&
(control->stcb != NULL) &&
((control->spec_flags & M_NOTIFICATION) == 0))
control->stcb->asoc.strmin[control->sinfo_stream].delivery_started = 0;
} if (control->spec_flags & M_NOTIFICATION) {
out_flags |= MSG_NOTIFICATION;
} /* we ate up the mbuf */ if (in_flags & MSG_PEEK) { /* just looking */
m = SCTP_BUF_NEXT(m);
copied_so_far += cp_len;
} else { /* dispose of the mbuf */ if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_SB_LOGGING_ENABLE) {
sctp_sblog(&so->so_rcv,
control->do_not_ref_stcb?NULL:stcb, SCTP_LOG_SBFREE, SCTP_BUF_LEN(m));
}
sctp_sbfree(control, stcb, &so->so_rcv, m); if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_SB_LOGGING_ENABLE) {
sctp_sblog(&so->so_rcv,
control->do_not_ref_stcb?NULL:stcb, SCTP_LOG_SBRESULT, 0);
}
copied_so_far += cp_len;
freed_so_far += (uint32_t)cp_len;
freed_so_far += MSIZE;
atomic_subtract_int(&control->length, (int)cp_len);
control->data = sctp_m_free(m);
m = control->data; /* been through it all, must hold sb lock ok to null tail */ if (control->data == NULL) { #ifdef INVARIANTS #ifdefined(__FreeBSD__) && !defined(__Userspace__) if ((control->end_added == 0) ||
(TAILQ_NEXT(control, next) == NULL)) { /* If the end is not added, OR the * next is NOT null we MUST have the lock.
*/ if (mtx_owned(&inp->inp_rdata_mtx) == 0) {
panic("Hmm we don't own the lock?");
}
} #endif #endif
control->tail_mbuf = NULL; #ifdef INVARIANTS if ((control->end_added) && ((out_flags & MSG_EOR) == 0)) {
panic("end_added, nothing left and no MSG_EOR");
} #endif
}
}
} else { /* Do we need to trim the mbuf? */ if (control->spec_flags & M_NOTIFICATION) {
out_flags |= MSG_NOTIFICATION;
} if ((in_flags & MSG_PEEK) == 0) {
SCTP_BUF_RESV_UF(m, cp_len);
SCTP_BUF_LEN(m) -= (int)cp_len; if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_SB_LOGGING_ENABLE) {
sctp_sblog(&so->so_rcv, control->do_not_ref_stcb?NULL:stcb, SCTP_LOG_SBFREE, (int)cp_len);
}
SCTP_SB_DECR(&so->so_rcv, cp_len); if ((control->do_not_ref_stcb == 0) &&
stcb) {
atomic_subtract_int(&stcb->asoc.sb_cc, (int)cp_len);
}
copied_so_far += cp_len;
freed_so_far += (uint32_t)cp_len;
freed_so_far += MSIZE; if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_SB_LOGGING_ENABLE) {
sctp_sblog(&so->so_rcv, control->do_not_ref_stcb?NULL:stcb,
SCTP_LOG_SBRESULT, 0);
}
atomic_subtract_int(&control->length, (int)cp_len);
} else {
copied_so_far += cp_len;
}
} #ifdefined(__APPLE__) && !defined(__Userspace__) #ifdefined(APPLE_LEOPARD) if ((out_flags & MSG_EOR) || (uio->uio_resid == 0)) { #else if ((out_flags & MSG_EOR) || (uio_resid(uio) == 0)) { #endif #else if ((out_flags & MSG_EOR) || (uio->uio_resid == 0)) { #endif break;
} if (((stcb) && (in_flags & MSG_PEEK) == 0) &&
(control->do_not_ref_stcb == 0) &&
(freed_so_far >= rwnd_req)) {
sctp_user_rcvd(stcb, &freed_so_far, hold_rlock, rwnd_req);
}
} /* end while(m) */ /* * At this point we have looked at it all and we either have * a MSG_EOR/or read all the user wants... <OR> * control->length == 0.
*/ if ((out_flags & MSG_EOR) && ((in_flags & MSG_PEEK) == 0)) { /* we are done with this control */ if (control->length == 0) { if (control->data) { #ifdef INVARIANTS
panic("control->data not null at read eor?"); #else
SCTP_PRINTF("Strange, data left in the control buffer .. invariants would panic?\n");
sctp_m_freem(control->data);
control->data = NULL; #endif
}
done_with_control: if (hold_rlock == 0) {
SCTP_INP_READ_LOCK(inp);
hold_rlock = 1;
}
TAILQ_REMOVE(&inp->read_queue, control, next); /* Add back any hidden data */ if (control->held_length) {
held_length = 0;
control->held_length = 0;
wakeup_read_socket = 1;
} if (control->aux_data) {
sctp_m_free (control->aux_data);
control->aux_data = NULL;
}
no_rcv_needed = control->do_not_ref_stcb;
sctp_free_remote_addr(control->whoFrom);
control->data = NULL; #ifdef INVARIANTS if (control->on_strm_q) {
panic("About to free ctl:%p so:%p and its in %d",
control, so, control->on_strm_q);
} #endif
sctp_free_a_readq(stcb, control);
control = NULL; if ((freed_so_far >= rwnd_req) &&
(no_rcv_needed == 0))
sctp_user_rcvd(stcb, &freed_so_far, hold_rlock, rwnd_req);
} else { /* * The user did not read all of this * message, turn off the returned MSG_EOR * since we are leaving more behind on the * control to read.
*/ #ifdef INVARIANTS if (control->end_added &&
(control->data == NULL) &&
(control->tail_mbuf == NULL)) {
panic("Gak, control->length is corrupt?");
} #endif
no_rcv_needed = control->do_not_ref_stcb;
out_flags &= ~MSG_EOR;
}
} if (out_flags & MSG_EOR) { goto release;
} #ifdefined(__APPLE__) && !defined(__Userspace__) #ifdefined(APPLE_LEOPARD) if ((uio->uio_resid == 0) || #else if ((uio_resid(uio) == 0) || #endif #else if ((uio->uio_resid == 0) || #endif
((in_eeor_mode) &&
(copied_so_far >= max(so->so_rcv.sb_lowat, 1)))) { goto release;
} /* * If I hit here the receiver wants more and this message is * NOT done (pd-api). So two questions. Can we block? if not * we are done. Did the user NOT set MSG_WAITALL?
*/ if (block_allowed == 0) { goto release;
} /* * We need to wait for more data a few things: * - We don't release the I/O lock so we don't get someone else * reading. * - We must be sure to account for the case where what is added * is NOT to our control when we wakeup.
*/
/* Do we need to tell the transport a rwnd update might be * needed before we go to sleep?
*/ if (((stcb) && (in_flags & MSG_PEEK) == 0) &&
((freed_so_far >= rwnd_req) &&
(control->do_not_ref_stcb == 0) &&
(no_rcv_needed == 0))) {
sctp_user_rcvd(stcb, &freed_so_far, hold_rlock, rwnd_req);
}
wait_some_more: #if (defined(__FreeBSD__) || defined(_WIN32)) && !defined(__Userspace__) if (so->so_rcv.sb_state & SBS_CANTRCVMORE) { goto release;
} #else if (so->so_state & SS_CANTRCVMORE) { goto release;
} #endif
if (inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) goto release;
if (hold_rlock == 1) {
SCTP_INP_READ_UNLOCK(inp);
hold_rlock = 0;
} if (hold_sblock == 0) {
SOCKBUF_LOCK(&so->so_rcv);
hold_sblock = 1;
} if ((copied_so_far) && (control->length == 0) &&
(sctp_is_feature_on(inp, SCTP_PCB_FLAGS_FRAG_INTERLEAVE))) { goto release;
} #ifdefined(__APPLE__) && !defined(__Userspace__)
sbunlock(&so->so_rcv, 1); #endif if (SCTP_SBAVAIL(&so->so_rcv) <= control->held_length) { #ifdefined(__FreeBSD__) && !defined(__Userspace__)
error = sbwait(so, SO_RCV); #else
error = sbwait(&so->so_rcv); #endif if (error) { #ifdefined(__APPLE__) && !defined(__Userspace__) goto release_unlocked; #else goto release; #endif
}
control->held_length = 0;
} #ifdefined(__APPLE__) && !defined(__Userspace__)
error = sblock(&so->so_rcv, SBLOCKWAIT(in_flags)); #endif if (hold_sblock) {
SOCKBUF_UNLOCK(&so->so_rcv);
hold_sblock = 0;
} if (control->length == 0) { /* still nothing here */ if (control->end_added == 1) { /* he aborted, or is done i.e.did a shutdown */
out_flags |= MSG_EOR; if (control->pdapi_aborted) { if ((control->do_not_ref_stcb == 0) && ((control->spec_flags & M_NOTIFICATION) == 0))
control->stcb->asoc.strmin[control->sinfo_stream].delivery_started = 0;
out_flags |= MSG_TRUNC;
} else { if ((control->do_not_ref_stcb == 0) && ((control->spec_flags & M_NOTIFICATION) == 0))
control->stcb->asoc.strmin[control->sinfo_stream].delivery_started = 0;
} goto done_with_control;
} if (SCTP_SBAVAIL(&so->so_rcv) > held_length) {
control->held_length = SCTP_SBAVAIL(&so->so_rcv);
held_length = 0;
} goto wait_some_more;
} elseif (control->data == NULL) { /* we must re-sync since data * is probably being added
*/
SCTP_INP_READ_LOCK(inp); if ((control->length > 0) && (control->data == NULL)) { /* big trouble.. we have the lock and its corrupt? */ #ifdef INVARIANTS
panic ("Impossible data==NULL length !=0"); #endif
out_flags |= MSG_EOR;
out_flags |= MSG_TRUNC;
control->length = 0;
SCTP_INP_READ_UNLOCK(inp); goto done_with_control;
}
SCTP_INP_READ_UNLOCK(inp); /* We will fall around to get more data */
} goto get_more_data;
} else { /*- * Give caller back the mbuf chain, * store in uio_resid the length
*/
wakeup_read_socket = 0; if ((control->end_added == 0) ||
(TAILQ_NEXT(control, next) == NULL)) { /* Need to get rlock */ if (hold_rlock == 0) {
SCTP_INP_READ_LOCK(inp);
hold_rlock = 1;
}
} if (control->end_added) {
out_flags |= MSG_EOR; if ((control->do_not_ref_stcb == 0) &&
(control->stcb != NULL) &&
((control->spec_flags & M_NOTIFICATION) == 0))
control->stcb->asoc.strmin[control->sinfo_stream].delivery_started = 0;
} if (control->spec_flags & M_NOTIFICATION) {
out_flags |= MSG_NOTIFICATION;
} #ifdefined(__APPLE__) && !defined(__Userspace__) #ifdefined(APPLE_LEOPARD)
uio->uio_resid = control->length; #else
uio_setresid(uio, control->length); #endif #else
uio->uio_resid = control->length; #endif
*mp = control->data;
m = control->data; while (m) { if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_SB_LOGGING_ENABLE) {
sctp_sblog(&so->so_rcv,
control->do_not_ref_stcb?NULL:stcb, SCTP_LOG_SBFREE, SCTP_BUF_LEN(m));
}
sctp_sbfree(control, stcb, &so->so_rcv, m);
freed_so_far += (uint32_t)SCTP_BUF_LEN(m);
freed_so_far += MSIZE; if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_SB_LOGGING_ENABLE) {
sctp_sblog(&so->so_rcv,
control->do_not_ref_stcb?NULL:stcb, SCTP_LOG_SBRESULT, 0);
}
m = SCTP_BUF_NEXT(m);
}
control->data = control->tail_mbuf = NULL;
control->length = 0; if (out_flags & MSG_EOR) { /* Done with this control */ goto done_with_control;
}
}
release: if (hold_rlock == 1) {
SCTP_INP_READ_UNLOCK(inp);
hold_rlock = 0;
} #ifdefined(__Userspace__) if (hold_sblock == 0) {
SOCKBUF_LOCK(&so->so_rcv);
hold_sblock = 1;
} #else if (hold_sblock == 1) {
SOCKBUF_UNLOCK(&so->so_rcv);
hold_sblock = 0;
} #endif #ifdefined(__APPLE__) && !defined(__Userspace__)
sbunlock(&so->so_rcv, 1); #endif
if (freecnt_applied) { /* * The lock on the socket buffer protects us so the free * code will stop. But since we used the socketbuf lock and * the sender uses the tcb_lock to increment, we need to use * the atomic add to the refcnt.
*/ if (stcb == NULL) { #ifdef INVARIANTS
panic("stcb for refcnt has gone NULL?"); goto stage_left; #else goto stage_left; #endif
} /* Save the value back for next time */
stcb->freed_by_sorcv_sincelast = freed_so_far;
atomic_subtract_int(&stcb->asoc.refcnt, 1);
} if (SCTP_BASE_SYSCTL(sctp_logging_level) &SCTP_RECV_RWND_LOGGING_ENABLE) { if (stcb) {
sctp_misc_ints(SCTP_SORECV_DONE,
freed_so_far, #ifdefined(__APPLE__) && !defined(__Userspace__) #ifdefined(APPLE_LEOPARD)
((uio) ? (slen - uio->uio_resid) : slen), #else
((uio) ? (slen - uio_resid(uio)) : slen), #endif #else
(uint32_t)((uio) ? (slen - uio->uio_resid) : slen), #endif
stcb->asoc.my_rwnd,
SCTP_SBAVAIL(&so->so_rcv));
} else {
sctp_misc_ints(SCTP_SORECV_DONE,
freed_so_far, #ifdefined(__APPLE__) && !defined(__Userspace__) #ifdefined(APPLE_LEOPARD)
((uio) ? (slen - uio->uio_resid) : slen), #else
((uio) ? (slen - uio_resid(uio)) : slen), #endif #else
(uint32_t)((uio) ? (slen - uio->uio_resid) : slen), #endif
0,
SCTP_SBAVAIL(&so->so_rcv));
}
}
stage_left: if (wakeup_read_socket) {
sctp_sorwakeup(inp, so);
} return (error);
}
int
sctp_dynamic_set_primary(struct sockaddr *sa, uint32_t vrf_id)
{ /* Given a local address. For all associations * that holds the address, request a peer-set-primary.
*/ struct sctp_ifa *ifa; struct sctp_laddr *wi;
ifa = sctp_find_ifa_by_addr(sa, vrf_id, SCTP_ADDR_NOT_LOCKED); if (ifa == NULL) {
SCTP_LTRACE_ERR_RET(NULL, NULL, NULL, SCTP_FROM_SCTPUTIL, EADDRNOTAVAIL); return (EADDRNOTAVAIL);
} /* Now that we have the ifa we must awaken the * iterator with this message.
*/
wi = SCTP_ZONE_GET(SCTP_BASE_INFO(ipi_zone_laddr), struct sctp_laddr); if (wi == NULL) {
SCTP_LTRACE_ERR_RET(NULL, NULL, NULL, SCTP_FROM_SCTPUTIL, ENOMEM); return (ENOMEM);
} /* Now incr the count and int wi structure */
SCTP_INCR_LADDR_COUNT();
memset(wi, 0, sizeof(*wi));
(void)SCTP_GETTIME_TIMEVAL(&wi->start_time);
wi->ifa = ifa;
wi->action = SCTP_SET_PRIM_ADDR;
atomic_add_int(&ifa->refcount, 1);
/* Now add it to the work queue */
SCTP_WQ_ADDR_LOCK(); /* * Should this really be a tailq? As it is we will process the * newest first :-0
*/
LIST_INSERT_HEAD(&SCTP_BASE_INFO(addr_wq), wi, sctp_nxt_addr);
sctp_timer_start(SCTP_TIMER_TYPE_ADDR_WQ,
(struct sctp_inpcb *)NULL,
(struct sctp_tcb *)NULL,
(struct sctp_nets *)NULL);
SCTP_WQ_ADDR_UNLOCK(); return (0);
}
#ifdefined(__Userspace__) /* no sctp_soreceive for __Userspace__ now */ #endif #if !defined(__Userspace__) int
sctp_soreceive( struct socket *so, struct sockaddr **psa, struct uio *uio, struct mbuf **mp0, struct mbuf **controlp, int *flagsp)
{ int error, fromlen;
uint8_t sockbuf[256]; struct sockaddr *from; struct sctp_extrcvinfo sinfo; int filling_sinfo = 1; int flags; struct sctp_inpcb *inp;
inp = (struct sctp_inpcb *)so->so_pcb; /* pickup the assoc we are reading from */ if (inp == NULL) {
SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTPUTIL, EINVAL); return (EINVAL);
} if ((sctp_is_feature_off(inp, SCTP_PCB_FLAGS_RECVDATAIOEVNT) &&
sctp_is_feature_off(inp, SCTP_PCB_FLAGS_RECVRCVINFO) &&
sctp_is_feature_off(inp, SCTP_PCB_FLAGS_RECVNXTINFO)) ||
(controlp == NULL)) { /* user does not want the sndrcv ctl */
filling_sinfo = 0;
} if (psa) {
from = (struct sockaddr *)sockbuf;
fromlen = sizeof(sockbuf); #ifdef HAVE_SA_LEN
from->sa_len = 0; #endif
} else {
from = NULL;
fromlen = 0;
}
#ifdefined(__APPLE__) && !defined(__Userspace__)
SCTP_SOCKET_LOCK(so, 1); #endif if (filling_sinfo) {
memset(&sinfo, 0, sizeof(struct sctp_extrcvinfo));
} if (flagsp != NULL) {
flags = *flagsp;
} else {
flags = 0;
}
error = sctp_sorecvmsg(so, uio, mp0, from, fromlen, &flags,
(struct sctp_sndrcvinfo *)&sinfo, filling_sinfo); if (flagsp != NULL) {
*flagsp = flags;
} if (controlp != NULL) { /* copy back the sinfo in a CMSG format */ if (filling_sinfo && ((flags & MSG_NOTIFICATION) == 0)) {
*controlp = sctp_build_ctl_nchunk(inp,
(struct sctp_sndrcvinfo *)&sinfo);
} else {
*controlp = NULL;
}
} if (psa) { /* copy back the address info */ #ifdef HAVE_SA_LEN if (from && from->sa_len) { #else if (from) { #endif #if (defined(__FreeBSD__) || defined(_WIN32)) && !defined(__Userspace__)
*psa = sodupsockaddr(from, M_NOWAIT); #else
*psa = dup_sockaddr(from, mp0 == 0); #endif
} else {
*psa = NULL;
}
} #ifdefined(__APPLE__) && !defined(__Userspace__)
SCTP_SOCKET_UNLOCK(so, 1); #endif return (error);
}
#ifdefined(_WIN32) && !defined(__Userspace__) /* * General routine to allocate a hash table with control of memory flags. * is in 7.0 and beyond for sure :-)
*/ void *
sctp_hashinit_flags(int elements, struct malloc_type *type,
u_long *hashmask, int flags)
{ long hashsize;
LIST_HEAD(generic, generic) *hashtbl; int i;
if (elements <= 0) { #ifdef INVARIANTS
panic("hashinit: bad elements"); #else
SCTP_PRINTF("hashinit: bad elements?");
elements = 1; #endif
} for (hashsize = 1; hashsize <= elements; hashsize <<= 1) continue;
hashsize >>= 1; if (flags & HASH_WAITOK)
hashtbl = malloc((u_long)hashsize * sizeof(*hashtbl), type, M_WAITOK); elseif (flags & HASH_NOWAIT)
hashtbl = malloc((u_long)hashsize * sizeof(*hashtbl), type, M_NOWAIT); else { #ifdef INVARIANTS
panic("flag incorrect in hashinit_flags"); #else return (NULL); #endif
}
/* no memory? */ if (hashtbl == NULL) return (NULL);
for (i = 0; i < hashsize; i++)
LIST_INIT(&hashtbl[i]);
*hashmask = hashsize - 1; return (hashtbl);
} #endif #else/* __Userspace__ ifdef above sctp_soreceive */ /* * __Userspace__ Defining sctp_hashinit_flags() and sctp_hashdestroy() for userland. * NOTE: We don't want multiple definitions here. So sctp_hashinit_flags() above for *__FreeBSD__ must be excluded. *
*/
void *
sctp_hashinit_flags(int elements, struct malloc_type *type,
u_long *hashmask, int flags)
{ long hashsize;
LIST_HEAD(generic, generic) *hashtbl; int i;
if (elements <= 0) {
SCTP_PRINTF("hashinit: bad elements?"); #ifdef INVARIANTS return (NULL); #else
elements = 1; #endif
} for (hashsize = 1; hashsize <= elements; hashsize <<= 1) continue;
hashsize >>= 1; /*cannot use MALLOC here because it has to be declared or defined
using MALLOC_DECLARE or MALLOC_DEFINE first. */ if (flags & HASH_WAITOK)
hashtbl = malloc((u_long)hashsize * sizeof(*hashtbl)); elseif (flags & HASH_NOWAIT)
hashtbl = malloc((u_long)hashsize * sizeof(*hashtbl)); else { #ifdef INVARIANTS
SCTP_PRINTF("flag incorrect in hashinit_flags.\n"); #endif return (NULL);
}
/* no memory? */ if (hashtbl == NULL) return (NULL);
for (i = 0; i < hashsize; i++)
LIST_INIT(&hashtbl[i]);
*hashmask = hashsize - 1; return (hashtbl);
}
at = 0;
sa = addr;
*num_v6 = *num_v4 = 0; /* account and validate addresses */ if (totaddr == 0) { return (EINVAL);
} for (i = 0; i < totaddr; i++) { if (at + sizeof(struct sockaddr) > limit) { return (EINVAL);
} switch (sa->sa_family) { #ifdef INET case AF_INET:
incr = (unsignedint)sizeof(struct sockaddr_in); #ifdef HAVE_SA_LEN if (sa->sa_len != incr) { return (EINVAL);
} #endif
(*num_v4) += 1; break; #endif #ifdef INET6 case AF_INET6:
{ struct sockaddr_in6 *sin6;
incr = (unsignedint)sizeof(struct sockaddr_in6); #ifdef HAVE_SA_LEN if (sa->sa_len != incr) { return (EINVAL);
} #endif
sin6 = (struct sockaddr_in6 *)sa; if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { /* Must be non-mapped for connectx */ return (EINVAL);
}
(*num_v6) += 1; break;
} #endif default: return (EINVAL);
} if ((at + incr) > limit) { return (EINVAL);
}
SCTP_INP_INCR_REF(inp);
stcb = sctp_findassociation_ep_addr(&inp, sa, NULL, NULL, NULL); if (stcb != NULL) {
SCTP_TCB_UNLOCK(stcb); return (EALREADY);
} else {
SCTP_INP_DECR_REF(inp);
}
at += incr;
sa = (struct sockaddr *)((caddr_t)sa + incr);
} return (0);
}
/* * sctp_bindx(ADD) for one address. * assumes all arguments are valid/checked by caller.
*/ void
sctp_bindx_add_address(struct socket *so, struct sctp_inpcb *inp, struct sockaddr *sa, uint32_t vrf_id, int *error, void *p)
{ #ifdefined(INET) && defined(INET6) struct sockaddr_in sin; #endif #ifdef INET6 struct sockaddr_in6 *sin6; #endif #ifdef INET struct sockaddr_in *sinp; #endif struct sockaddr *addr_to_use; struct sctp_inpcb *lep; #ifdef SCTP_MVRF int i; #endif
uint16_t port;
/* see if we're bound all already! */ if (inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) {
SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTPUTIL, EINVAL);
*error = EINVAL; return;
} #ifdef SCTP_MVRF /* Is the VRF one we have */ for (i = 0; i < inp->num_vrfs; i++) { if (vrf_id == inp->m_vrf_ids[i]) { break;
}
} if (i == inp->num_vrfs) {
SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTPUTIL, EINVAL);
*error = EINVAL; return;
} #endif switch (sa->sa_family) { #ifdef INET6 case AF_INET6: #ifdef HAVE_SA_LEN if (sa->sa_len != sizeof(struct sockaddr_in6)) {
SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTPUTIL, EINVAL);
*error = EINVAL; return;
} #endif if ((inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) == 0) { /* can only bind v6 on PF_INET6 sockets */
SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTPUTIL, EINVAL);
*error = EINVAL; return;
}
sin6 = (struct sockaddr_in6 *)sa;
port = sin6->sin6_port; #ifdef INET if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { if ((inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) &&
SCTP_IPV6_V6ONLY(inp)) { /* can't bind v4-mapped on PF_INET sockets */
SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTPUTIL, EINVAL);
*error = EINVAL; return;
}
in6_sin6_2_sin(&sin, sin6);
addr_to_use = (struct sockaddr *)&sin;
} else {
addr_to_use = sa;
} #else
addr_to_use = sa; #endif break; #endif #ifdef INET case AF_INET: #ifdef HAVE_SA_LEN if (sa->sa_len != sizeof(struct sockaddr_in)) {
SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTPUTIL, EINVAL);
*error = EINVAL; return;
} #endif if ((inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) &&
SCTP_IPV6_V6ONLY(inp)) { /* can't bind v4 on PF_INET sockets */
SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTPUTIL, EINVAL);
*error = EINVAL; return;
}
sinp = (struct sockaddr_in *)sa;
port = sinp->sin_port;
addr_to_use = sa; break; #endif default:
SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTPUTIL, EINVAL);
*error = EINVAL; return;
} if (inp->sctp_flags & SCTP_PCB_FLAGS_UNBOUND) { #if !(defined(_WIN32) || defined(__Userspace__)) if (p == NULL) { /* Can't get proc for Net/Open BSD */
SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTPUTIL, EINVAL);
*error = EINVAL; return;
} #endif
*error = sctp_inpcb_bind(so, addr_to_use, NULL, p); return;
} /* Validate the incoming port. */ if ((port != 0) && (port != inp->sctp_lport)) {
SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTPUTIL, EINVAL);
*error = EINVAL; return;
}
lep = sctp_pcb_findep(addr_to_use, 1, 0, vrf_id); if (lep == NULL) { /* add the address */
*error = sctp_addr_mgmt_ep_sa(inp, addr_to_use,
SCTP_ADD_IP_ADDRESS, vrf_id);
} else { if (lep != inp) {
*error = EADDRINUSE;
}
SCTP_INP_DECR_REF(lep);
}
}
/* * sctp_bindx(DELETE) for one address. * assumes all arguments are valid/checked by caller.
*/ void
sctp_bindx_delete_address(struct sctp_inpcb *inp, struct sockaddr *sa, uint32_t vrf_id, int *error)
{ struct sockaddr *addr_to_use; #ifdefined(INET) && defined(INET6) struct sockaddr_in6 *sin6; struct sockaddr_in sin; #endif #ifdef SCTP_MVRF int i; #endif
/* see if we're bound all already! */ if (inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) {
SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTPUTIL, EINVAL);
*error = EINVAL; return;
} #ifdef SCTP_MVRF /* Is the VRF one we have */ for (i = 0; i < inp->num_vrfs; i++) { if (vrf_id == inp->m_vrf_ids[i]) { break;
}
} if (i == inp->num_vrfs) {
SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTPUTIL, EINVAL);
*error = EINVAL; return;
} #endif switch (sa->sa_family) { #ifdef INET6 case AF_INET6: #ifdef HAVE_SA_LEN if (sa->sa_len != sizeof(struct sockaddr_in6)) {
SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTPUTIL, EINVAL);
*error = EINVAL; return;
} #endif if ((inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) == 0) { /* can only bind v6 on PF_INET6 sockets */
SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTPUTIL, EINVAL);
*error = EINVAL; return;
} #ifdef INET
sin6 = (struct sockaddr_in6 *)sa; if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { if ((inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) &&
SCTP_IPV6_V6ONLY(inp)) { /* can't bind mapped-v4 on PF_INET sockets */
SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTPUTIL, EINVAL);
*error = EINVAL; return;
}
in6_sin6_2_sin(&sin, sin6);
addr_to_use = (struct sockaddr *)&sin;
} else {
addr_to_use = sa;
} #else
addr_to_use = sa; #endif break; #endif #ifdef INET case AF_INET: #ifdef HAVE_SA_LEN if (sa->sa_len != sizeof(struct sockaddr_in)) {
SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTPUTIL, EINVAL);
*error = EINVAL; return;
} #endif if ((inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) &&
SCTP_IPV6_V6ONLY(inp)) { /* can't bind v4 on PF_INET sockets */
SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTPUTIL, EINVAL);
*error = EINVAL; return;
}
addr_to_use = sa; break; #endif default:
SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTPUTIL, EINVAL);
*error = EINVAL; return;
} /* No lock required mgmt_ep_sa does its own locking. */
*error = sctp_addr_mgmt_ep_sa(inp, addr_to_use, SCTP_DEL_IP_ADDRESS,
vrf_id);
}
/* * returns the valid local address count for an assoc, taking into account * all scoping rules
*/ int
sctp_local_addr_count(struct sctp_tcb *stcb)
{ int loopback_scope; #ifdefined(INET) int ipv4_local_scope, ipv4_addr_legal; #endif #ifdefined(INET6) int local_scope, site_scope, ipv6_addr_legal; #endif #ifdefined(__Userspace__) int conn_addr_legal; #endif struct sctp_vrf *vrf; struct sctp_ifn *sctp_ifn; struct sctp_ifa *sctp_ifa; int count = 0;
/* Turn on all the appropriate scopes */
loopback_scope = stcb->asoc.scope.loopback_scope; #ifdefined(INET)
ipv4_local_scope = stcb->asoc.scope.ipv4_local_scope;
ipv4_addr_legal = stcb->asoc.scope.ipv4_addr_legal; #endif #ifdefined(INET6)
local_scope = stcb->asoc.scope.local_scope;
site_scope = stcb->asoc.scope.site_scope;
ipv6_addr_legal = stcb->asoc.scope.ipv6_addr_legal; #endif #ifdefined(__Userspace__)
conn_addr_legal = stcb->asoc.scope.conn_addr_legal; #endif
SCTP_IPI_ADDR_RLOCK();
vrf = sctp_find_vrf(stcb->asoc.vrf_id); if (vrf == NULL) { /* no vrf, no addresses */
SCTP_IPI_ADDR_RUNLOCK(); return (0);
}
if (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) { /* * bound all case: go through all ifns on the vrf
*/
LIST_FOREACH(sctp_ifn, &vrf->ifnlist, next_ifn) { if ((loopback_scope == 0) &&
SCTP_IFN_IS_IFT_LOOP(sctp_ifn)) { continue;
}
LIST_FOREACH(sctp_ifa, &sctp_ifn->ifalist, next_ifa) { if (sctp_is_addr_restricted(stcb, sctp_ifa)) continue; switch (sctp_ifa->address.sa.sa_family) { #ifdef INET case AF_INET: if (ipv4_addr_legal) { struct sockaddr_in *sin;
sin = &sctp_ifa->address.sin; if (sin->sin_addr.s_addr == 0) { /* skip unspecified addrs */ continue;
} #ifdefined(__FreeBSD__) && !defined(__Userspace__) if (prison_check_ip4(stcb->sctp_ep->ip_inp.inp.inp_cred,
&sin->sin_addr) != 0) { continue;
} #endif if ((ipv4_local_scope == 0) &&
(IN4_ISPRIVATE_ADDRESS(&sin->sin_addr))) { continue;
} /* count this one */
count++;
} else { continue;
} break; #endif #ifdef INET6 case AF_INET6: if (ipv6_addr_legal) { struct sockaddr_in6 *sin6;
#ifdefined(SCTP_EMBEDDED_V6_SCOPE) && !defined(SCTP_KAME) struct sockaddr_in6 lsa6; #endif
sin6 = &sctp_ifa->address.sin6; if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { continue;
} #ifdefined(__FreeBSD__) && !defined(__Userspace__) if (prison_check_ip6(stcb->sctp_ep->ip_inp.inp.inp_cred,
&sin6->sin6_addr) != 0) { continue;
} #endif if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) { if (local_scope == 0) continue; #ifdefined(SCTP_EMBEDDED_V6_SCOPE) if (sin6->sin6_scope_id == 0) { #ifdef SCTP_KAME if (sa6_recoverscope(sin6) != 0) /* * bad link * local * address
*/ continue; #else
lsa6 = *sin6; if (in6_recoverscope(&lsa6,
&lsa6.sin6_addr,
NULL)) /* * bad link * local * address
*/ continue;
sin6 = &lsa6; #endif/* SCTP_KAME */
} #endif/* SCTP_EMBEDDED_V6_SCOPE */
} if ((site_scope == 0) &&
(IN6_IS_ADDR_SITELOCAL(&sin6->sin6_addr))) { continue;
} /* count this one */
count++;
} break; #endif #ifdefined(__Userspace__) case AF_CONN: if (conn_addr_legal) {
count++;
} break; #endif default: /* TSNH */ break;
}
}
}
} else { /* * subset bound case
*/ struct sctp_laddr *laddr;
LIST_FOREACH(laddr, &stcb->sctp_ep->sctp_addr_list,
sctp_nxt_addr) { if (sctp_is_addr_restricted(stcb, laddr->ifa)) { continue;
} /* count this one */
count++;
}
}
SCTP_IPI_ADDR_RUNLOCK(); return (count);
}
if ((m->m_flags & M_PKTHDR) == 0) { /* Can't handle one that is not a pkt hdr */ goto out;
} /* Pull the src port */
iph = mtod(m, struct ip *);
uhdr = (struct udphdr *)((caddr_t)iph + off);
port = uhdr->uh_sport; /* Split out the mbuf chain. Leave the * IP header in m, place the * rest in the sp.
*/
sp = m_split(m, off, M_NOWAIT); if (sp == NULL) { /* Gak, drop packet, we can't do a split */ goto out;
} if (sp->m_pkthdr.len < sizeof(struct udphdr) + sizeof(struct sctphdr)) { /* Gak, packet can't have an SCTP header in it - too small */
m_freem(sp); goto out;
} /* Now pull up the UDP header and SCTP header together */
sp = m_pullup(sp, sizeof(struct udphdr) + sizeof(struct sctphdr)); if (sp == NULL) { /* Gak pullup failed */ goto out;
} /* Trim out the UDP header */
m_adj(sp, sizeof(struct udphdr));
/* Now reconstruct the mbuf chain */ for (last = m; last->m_next; last = last->m_next);
last->m_next = sp;
m->m_pkthdr.len += sp->m_pkthdr.len; /* * The CSUM_DATA_VALID flags indicates that the HW checked the * UDP checksum and it was valid. * Since CSUM_DATA_VALID == CSUM_SCTP_VALID this would imply that * the HW also verified the SCTP checksum. Therefore, clear the bit.
*/
SCTPDBG(SCTP_DEBUG_CRCOFFLOAD, "sctp_recv_udp_tunneled_packet(): Packet of length %d received on %s with csum_flags 0x%b.\n",
m->m_pkthdr.len,
if_name(m->m_pkthdr.rcvif),
(int)m->m_pkthdr.csum_flags, CSUM_BITS);
m->m_pkthdr.csum_flags &= ~CSUM_DATA_VALID;
iph = mtod(m, struct ip *); switch (iph->ip_v) { #ifdef INET case IPVERSION:
iph->ip_len = htons(ntohs(iph->ip_len) - sizeof(struct udphdr));
sctp_input_with_port(m, off, port); break; #endif #ifdef INET6 case IPV6_VERSION >> 4:
ip6 = mtod(m, struct ip6_hdr *);
ip6->ip6_plen = htons(ntohs(ip6->ip6_plen) - sizeof(struct udphdr));
sctp6_input_with_port(&m, &off, port); break; #endif default: goto out; break;
} return (true);
out:
m_freem(m);
/* * XXX: We assume that when IPV6 is non NULL, M and OFF are * valid.
*/ if (ip6cp->ip6c_m == NULL) { return;
} /* Check if we can safely examine the ports and the * verification tag of the SCTP common header.
*/ if (ip6cp->ip6c_m->m_pkthdr.len <
ip6cp->ip6c_off + sizeof(struct udphdr)+ offsetof(struct sctphdr, checksum)) { return;
} /* Copy out the UDP header. */
memset(&udp, 0, sizeof(struct udphdr));
m_copydata(ip6cp->ip6c_m,
ip6cp->ip6c_off, sizeof(struct udphdr),
(caddr_t)&udp); /* Copy out the port numbers and the verification tag. */
memset(&sh, 0, sizeof(struct sctphdr));
m_copydata(ip6cp->ip6c_m,
ip6cp->ip6c_off + sizeof(struct udphdr), sizeof(uint16_t) + sizeof(uint16_t) + sizeof(uint32_t),
(caddr_t)&sh);
memset(&src, 0, sizeof(struct sockaddr_in6));
src.sin6_family = AF_INET6; #ifdef HAVE_SIN6_LEN
src.sin6_len = sizeof(struct sockaddr_in6); #endif
src.sin6_port = sh.src_port;
src.sin6_addr = ip6cp->ip6c_ip6->ip6_src; #ifdefined(__FreeBSD__) && !defined(__Userspace__) if (in6_setscope(&src.sin6_addr, ip6cp->ip6c_m->m_pkthdr.rcvif, NULL) != 0) { return;
} #endif
memset(&dst, 0, sizeof(struct sockaddr_in6));
dst.sin6_family = AF_INET6; #ifdef HAVE_SIN6_LEN
dst.sin6_len = sizeof(struct sockaddr_in6); #endif
dst.sin6_port = sh.dest_port;
dst.sin6_addr = ip6cp->ip6c_ip6->ip6_dst; #ifdefined(__FreeBSD__) && !defined(__Userspace__) if (in6_setscope(&dst.sin6_addr, ip6cp->ip6c_m->m_pkthdr.rcvif, NULL) != 0) { return;
} #endif
inp = NULL;
net = NULL;
stcb = sctp_findassociation_addr_sa((struct sockaddr *)&dst,
(struct sockaddr *)&src,
&inp, &net, 1, SCTP_DEFAULT_VRFID); if ((stcb != NULL) &&
(net != NULL) &&
(inp != NULL)) { /* Check the UDP port numbers */ if ((udp.uh_dport != net->port) ||
(udp.uh_sport != htons(SCTP_BASE_SYSCTL(sctp_udp_tunneling_port)))) {
SCTP_TCB_UNLOCK(stcb); return;
} /* Check the verification tag */ if (ntohl(sh.v_tag) != 0) { /* * This must be the verification tag used for * sending out packets. We don't consider * packets reflecting the verification tag.
*/ if (ntohl(sh.v_tag) != stcb->asoc.peer_vtag) {
SCTP_TCB_UNLOCK(stcb); return;
}
} else { #ifdefined(__FreeBSD__) && !defined(__Userspace__) if (ip6cp->ip6c_m->m_pkthdr.len >=
ip6cp->ip6c_off + sizeof(struct udphdr) + sizeof(struct sctphdr) + sizeof(struct sctp_chunkhdr) +
offsetof(struct sctp_init, a_rwnd)) { /* * In this case we can check if we * got an INIT chunk and if the * initiate tag matches.
*/
uint32_t initiate_tag;
uint8_t chunk_type;
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.235Bemerkung:
(vorverarbeitet am 2026-05-08)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.