void
sctp_free_ifa(struct sctp_ifa *sctp_ifap)
{ if (SCTP_DECREMENT_AND_CHECK_REFCOUNT(&sctp_ifap->refcount)) { /* We zero'd the count */ if (sctp_ifap->ifn_p) {
sctp_free_ifn(sctp_ifap->ifn_p);
}
SCTP_FREE(sctp_ifap, SCTP_M_IFA);
atomic_subtract_int(&SCTP_BASE_INFO(ipi_count_ifas), 1);
}
}
staticvoid
sctp_delete_ifn(struct sctp_ifn *sctp_ifnp, int hold_addr_lock)
{ struct sctp_ifn *found;
found = sctp_find_ifn(sctp_ifnp->ifn_p, sctp_ifnp->ifn_index); if (found == NULL) { /* Not in the list.. sorry */ return;
} if (hold_addr_lock == 0) {
SCTP_IPI_ADDR_WLOCK();
} else {
SCTP_IPI_ADDR_WLOCK_ASSERT();
}
LIST_REMOVE(sctp_ifnp, next_bucket);
LIST_REMOVE(sctp_ifnp, next_ifn); if (hold_addr_lock == 0) {
SCTP_IPI_ADDR_WUNLOCK();
} /* Take away the reference, and possibly free it */
sctp_free_ifn(sctp_ifnp);
}
sin = (struct sockaddr_in *)to; if (prison_check_ip4(inp->ip_inp.inp.inp_cred,
&sin->sin_addr) != 0) {
SCTP_INP_RUNLOCK(inp); continue;
} break;
} #endif #ifdef INET6 case AF_INET6:
{ struct sockaddr_in6 *sin6;
sin6 = (struct sockaddr_in6 *)to; if (prison_check_ip6(inp->ip_inp.inp.inp_cred,
&sin6->sin6_addr) != 0) {
SCTP_INP_RUNLOCK(inp); continue;
} break;
} #endif default:
SCTP_INP_RUNLOCK(inp); continue;
} #endif #ifdef SCTP_MVRF
fnd = 0; for (i = 0; i < inp->num_vrfs; i++) { if (inp->m_vrf_ids[i] == vrf_id) {
fnd = 1; break;
}
} if (fnd == 0) {
SCTP_INP_RUNLOCK(inp); continue;
} #else if (inp->def_vrf_id != vrf_id) {
SCTP_INP_RUNLOCK(inp); continue;
} #endif /* check to see if the ep has one of the addresses */ if ((inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) == 0) { /* We are NOT bound all, so look further */ int match = 0;
LIST_FOREACH(laddr, &inp->sctp_addr_list, sctp_nxt_addr) { if (laddr->ifa == NULL) {
SCTPDBG(SCTP_DEBUG_PCB1, "%s: NULL ifa\n", __func__); continue;
} if (laddr->ifa->localifa_flags & SCTP_BEING_DELETED) {
SCTPDBG(SCTP_DEBUG_PCB1, "ifa being deleted\n"); continue;
} if (laddr->ifa->address.sa.sa_family ==
to->sa_family) { /* see if it matches */ #ifdef INET if (from->sa_family == AF_INET) { struct sockaddr_in *intf_addr, *sin;
intf_addr = &laddr->ifa->address.sin;
sin = (struct sockaddr_in *)to; if (sin->sin_addr.s_addr ==
intf_addr->sin_addr.s_addr) {
match = 1; break;
}
} #endif #ifdef INET6 if (from->sa_family == AF_INET6) { struct sockaddr_in6 *intf_addr6; struct sockaddr_in6 *sin6;
if (SCTP6_ARE_ADDR_EQUAL(sin6,
intf_addr6)) {
match = 1; break;
}
} #endif #ifdefined(__Userspace__) if (from->sa_family == AF_CONN) { struct sockaddr_conn *intf_addr, *sconn;
intf_addr = &laddr->ifa->address.sconn;
sconn = (struct sockaddr_conn *)to; if (sconn->sconn_addr ==
intf_addr->sconn_addr) {
match = 1; break;
}
} #endif
}
} if (match == 0) { /* This endpoint does not have this address */
SCTP_INP_RUNLOCK(inp); continue;
}
} /* *Okifwehitheretheephastheaddress,doesithold *thetcb?
*/ /* XXX: Why don't we TAILQ_FOREACH through sctp_asoc_list? */
stcb = LIST_FIRST(&inp->sctp_asoc_list); if (stcb == NULL) {
SCTP_INP_RUNLOCK(inp); continue;
}
SCTP_TCB_LOCK(stcb); if (!sctp_does_stcb_own_this_addr(stcb, to)) {
SCTP_TCB_UNLOCK(stcb);
SCTP_INP_RUNLOCK(inp); continue;
} if (stcb->rport != rport) { /* remote port does not match. */
SCTP_TCB_UNLOCK(stcb);
SCTP_INP_RUNLOCK(inp); continue;
} if (stcb->asoc.state & SCTP_STATE_ABOUT_TO_BE_FREED) {
SCTP_TCB_UNLOCK(stcb);
SCTP_INP_RUNLOCK(inp); continue;
} if (!sctp_does_stcb_own_this_addr(stcb, to)) {
SCTP_TCB_UNLOCK(stcb);
SCTP_INP_RUNLOCK(inp); continue;
} /* Does this TCB have a matching address? */
TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) { if (net->ro._l_addr.sa.sa_family != from->sa_family) { /* not the same family, can't be a match */ continue;
} switch (from->sa_family) { #ifdef INET case AF_INET:
{ struct sockaddr_in *sin, *rsin;
sin = (struct sockaddr_in *)&net->ro._l_addr;
rsin = (struct sockaddr_in *)from; if (sin->sin_addr.s_addr ==
rsin->sin_addr.s_addr) { /* found it */ if (netp != NULL) {
*netp = net;
} /* Update the endpoint pointer */
*inp_p = inp;
SCTP_INP_RUNLOCK(inp); return (stcb);
} break;
} #endif #ifdef INET6 case AF_INET6:
{ struct sockaddr_in6 *sin6, *rsin6;
sin6 = (struct sockaddr_in6 *)&net->ro._l_addr;
rsin6 = (struct sockaddr_in6 *)from; if (SCTP6_ARE_ADDR_EQUAL(sin6,
rsin6)) { /* found it */ if (netp != NULL) {
*netp = net;
} /* Update the endpoint pointer */
*inp_p = inp;
SCTP_INP_RUNLOCK(inp); return (stcb);
} break;
} #endif #ifdefined(__Userspace__) case AF_CONN:
{ struct sockaddr_conn *sconn, *rsconn;
inp = *inp_p; switch (remote->sa_family) { #ifdef INET case AF_INET:
rport = (((struct sockaddr_in *)remote)->sin_port); break; #endif #ifdef INET6 case AF_INET6:
rport = (((struct sockaddr_in6 *)remote)->sin6_port); break; #endif #ifdefined(__Userspace__) case AF_CONN:
rport = (((struct sockaddr_conn *)remote)->sconn_port); break; #endif default: return (NULL);
} if (locked_tcb) { /* *UN-locksowecandoproperlockingherethisoccurswhen *calledfromload_addresses_from_init.
*/
atomic_add_int(&locked_tcb->asoc.refcnt, 1);
SCTP_TCB_UNLOCK(locked_tcb);
}
SCTP_INP_INFO_RLOCK(); if ((inp->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) ||
(inp->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL)) { /*- *Noweitherthisguyisourlistenerorit'sthe *connector.Ifitistheonethatissuedtheconnect,then *it'sonlychanceistobethefirstTCBinthelist.If *itistheacceptor,thendothespecial_lookuptohash *andfindtherealinp.
*/ if ((inp->sctp_socket) && SCTP_IS_LISTENING(inp)) { /* to is peer addr, from is my addr */ #ifndef SCTP_MVRF
stcb = sctp_tcb_special_locate(inp_p, remote, local,
netp, inp->def_vrf_id); if ((stcb != NULL) && (locked_tcb == NULL)) { /* we have a locked tcb, lower refcount */
SCTP_INP_DECR_REF(inp);
} if ((locked_tcb != NULL) && (locked_tcb != stcb)) {
SCTP_INP_RLOCK(locked_tcb->sctp_ep);
SCTP_TCB_LOCK(locked_tcb);
atomic_subtract_int(&locked_tcb->asoc.refcnt, 1);
SCTP_INP_RUNLOCK(locked_tcb->sctp_ep);
} #else /*- *MVRFistricky,wemustlookineveryVRF *theendpointhas.
*/ int i;
for (i = 0; i < inp->num_vrfs; i++) {
stcb = sctp_tcb_special_locate(inp_p, remote, local,
netp, inp->m_vrf_ids[i]); if ((stcb != NULL) && (locked_tcb == NULL)) { /* we have a locked tcb, lower refcount */
SCTP_INP_DECR_REF(inp); break;
} if ((locked_tcb != NULL) && (locked_tcb != stcb)) {
SCTP_INP_RLOCK(locked_tcb->sctp_ep);
SCTP_TCB_LOCK(locked_tcb);
atomic_subtract_int(&locked_tcb->asoc.refcnt, 1);
SCTP_INP_RUNLOCK(locked_tcb->sctp_ep); break;
}
} #endif
SCTP_INP_INFO_RUNLOCK(); return (stcb);
} else {
SCTP_INP_WLOCK(inp); if (inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_ALLGONE) { goto null_return;
}
stcb = LIST_FIRST(&inp->sctp_asoc_list); if (stcb == NULL) { goto null_return;
}
SCTP_TCB_LOCK(stcb);
if (stcb->rport != rport) { /* remote port does not match. */
SCTP_TCB_UNLOCK(stcb); goto null_return;
} if (stcb->asoc.state & SCTP_STATE_ABOUT_TO_BE_FREED) {
SCTP_TCB_UNLOCK(stcb); goto null_return;
} if (local && !sctp_does_stcb_own_this_addr(stcb, local)) {
SCTP_TCB_UNLOCK(stcb); goto null_return;
} /* now look at the list of remote addresses */
TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) { #ifdef INVARIANTS if (net == (TAILQ_NEXT(net, sctp_next))) {
panic("Corrupt net list");
} #endif if (net->ro._l_addr.sa.sa_family !=
remote->sa_family) { /* not the same family */ continue;
} switch (remote->sa_family) { #ifdef INET case AF_INET:
{ struct sockaddr_in *sin, *rsin;
sin = (struct sockaddr_in *)
&net->ro._l_addr;
rsin = (struct sockaddr_in *)remote; if (sin->sin_addr.s_addr ==
rsin->sin_addr.s_addr) { /* found it */ if (netp != NULL) {
*netp = net;
} if (locked_tcb == NULL) {
SCTP_INP_DECR_REF(inp);
} elseif (locked_tcb != stcb) {
SCTP_TCB_LOCK(locked_tcb);
} if (locked_tcb) {
atomic_subtract_int(&locked_tcb->asoc.refcnt, 1);
}
head = &SCTP_BASE_INFO(sctp_ephash)[SCTP_PCBHASH_ALLADDR(lport,
SCTP_BASE_INFO(hashmark))];
LIST_FOREACH(t_inp, head, sctp_hash) { if (t_inp->sctp_lport != lport) { continue;
} /* is it in the VRF in question */
fnd = 0; #ifdef SCTP_MVRF for (i = 0; i < inp->num_vrfs; i++) { if (t_inp->m_vrf_ids[i] == vrf_id) {
fnd = 1; break;
}
} #else if (t_inp->def_vrf_id == vrf_id)
fnd = 1; #endif if (!fnd) continue;
/* This one is in use. */ /* check the v6/v4 binding issue */ if ((t_inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) &&
SCTP_IPV6_V6ONLY(t_inp)) { if (inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) { /* collision in V6 space */ return (t_inp);
} else { /* inp is BOUND_V4 no conflict */ continue;
}
} elseif (t_inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) { /* t_inp is bound v4 and v6, conflict always */ return (t_inp);
} else { /* t_inp is bound only V4 */ if ((inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) &&
SCTP_IPV6_V6ONLY(inp)) { /* no conflict */ continue;
} /* else fall through to conflict */
} return (t_inp);
} return (NULL);
}
int
sctp_swap_inpcb_for_listen(struct sctp_inpcb *inp)
{ /* For 1-2-1 with port reuse */ struct sctppcbhead *head; struct sctp_inpcb *tinp, *ninp;
if (sctp_is_feature_off(inp, SCTP_PCB_FLAGS_PORTREUSE)) { /* only works with port reuse on */ return (-1);
} if ((inp->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL) == 0) { return (0);
}
SCTP_INP_WUNLOCK(inp);
head = &SCTP_BASE_INFO(sctp_ephash)[SCTP_PCBHASH_ALLADDR(inp->sctp_lport,
SCTP_BASE_INFO(hashmark))]; /* Kick out all non-listeners to the TCP hash */
LIST_FOREACH_SAFE(tinp, head, sctp_hash, ninp) { if (tinp->sctp_lport != inp->sctp_lport) { continue;
} if (tinp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_ALLGONE) { continue;
} if (tinp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) { continue;
} if (SCTP_IS_LISTENING(tinp)) { continue;
}
SCTP_INP_WLOCK(tinp);
LIST_REMOVE(tinp, sctp_hash);
head = &SCTP_BASE_INFO(sctp_tcpephash)[SCTP_PCBHASH_ALLADDR(tinp->sctp_lport, SCTP_BASE_INFO(hashtcpmark))];
tinp->sctp_flags |= SCTP_PCB_FLAGS_IN_TCPPOOL;
LIST_INSERT_HEAD(head, tinp, sctp_hash);
SCTP_INP_WUNLOCK(tinp);
}
SCTP_INP_WLOCK(inp); /* Pull from where he was */
LIST_REMOVE(inp, sctp_hash);
inp->sctp_flags &= ~SCTP_PCB_FLAGS_IN_TCPPOOL;
head = &SCTP_BASE_INFO(sctp_ephash)[SCTP_PCBHASH_ALLADDR(inp->sctp_lport, SCTP_BASE_INFO(hashmark))];
LIST_INSERT_HEAD(head, inp, sctp_hash); return (0);
}
phdr = sctp_get_next_param(m, offset, ¶m_buf, sizeof(param_buf)); while (phdr != NULL) { /* now we must see if we want the parameter */ #ifdefined(INET) || defined(INET6)
ptype = ntohs(phdr->param_type); #endif
plen = ntohs(phdr->param_length); if (plen == 0) { break;
} #ifdef INET if (ptype == SCTP_IPV4_ADDRESS &&
plen == sizeof(struct sctp_ipv4addr_param)) { /* Get the rest of the address */ struct sctp_ipv4addr_param ip4_param, *p4;
phdr = sctp_get_next_param(m, offset,
(struct sctp_paramhdr *)&ip4_param, sizeof(ip4_param)); if (phdr == NULL) { return (NULL);
}
p4 = (struct sctp_ipv4addr_param *)phdr;
memcpy(&sin4.sin_addr, &p4->addr, sizeof(p4->addr)); /* look it up */
stcb = sctp_findassociation_ep_addr(inp_p,
(struct sockaddr *)&sin4, netp, dst, NULL); if (stcb != NULL) { return (stcb);
}
} #endif #ifdef INET6 if (ptype == SCTP_IPV6_ADDRESS &&
plen == sizeof(struct sctp_ipv6addr_param)) { /* Get the rest of the address */ struct sctp_ipv6addr_param ip6_param, *p6;
if (sh->v_tag) { /* we only go down this path if vtag is non-zero */
stcb = sctp_findassoc_by_vtag(src, dst, ntohl(sh->v_tag),
inp_p, netp, sh->src_port, sh->dest_port, 0, vrf_id, 0); if (stcb) { return (stcb);
}
}
if (inp_p) {
stcb = sctp_findassociation_addr_sa(src, dst, inp_p, netp, 1, vrf_id);
inp = *inp_p;
} else {
stcb = sctp_findassociation_addr_sa(src, dst, &inp, netp, 1, vrf_id);
}
SCTPDBG(SCTP_DEBUG_PCB1, "stcb:%p inp:%p\n", (void *)stcb, (void *)inp); if (stcb == NULL && inp) { /* Found a EP but not this address */ if ((ch->chunk_type == SCTP_INITIATION) ||
(ch->chunk_type == SCTP_INITIATION_ACK)) { /*- *specialhook,wedoNOTreturnlinporan *associationthatislinkedtoanexisting *associationthatisundertheTCPpool(i.e.no *listenerexists).Theendpointfindingroutine *willalwaysfindalistenerbeforeexaminingthe *TCPpool.
*/ if (inp->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL) { if (inp_p) {
*inp_p = NULL;
} return (NULL);
}
stcb = sctp_findassociation_special_addr(m,
offset, sh, &inp, netp, dst); if (inp_p != NULL) {
*inp_p = inp;
}
}
}
SCTPDBG(SCTP_DEBUG_PCB1, "stcb is %p\n", (void *)stcb); return (stcb);
}
m->sctp_default_cc_module = SCTP_BASE_SYSCTL(sctp_default_cc_module);
m->sctp_default_ss_module = SCTP_BASE_SYSCTL(sctp_default_ss_module);
m->max_open_streams_intome = SCTP_BASE_SYSCTL(sctp_nr_incoming_streams_default); /* number of streams to pre-open on a association */
m->pre_open_stream_count = SCTP_BASE_SYSCTL(sctp_nr_outgoing_streams_default);
/* make it so new data pours into the new socket */
stcb->sctp_socket = new_inp->sctp_socket;
stcb->sctp_ep = new_inp;
/* Copy the port across */
lport = new_inp->sctp_lport = old_inp->sctp_lport;
rport = stcb->rport; /* Pull the tcb from the old association */
LIST_REMOVE(stcb, sctp_tcbhash);
LIST_REMOVE(stcb, sctp_tcblist); if (stcb->asoc.in_asocid_hash) {
LIST_REMOVE(stcb, sctp_tcbasocidhash);
} /* Now insert the new_inp into the TCP connected hash */
head = &SCTP_BASE_INFO(sctp_tcpephash)[SCTP_PCBHASH_ALLADDR((lport | rport), SCTP_BASE_INFO(hashtcpmark))];
LIST_INSERT_HEAD(head, new_inp, sctp_hash); /* Its safe to access */
new_inp->sctp_flags &= ~SCTP_PCB_FLAGS_UNBOUND;
/* Now move the tcb into the endpoint list */
LIST_INSERT_HEAD(&new_inp->sctp_asoc_list, stcb, sctp_tcblist); /* *Question,doweevenneedtoworryabouttheep-hashsincewe *onlyhaveoneconnection?Probablynot:>soletsgetridofit *andnotsuckupanykernelmemoryinthat.
*/ if (stcb->asoc.in_asocid_hash) { struct sctpasochead *lhd;
lhd = &new_inp->sctp_asocidhash[SCTP_PCBHASH_ASOC(stcb->asoc.assoc_id,
new_inp->hashasocidmark)];
LIST_INSERT_HEAD(lhd, stcb, sctp_tcbasocidhash);
} /* Ok. Let's restart timer. */
TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) {
sctp_timer_start(SCTP_TIMER_TYPE_PATHMTURAISE, new_inp,
stcb, net);
}
SCTP_INP_INFO_WUNLOCK(); if (new_inp->sctp_tcbhash != NULL) {
SCTP_HASH_FREE(new_inp->sctp_tcbhash, new_inp->sctp_hashmark);
new_inp->sctp_tcbhash = NULL;
} if ((new_inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) == 0) { /* Subset bound, so copy in the laddr list from the old_inp */
LIST_FOREACH(oladdr, &old_inp->sctp_addr_list, sctp_nxt_addr) {
laddr = SCTP_ZONE_GET(SCTP_BASE_INFO(ipi_zone_laddr), struct sctp_laddr); if (laddr == NULL) { /* *Gak,whatcanwedo?Thisassocisreally *HOSED.Weprobablyshouldsendanabort *here.
*/
SCTPDBG(SCTP_DEBUG_PCB1, "Association hosed in TCP model, out of laddr memory\n"); continue;
}
SCTP_INCR_LADDR_COUNT();
memset(laddr, 0, sizeof(*laddr));
(void)SCTP_GETTIME_TIMEVAL(&laddr->start_time);
laddr->ifa = oladdr->ifa;
atomic_add_int(&laddr->ifa->refcount, 1);
LIST_INSERT_HEAD(&new_inp->sctp_addr_list, laddr,
sctp_nxt_addr);
new_inp->laddr_count++; if (oladdr == stcb->asoc.last_used_address) {
stcb->asoc.last_used_address = laddr;
}
}
} /* Now any running timers need to be adjusted. */ if (stcb->asoc.dack_timer.ep == old_inp) {
SCTP_INP_DECR_REF(old_inp);
stcb->asoc.dack_timer.ep = new_inp;
SCTP_INP_INCR_REF(new_inp);
} if (stcb->asoc.asconf_timer.ep == old_inp) {
SCTP_INP_DECR_REF(old_inp);
stcb->asoc.asconf_timer.ep = new_inp;
SCTP_INP_INCR_REF(new_inp);
} if (stcb->asoc.strreset_timer.ep == old_inp) {
SCTP_INP_DECR_REF(old_inp);
stcb->asoc.strreset_timer.ep = new_inp;
SCTP_INP_INCR_REF(new_inp);
} if (stcb->asoc.shut_guard_timer.ep == old_inp) {
SCTP_INP_DECR_REF(old_inp);
stcb->asoc.shut_guard_timer.ep = new_inp;
SCTP_INP_INCR_REF(new_inp);
} if (stcb->asoc.autoclose_timer.ep == old_inp) {
SCTP_INP_DECR_REF(old_inp);
stcb->asoc.autoclose_timer.ep = new_inp;
SCTP_INP_INCR_REF(new_inp);
} if (stcb->asoc.delete_prim_timer.ep == old_inp) {
SCTP_INP_DECR_REF(old_inp);
stcb->asoc.delete_prim_timer.ep = new_inp;
SCTP_INP_INCR_REF(new_inp);
} /* now what about the nets? */
TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) { if (net->pmtu_timer.ep == old_inp) {
SCTP_INP_DECR_REF(old_inp);
net->pmtu_timer.ep = new_inp;
SCTP_INP_INCR_REF(new_inp);
} if (net->hb_timer.ep == old_inp) {
SCTP_INP_DECR_REF(old_inp);
net->hb_timer.ep = new_inp;
SCTP_INP_INCR_REF(new_inp);
} if (net->rxt_timer.ep == old_inp) {
SCTP_INP_DECR_REF(old_inp);
net->rxt_timer.ep = new_inp;
SCTP_INP_INCR_REF(new_inp);
}
}
SCTP_INP_WUNLOCK(new_inp);
SCTP_INP_WUNLOCK(old_inp);
}
#ifdef HAVE_SA_LEN if (addr->sa_len != sizeof(struct sockaddr_conn)) {
error = EINVAL;
SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PCB, error); goto out;
} #endif
sconn = (struct sockaddr_conn *)addr;
lport = sconn->sconn_port; if (sconn->sconn_addr != NULL) {
bindall = 0;
} break;
} #endif default:
error = EAFNOSUPPORT;
SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PCB, error); goto out;
}
} /* Setup a vrf_id to be the default for the non-bind-all case. */
vrf_id = inp->def_vrf_id;
if (lport) { /* *Didthecallerspecifyaport?ifsowemustseeifanep *alreadyhasthisonebound.
*/ /* got to be root to get at low ports */ #if !(defined(_WIN32) && !defined(__Userspace__)) if (ntohs(lport) < IPPORT_RESERVED && #ifdefined(__FreeBSD__) && !defined(__Userspace__)
(error = priv_check(td, PRIV_NETINET_RESERVEDPORT)) != 0) { #elifdefined(__APPLE__) && !defined(__Userspace__)
(error = suser(p->p_ucred, &p->p_acflag)) != 0) { #elifdefined(__Userspace__) /* TODO ensure uid is 0, etc... */ 0) { #else
(error = suser(p, 0)) != 0) { #endif goto out;
} #endif
SCTP_INP_INCR_REF(inp);
SCTP_INP_WUNLOCK(inp); if (bindall) { #ifdef SCTP_MVRF for (i = 0; i < inp->num_vrfs; i++) {
vrf_id = inp->m_vrf_ids[i]; #else
vrf_id = inp->def_vrf_id; #endif
inp_tmp = sctp_pcb_findep(addr, 0, 1, vrf_id); if (inp_tmp != NULL) { /* *lockguyreturnedandlowercount *notethatwearenotboundso *inp_tmpshouldNEVERbeinp.And *itisthisinp(inp_tmp)thatgets *thereferencebump,sowemust *lowerit.
*/
SCTP_INP_DECR_REF(inp_tmp); /* unlock info */ if ((sctp_is_feature_on(inp, SCTP_PCB_FLAGS_PORTREUSE)) &&
(sctp_is_feature_on(inp_tmp, SCTP_PCB_FLAGS_PORTREUSE))) { /* Ok, must be one-2-one and allowing port re-use */
port_reuse_active = 1; goto continue_anyway;
}
SCTP_INP_WLOCK(inp);
SCTP_INP_DECR_REF(inp);
error = EADDRINUSE;
SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PCB, error); goto out;
} #ifdef SCTP_MVRF
} #endif
} else {
inp_tmp = sctp_pcb_findep(addr, 0, 1, vrf_id); if (inp_tmp != NULL) { /* *lockguyreturnedandlowercountnote *thatwearenotboundsoinp_tmpshould *NEVERbeinp.Anditisthisinp(inp_tmp) *thatgetsthereferencebump,sowemust *lowerit.
*/
SCTP_INP_DECR_REF(inp_tmp); /* unlock info */ if ((sctp_is_feature_on(inp, SCTP_PCB_FLAGS_PORTREUSE)) &&
(sctp_is_feature_on(inp_tmp, SCTP_PCB_FLAGS_PORTREUSE))) { /* Ok, must be one-2-one and allowing port re-use */
port_reuse_active = 1; goto continue_anyway;
}
SCTP_INP_WLOCK(inp);
SCTP_INP_DECR_REF(inp);
error = EADDRINUSE;
SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PCB, error); goto out;
}
}
continue_anyway:
SCTP_INP_WLOCK(inp);
SCTP_INP_DECR_REF(inp); if (bindall) { /* verify that no lport is not used by a singleton */ if ((port_reuse_active == 0) &&
(inp_tmp = sctp_isport_inuse(inp, lport, vrf_id))) { /* Sorry someone already has this one bound */ if ((sctp_is_feature_on(inp, SCTP_PCB_FLAGS_PORTREUSE)) &&
(sctp_is_feature_on(inp_tmp, SCTP_PCB_FLAGS_PORTREUSE))) {
port_reuse_active = 1;
} else {
error = EADDRINUSE;
SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PCB, error); goto out;
}
}
}
} else {
uint16_t first, last, candidate;
uint16_t count;
#ifdefined(__Userspace__)
first = MODULE_GLOBAL(ipport_firstauto);
last = MODULE_GLOBAL(ipport_lastauto); #elifdefined(_WIN32)
first = 1;
last = 0xffff; #elifdefined(__FreeBSD__) || defined(__APPLE__) if (ip_inp->inp_flags & INP_HIGHPORT) {
first = MODULE_GLOBAL(ipport_hifirstauto);
last = MODULE_GLOBAL(ipport_hilastauto);
} elseif (ip_inp->inp_flags & INP_LOWPORT) { #ifdefined(__FreeBSD__) if ((error = priv_check(td, PRIV_NETINET_RESERVEDPORT)) != 0) { #else if ((error = suser(p->p_ucred, &p->p_acflag)) != 0) { #endif
SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PCB, error); goto out;
}
first = MODULE_GLOBAL(ipport_lowfirstauto);
last = MODULE_GLOBAL(ipport_lowlastauto);
} else {
first = MODULE_GLOBAL(ipport_firstauto);
last = MODULE_GLOBAL(ipport_lastauto);
} #endif if (first > last) {
uint16_t temp;
temp = first;
first = last;
last = temp;
}
count = last - first + 1; /* number of candidates */
candidate = first + sctp_select_initial_TSN(&inp->sctp_ep) % (count);
for (;;) { #ifdef SCTP_MVRF for (i = 0; i < inp->num_vrfs; i++) { if (sctp_isport_inuse(inp, htons(candidate), inp->m_vrf_ids[i]) != NULL) { break;
}
} if (i == inp->num_vrfs) {
lport = htons(candidate); break;
} #else if (sctp_isport_inuse(inp, htons(candidate), inp->def_vrf_id) == NULL) {
lport = htons(candidate); break;
} #endif if (--count == 0) {
error = EADDRINUSE;
SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PCB, error); goto out;
} if (candidate == last)
candidate = first; else
candidate = candidate + 1;
}
} if (inp->sctp_flags & (SCTP_PCB_FLAGS_SOCKET_GONE |
SCTP_PCB_FLAGS_SOCKET_ALLGONE)) { /* *thisreallyshouldnothappen.Theguydidanon-blocking *bindandthendidacloseatthesametime.
*/
error = EINVAL;
SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PCB, error); goto out;
} /* ok we look clear to give out this port, so lets setup the binding */ if (bindall) { /* binding to all addresses, so just set in the proper flags */
inp->sctp_flags |= SCTP_PCB_FLAGS_BOUNDALL; /* set the automatic addr changes from kernel flag */ if (SCTP_BASE_SYSCTL(sctp_auto_asconf) == 0) {
sctp_feature_off(inp, SCTP_PCB_FLAGS_DO_ASCONF);
sctp_feature_off(inp, SCTP_PCB_FLAGS_AUTO_ASCONF);
} else {
sctp_feature_on(inp, SCTP_PCB_FLAGS_DO_ASCONF);
sctp_feature_on(inp, SCTP_PCB_FLAGS_AUTO_ASCONF);
} if (SCTP_BASE_SYSCTL(sctp_multiple_asconfs) == 0) {
sctp_feature_off(inp, SCTP_PCB_FLAGS_MULTIPLE_ASCONFS);
} else {
sctp_feature_on(inp, SCTP_PCB_FLAGS_MULTIPLE_ASCONFS);
} /* set the automatic mobility_base from kernel flag(bymicchie)
*/ if (SCTP_BASE_SYSCTL(sctp_mobility_base) == 0) {
sctp_mobility_feature_off(inp, SCTP_MOBILITY_BASE);
sctp_mobility_feature_off(inp, SCTP_MOBILITY_PRIM_DELETED);
} else {
sctp_mobility_feature_on(inp, SCTP_MOBILITY_BASE);
sctp_mobility_feature_off(inp, SCTP_MOBILITY_PRIM_DELETED);
} /* set the automatic mobility_fasthandoff from kernel flag(bymicchie)
*/ if (SCTP_BASE_SYSCTL(sctp_mobility_fasthandoff) == 0) {
sctp_mobility_feature_off(inp, SCTP_MOBILITY_FASTHANDOFF);
sctp_mobility_feature_off(inp, SCTP_MOBILITY_PRIM_DELETED);
} else {
sctp_mobility_feature_on(inp, SCTP_MOBILITY_FASTHANDOFF);
sctp_mobility_feature_off(inp, SCTP_MOBILITY_PRIM_DELETED);
}
} else { /* *bindspecific,makesureflagsisoffandaddanew *addressstructuretothesctp_addr_listinsidetheep *structure. * *Wewillneedtoallocateoneandinsertitatthehead.The *socketoptcallcanjustinsertnewaddressesinthereas *well.Itwillalsohavetodotheembedscopekamehack *too(beforeadding).
*/ struct sctp_ifa *ifa; union sctp_sockstore store;
memset(&store, 0, sizeof(store)); switch (addr->sa_family) { #ifdef INET case AF_INET:
memcpy(&store.sin, addr, sizeof(struct sockaddr_in));
store.sin.sin_port = 0; break; #endif #ifdef INET6 case AF_INET6:
memcpy(&store.sin6, addr, sizeof(struct sockaddr_in6));
store.sin6.sin6_port = 0; break; #endif #ifdefined(__Userspace__) case AF_CONN:
memcpy(&store.sconn, addr, sizeof(struct sockaddr_conn));
store.sconn.sconn_port = 0; break; #endif default: break;
} /* *firstfindtheinterfacewiththeboundaddressneedto *zeroouttheporttofindtheaddress!yuck!can'tdo *thisearliersinceneedportforsctp_pcb_findep()
*/ if (sctp_ifap != NULL) {
ifa = sctp_ifap;
} else { /* Note for BSD we hit here always other *O/S'swillpassthingsinviathe *sctp_ifapargument.
*/
ifa = sctp_find_ifa_by_addr(&store.sa,
vrf_id, SCTP_ADDR_NOT_LOCKED);
} if (ifa == NULL) {
error = EADDRNOTAVAIL; /* Can't find an interface with that address */
SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PCB, error); goto out;
} #ifdef INET6 if (addr->sa_family == AF_INET6) { /* GAK, more FIXME IFA lock? */ if (ifa->localifa_flags & SCTP_ADDR_IFA_UNUSEABLE) { /* Can't bind a non-existent addr. */
error = EINVAL;
SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PCB, error); goto out;
}
} #endif /* we're not bound all */
inp->sctp_flags &= ~SCTP_PCB_FLAGS_BOUNDALL; /* allow bindx() to send ASCONF's for binding changes */
sctp_feature_on(inp, SCTP_PCB_FLAGS_DO_ASCONF); /* clear automatic addr changes from kernel flag */
sctp_feature_off(inp, SCTP_PCB_FLAGS_AUTO_ASCONF);
/* add this address to the endpoint list */
error = sctp_insert_laddr(&inp->sctp_addr_list, ifa, 0); if (error != 0) goto out;
inp->laddr_count++;
} /* find the bucket */ if (port_reuse_active) { /* Put it into tcp 1-2-1 hash */
head = &SCTP_BASE_INFO(sctp_tcpephash)[SCTP_PCBHASH_ALLADDR(lport, SCTP_BASE_INFO(hashtcpmark))];
inp->sctp_flags |= SCTP_PCB_FLAGS_IN_TCPPOOL;
} else {
head = &SCTP_BASE_INFO(sctp_ephash)[SCTP_PCBHASH_ALLADDR(lport, SCTP_BASE_INFO(hashmark))];
} /* put it in the bucket */
LIST_INSERT_HEAD(head, inp, sctp_hash);
SCTPDBG(SCTP_DEBUG_PCB1, "Main hash to bind at head:%p, bound port:%d - in tcp_pool=%d\n",
(void *)head, ntohs(lport), port_reuse_active); /* set in the port */
inp->sctp_lport = lport;
/* turn off just the unbound flag */
KASSERT((inp->sctp_flags & SCTP_PCB_FLAGS_UNBOUND) != 0,
("%s: inp %p is already bound", __func__, inp));
inp->sctp_flags &= ~SCTP_PCB_FLAGS_UNBOUND;
out: return (error);
}
/* *WeenterwiththeonlytheITERATOR_LOCKinplaceandawrite *lockontheinp_infostuff.
*/
it = sctp_it_ctl.cur_it; #ifdefined(__FreeBSD__) && !defined(__Userspace__) if (it && (it->vn != curvnet)) { /* Its not looking at our VNET */ return;
} #endif if (it && (it->inp == inp)) { /* *Thisistrickyandweholdtheiteratorlock, *butwhenitreturnsandgetsthelock(whenwe *releaseit)theiteratorwilltrytooperateon *inp.Weneedtostopthatfromhappening.But *ofcoursetheiteratorhasareferenceonthe *stcbandinp.Wecanmarkitanditwillstop. * *Ifitsasingleiteratorsituation,we *settheenditeratorflag.Otherwise *wesettheiteratortogotothenextinp. *
*/ if (it->iterator_flags & SCTP_ITERATOR_DO_SINGLE_INP) {
sctp_it_ctl.iterator_flags |= SCTP_ITERATOR_STOP_CUR_IT;
} else {
sctp_it_ctl.iterator_flags |= SCTP_ITERATOR_STOP_CUR_INP;
}
} /* Now go through and remove any single reference to *ourinpthatmaybestillpendingonthelist
*/
SCTP_IPI_ITERATOR_WQ_LOCK();
TAILQ_FOREACH_SAFE(it, &sctp_it_ctl.iteratorhead, sctp_nxt_itr, nit) { #ifdefined(__FreeBSD__) && !defined(__Userspace__) if (it->vn != curvnet) { continue;
} #endif if (it->inp == inp) { /* This one points to me is it inp specific? */ if (it->iterator_flags & SCTP_ITERATOR_DO_SINGLE_INP) { /* Remove and free this one */
TAILQ_REMOVE(&sctp_it_ctl.iteratorhead,
it, sctp_nxt_itr); if (it->function_atend != NULL) {
(*it->function_atend) (it->pointer, it->val);
}
SCTP_FREE(it, SCTP_M_ITER);
} else {
it->inp = LIST_NEXT(it->inp, sctp_list); if (it->inp) {
SCTP_INP_INCR_REF(it->inp);
}
} /* When its put in the refcnt is incremented so decr it */
SCTP_INP_DECR_REF(inp);
}
}
SCTP_IPI_ITERATOR_WQ_UNLOCK();
}
/* release sctp_inpcb unbind the port */ void
sctp_inpcb_free(struct sctp_inpcb *inp, int immediate, int from)
{ /* *Herewefreeaendpoint.Wemustfindit(ifitisintheHash *table)andremoveitfromthere.Thenwemustalsofinditinthe *overalllistandremoveitfromthere.Afterallremovalsare *completethenanytimerhastobestopped.Thenstarttheactual *freeing.a)Anylocallists.b)Anyassociations.c)Thehashof *allassociations.d)finallytheepitself.
*/ struct sctp_tcb *stcb, *nstcb; struct sctp_laddr *laddr, *nladdr; struct inpcb *ip_pcb; struct socket *so; int being_refed = 0; struct sctp_queued_to_read *sq, *nsq; #if !defined(__Userspace__) #if !defined(__FreeBSD__)
sctp_rtentry_t *rt; #endif #endif int cnt;
sctp_sharedkey_t *shared_key, *nshared_key;
#ifdefined(__APPLE__) && !defined(__Userspace__)
sctp_lock_assert(SCTP_INP_SO(inp)); #endif #ifdef SCTP_LOG_CLOSING
sctp_log_closing(inp, NULL, 0); #endif
SCTP_ITERATOR_LOCK(); /* mark any iterators on the list or being processed */
sctp_iterator_inp_being_freed(inp);
SCTP_ITERATOR_UNLOCK();
SCTP_ASOC_CREATE_LOCK(inp);
SCTP_INP_INFO_WLOCK();
SCTP_INP_WLOCK(inp);
so = inp->sctp_socket;
KASSERT((inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) != 0,
("%s: inp %p still has socket", __func__, inp));
KASSERT((inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_ALLGONE) == 0,
("%s: double free of inp %p", __func__, inp)); if (from == SCTP_CALLED_AFTER_CMPSET_OFCLOSE) {
inp->sctp_flags &= ~SCTP_PCB_FLAGS_CLOSE_IP; /* socket is gone, so no more wakeups allowed */
inp->sctp_flags |= SCTP_PCB_FLAGS_DONT_WAKE;
inp->sctp_flags &= ~SCTP_PCB_FLAGS_WAKEINPUT;
inp->sctp_flags &= ~SCTP_PCB_FLAGS_WAKEOUTPUT;
} /* First time through we have the socket lock, after that no more. */
sctp_timer_stop(SCTP_TIMER_TYPE_NEWCOOKIE, inp, NULL, NULL,
SCTP_FROM_SCTP_PCB + SCTP_LOC_1);
if (inp->control) {
sctp_m_freem(inp->control);
inp->control = NULL;
} if (inp->pkt) {
sctp_m_freem(inp->pkt);
inp->pkt = NULL;
}
ip_pcb = &inp->ip_inp.inp; /* we could just cast the main pointer *herebutIwillbenice:>(i.e.
* ip_pcb = ep;) */ if (immediate == SCTP_FREE_SHOULD_USE_GRACEFUL_CLOSE) { int cnt_in_sd;
cnt_in_sd = 0;
LIST_FOREACH_SAFE(stcb, &inp->sctp_asoc_list, sctp_tcblist, nstcb) {
SCTP_TCB_LOCK(stcb); /* Disconnect the socket please. */
stcb->sctp_socket = NULL;
SCTP_ADD_SUBSTATE(stcb, SCTP_STATE_CLOSED_SOCKET); if (stcb->asoc.state & SCTP_STATE_ABOUT_TO_BE_FREED) { /* Skip guys being freed */
cnt_in_sd++; if (stcb->asoc.state & SCTP_STATE_IN_ACCEPT_QUEUE) { /* *Specialcase-wedidnotstartakill *timerontheasocduetoitwasnot *closed.Sogoaheadandstartitnow.
*/
SCTP_CLEAR_SUBSTATE(stcb, SCTP_STATE_IN_ACCEPT_QUEUE);
sctp_timer_start(SCTP_TIMER_TYPE_ASOCKILL, inp, stcb, NULL);
}
SCTP_TCB_UNLOCK(stcb); continue;
} if (((SCTP_GET_STATE(stcb) == SCTP_STATE_COOKIE_WAIT) ||
(SCTP_GET_STATE(stcb) == SCTP_STATE_COOKIE_ECHOED)) &&
(stcb->asoc.total_output_queue_size == 0)) { /* If we have data in queue, we don't want to just *freesincetheappmayhavedone,send()/close *orconnect/send/close.Anditwantsthedata *togetacrossfirst.
*/ /* Just abandon things in the front states */ if (sctp_free_assoc(inp, stcb, SCTP_PCBFREE_NOFORCE,
SCTP_FROM_SCTP_PCB + SCTP_LOC_2) == 0) {
cnt_in_sd++;
} continue;
} if ((stcb->asoc.size_on_reasm_queue > 0) ||
(stcb->asoc.size_on_all_streams > 0) ||
((so != NULL) && (SCTP_SBAVAIL(&so->so_rcv) > 0))) { /* Left with Data unread */ struct mbuf *op_err;
#ifdef SCTP_TRACK_FREED_ASOCS /* TEMP CODE */
LIST_FOREACH_SAFE(stcb, &inp->sctp_asoc_free_list, sctp_tcblist, nstcb) {
LIST_REMOVE(stcb, sctp_tcblist);
SCTP_ZONE_FREE(SCTP_BASE_INFO(ipi_zone_asoc), stcb);
SCTP_DECR_ASOC_COUNT();
} /* *** END TEMP CODE ****/ #endif #ifdef SCTP_MVRF
SCTP_FREE(inp->m_vrf_ids, SCTP_M_MVRF); #endif /* Now lets see about freeing the EP hash table. */ if (inp->sctp_tcbhash != NULL) {
SCTP_HASH_FREE(inp->sctp_tcbhash, inp->sctp_hashmark);
inp->sctp_tcbhash = NULL;
} /* Now we must put the ep memory back into the zone pool */ #ifdefined(__FreeBSD__) && !defined(__Userspace__)
crfree(inp->ip_inp.inp.inp_cred);
INP_LOCK_DESTROY(&inp->ip_inp.inp); #endif
SCTP_INP_LOCK_DESTROY(inp);
SCTP_INP_READ_LOCK_DESTROY(inp);
SCTP_ASOC_CREATE_LOCK_DESTROY(inp); #if !(defined(__APPLE__) && !defined(__Userspace__))
SCTP_ZONE_FREE(SCTP_BASE_INFO(ipi_zone_ep), inp);
SCTP_DECR_EP_COUNT(); #else /* For Tiger, we will do this later... */ #endif
}
sin=(structsockaddr_in*)newaddr; if(sin->sin_addr.s_addr==0){ /* Invalid address */ return (-1);
} /* zero out the zero area */
memset(&sin->sin_zero, 0, sizeof(sin->sin_zero));
/* assure len is set */ #ifdef HAVE_SIN_LEN
sin->sin_len = sizeof(struct sockaddr_in); #endif if (set_scope) { if (IN4_ISPRIVATE_ADDRESS(&sin->sin_addr)) {
stcb->asoc.scope.ipv4_local_scope = 1;
}
} else { /* Validate the address is in scope */ if ((IN4_ISPRIVATE_ADDRESS(&sin->sin_addr)) &&
(stcb->asoc.scope.ipv4_local_scope == 0)) {
addr_inscope = 0;
}
} break;
} #endif #ifdef INET6 case AF_INET6:
{ struct sockaddr_in6 *sin6;
/* Now generate a route for this guy */ #ifdef INET6 #ifdef SCTP_EMBEDDED_V6_SCOPE /* KAME hack: embed scopeid */ if (newaddr->sa_family == AF_INET6) { struct sockaddr_in6 *sin6;
/* JRS - Use the congestion control given in the CC module */ if (stcb->asoc.cc_functions.sctp_set_initial_cc_param != NULL)
(*stcb->asoc.cc_functions.sctp_set_initial_cc_param)(stcb, net);
/* *CMT:CUCalgo-setfind_pseudo_cumacktoTRUE(1)atbeginning *ofassoc(2005/06/27,iyengar@cis.udel.edu)
*/
net->find_pseudo_cumack = 1;
net->find_rtx_pseudo_cumack = 1; #ifdefined(__FreeBSD__) && !defined(__Userspace__) /* Choose an initial flowid. */
net->flowid = stcb->asoc.my_vtag ^
ntohs(stcb->rport) ^
ntohs(stcb->sctp_ep->sctp_lport);
net->flowtype = M_HASHTYPE_OPAQUE_HASH; #endif if (netp) {
*netp = net;
}
netfirst = TAILQ_FIRST(&stcb->asoc.nets); #ifdefined(__FreeBSD__) && !defined(__Userspace__) if (net->ro.ro_nh == NULL) { #else if (net->ro.ro_rt == NULL) { #endif /* Since we have no route put it at the back */
TAILQ_INSERT_TAIL(&stcb->asoc.nets, net, sctp_next);
} elseif (netfirst == NULL) { /* We are the first one in the pool. */
TAILQ_INSERT_HEAD(&stcb->asoc.nets, net, sctp_next); #ifdefined(__FreeBSD__) && !defined(__Userspace__)
} elseif (netfirst->ro.ro_nh == NULL) { #else
} elseif (netfirst->ro.ro_rt == NULL) { #endif /* *FirstonehasNOroute.Placethisoneaheadofthefirst *one.
*/
TAILQ_INSERT_HEAD(&stcb->asoc.nets, net, sctp_next); #ifdefined(__FreeBSD__) && !defined(__Userspace__)
} elseif (net->ro.ro_nh->nh_ifp != netfirst->ro.ro_nh->nh_ifp) { #else
} elseif (net->ro.ro_rt->rt_ifp != netfirst->ro.ro_rt->rt_ifp) { #endif /* *Thisonehasadifferentinterfacethantheoneatthe *topofthelist.Placeitahead.
*/
TAILQ_INSERT_HEAD(&stcb->asoc.nets, net, sctp_next);
} else { /* *Okwehavethesameinterfaceasthefirstone.Move *forwarduntilwefindeithera)onewithaNULLroute... *insertaheadofthatb)onewithadifferentifp..insert *afterthat.c)endofthelist..insertatthetail.
*/ struct sctp_nets *netlook;
do {
netlook = TAILQ_NEXT(netfirst, sctp_next); if (netlook == NULL) { /* End of the list */
TAILQ_INSERT_TAIL(&stcb->asoc.nets, net, sctp_next); break; #ifdefined(__FreeBSD__) && !defined(__Userspace__)
} elseif (netlook->ro.ro_nh == NULL) { #else
} elseif (netlook->ro.ro_rt == NULL) { #endif /* next one has NO route */
TAILQ_INSERT_BEFORE(netfirst, net, sctp_next); break; #ifdefined(__FreeBSD__) && !defined(__Userspace__)
} elseif (netlook->ro.ro_nh->nh_ifp != net->ro.ro_nh->nh_ifp) { #else
} elseif (netlook->ro.ro_rt->rt_ifp != net->ro.ro_rt->rt_ifp) { #endif
TAILQ_INSERT_AFTER(&stcb->asoc.nets, netlook,
net, sctp_next); break;
} /* Shift forward */
netfirst = netlook;
} while (netlook != NULL);
}
/* got to have a primary set */ if (stcb->asoc.primary_destination == 0) {
stcb->asoc.primary_destination = net; #ifdefined(__FreeBSD__) && !defined(__Userspace__)
} elseif ((stcb->asoc.primary_destination->ro.ro_nh == NULL) &&
(net->ro.ro_nh) && #else
} elseif ((stcb->asoc.primary_destination->ro.ro_rt == NULL) &&
(net->ro.ro_rt) && #endif
((net->dest_state & SCTP_ADDR_UNCONFIRMED) == 0)) { /* No route to current primary adopt new primary */
stcb->asoc.primary_destination = net;
} /* Validate primary is first */
net = TAILQ_FIRST(&stcb->asoc.nets); if ((net != stcb->asoc.primary_destination) &&
(stcb->asoc.primary_destination)) { /* first one on the list is NOT the primary *sctp_cmpaddr()ismuchmoreefficientif *theprimaryisthefirstonthelist,makeit *so.
*/
TAILQ_REMOVE(&stcb->asoc.nets,
stcb->asoc.primary_destination, sctp_next);
TAILQ_INSERT_HEAD(&stcb->asoc.nets,
stcb->asoc.primary_destination, sctp_next);
} return (0);
}
asoc->assoc_id = sctp_aloc_a_assoc_id(inp, stcb); /* now that my_vtag is set, add it to the hash */
head = &SCTP_BASE_INFO(sctp_asochash)[SCTP_PCBHASH_ASOC(stcb->asoc.my_vtag, SCTP_BASE_INFO(hashasocmark))]; /* put it in the bucket in the vtag hash of assoc's for the system */
LIST_INSERT_HEAD(head, stcb, sctp_asocs);
LIST_INSERT_HEAD(&inp->sctp_asoc_list, stcb, sctp_tcblist); /* now file the port under the hash as well */ if (inp->sctp_tcbhash != NULL) {
head = &inp->sctp_tcbhash[SCTP_PCBHASH_ALLADDR(stcb->rport,
inp->sctp_hashmark)];
LIST_INSERT_HEAD(head, stcb, sctp_tcbhash);
} if (initialize_auth_params == SCTP_INITIALIZE_AUTH_PARAMS) {
sctp_initialize_auth_params(inp, stcb);
}
SCTPDBG(SCTP_DEBUG_PCB1, "Association %p now allocated\n", (void *)stcb); return (stcb);
}
/* locate the address */
TAILQ_FOREACH_SAFE(net, &asoc->nets, sctp_next, nnet) { if (net->ro._l_addr.sa.sa_family != remaddr->sa_family) { continue;
} if (sctp_cmpaddr((struct sockaddr *)&net->ro._l_addr,
remaddr)) { /* we found the guy */ if (asoc->numnets < 2) { /* Must have at LEAST two remote addresses */ return (-1);
} else {
sctp_remove_net(stcb, net); return (0);
}
}
} /* not found. */ return (-2);
}
SCTP_INP_INFO_WLOCK_ASSERT();
(void)SCTP_GETTIME_TIMEVAL(&now);
time = now.tv_sec + SCTP_BASE_SYSCTL(sctp_vtag_time_wait);
chain = &SCTP_BASE_INFO(vtag_timewait)[(tag % SCTP_STACK_VTAG_HASH_SIZE)];
set = false;
LIST_FOREACH(twait_block, chain, sctp_nxt_tagblock) { /* Block(s) present, lets find space, and expire on the fly */ for (i = 0; i < SCTP_NUMBER_IN_VTAG_BLOCK; i++) { if ((twait_block->vtag_block[i].v_tag == 0) && !set) {
sctp_set_vtag_block(twait_block->vtag_block + i, time, tag, lport, rport);
set = true; continue;
} if ((twait_block->vtag_block[i].v_tag != 0) &&
(twait_block->vtag_block[i].tv_sec_at_expire < now.tv_sec)) { if (set) { /* Audit expires this guy */
sctp_set_vtag_block(twait_block->vtag_block + i, 0, 0, 0, 0);
} else { /* Reuse it for the new tag */
sctp_set_vtag_block(twait_block->vtag_block + i, time, tag, lport, rport);
set = true;
}
}
} if (set) { /* *Weonlydouptotheblockwherewecan *placeourtagforaudits
*/ break;
}
} /* Need to add a new block to chain */ if (!set) {
SCTP_MALLOC(twait_block, struct sctp_tagblock *, sizeof(struct sctp_tagblock), SCTP_M_TIMW); if (twait_block == NULL) { return;
}
memset(twait_block, 0, sizeof(struct sctp_tagblock));
LIST_INSERT_HEAD(chain, twait_block, sctp_nxt_tagblock);
sctp_set_vtag_block(twait_block->vtag_block, time, tag, lport, rport);
}
}
/* Make it invalid too, that way if its *abouttorunitwillabortandreturn.
*/ /* re-increment the lock */ if (from_inpcbfree == SCTP_NORMAL_PROC) {
atomic_subtract_int(&stcb->asoc.refcnt, 1);
} if (stcb->asoc.refcnt) {
SCTP_CLEAR_SUBSTATE(stcb, SCTP_STATE_IN_ACCEPT_QUEUE);
sctp_timer_start(SCTP_TIMER_TYPE_ASOCKILL, inp, stcb, NULL); if (from_inpcbfree == SCTP_NORMAL_PROC) {
SCTP_INP_INFO_WUNLOCK();
SCTP_INP_WUNLOCK(inp);
}
SCTP_TCB_UNLOCK(stcb); return (0);
}
asoc->state = 0; if (inp->sctp_tcbhash) {
LIST_REMOVE(stcb, sctp_tcbhash);
} if (stcb->asoc.in_asocid_hash) {
LIST_REMOVE(stcb, sctp_tcbasocidhash);
} if (inp->sctp_socket == NULL) {
stcb->sctp_socket = NULL;
} /* Now lets remove it from the list of ALL associations in the EP */
LIST_REMOVE(stcb, sctp_tcblist); if (from_inpcbfree == SCTP_NORMAL_PROC) {
SCTP_INP_INCR_REF(inp);
SCTP_INP_WUNLOCK(inp);
} /* pull from vtag hash */
LIST_REMOVE(stcb, sctp_asocs);
sctp_add_vtag_to_timewait(asoc->my_vtag, inp->sctp_lport, stcb->rport);
/* Now restop the timers to be sure *thisisparanoiaatisfinest!
*/
sctp_stop_association_timers(stcb, true);
/* *ThechunklistsandsuchSHOULDbeemptybutwecheckthemjust *incase.
*/ /* anything on the wheel needs to be removed */ for (i = 0; i < asoc->streamoutcnt; i++) { struct sctp_stream_out *outs;
outs = &asoc->strmout[i]; /* now clean up any chunks here */
TAILQ_FOREACH_SAFE(sp, &outs->outqueue, next, nsp) {
atomic_subtract_int(&asoc->stream_queue_cnt, 1);
TAILQ_REMOVE(&outs->outqueue, sp, next);
stcb->asoc.ss_functions.sctp_ss_remove_from_stream(stcb, asoc, outs, sp);
sctp_free_spbufspace(stcb, asoc, sp); if (sp->data) { if (so) { /* Still an open socket - report */
sctp_ulp_notify(SCTP_NOTIFY_SPECIAL_SP_FAIL, stcb, 0, (void *)sp, SCTP_SO_LOCKED);
} if (sp->data) {
sctp_m_freem(sp->data);
sp->data = NULL;
sp->tail_mbuf = NULL;
sp->length = 0;
}
} if (sp->net) {
sctp_free_remote_addr(sp->net);
sp->net = NULL;
}
sctp_free_a_strmoq(stcb, sp, SCTP_SO_LOCKED);
}
} /*sa_ignore FREED_MEMORY*/
TAILQ_FOREACH_SAFE(strrst, &asoc->resetHead, next_resp, nstrrst) {
TAILQ_REMOVE(&asoc->resetHead, strrst, next_resp);
SCTP_FREE(strrst, SCTP_M_STRESET);
}
TAILQ_FOREACH_SAFE(sq, &asoc->pending_reply_queue, next, nsq) {
TAILQ_REMOVE(&asoc->pending_reply_queue, sq, next); if (sq->data) {
sctp_m_freem(sq->data);
sq->data = NULL;
}
sctp_free_remote_addr(sq->whoFrom);
sq->whoFrom = NULL;
sq->stcb = NULL; /* Free the ctl entry */
sctp_free_a_readq(stcb, sq); /*sa_ignore FREED_MEMORY*/
}
TAILQ_FOREACH_SAFE(chk, &asoc->free_chunks, sctp_next, nchk) {
TAILQ_REMOVE(&asoc->free_chunks, chk, sctp_next); if (chk->data) {
sctp_m_freem(chk->data);
chk->data = NULL;
} if (chk->holds_key_ref)
sctp_auth_key_release(stcb, chk->auth_keyid, SCTP_SO_LOCKED);
SCTP_ZONE_FREE(SCTP_BASE_INFO(ipi_zone_chunk), chk);
SCTP_DECR_CHK_COUNT();
atomic_subtract_int(&SCTP_BASE_INFO(ipi_free_chunks), 1);
asoc->free_chunk_cnt--; /*sa_ignore FREED_MEMORY*/
} /* pending send queue SHOULD be empty */
TAILQ_FOREACH_SAFE(chk, &asoc->send_queue, sctp_next, nchk) { if (asoc->strmout[chk->rec.data.sid].chunks_on_queues > 0) {
asoc->strmout[chk->rec.data.sid].chunks_on_queues--; #ifdef INVARIANTS
} else {
panic("No chunks on the queues for sid %u.", chk->rec.data.sid); #endif
}
TAILQ_REMOVE(&asoc->send_queue, chk, sctp_next); if (chk->data) { if (so) { /* Still a socket? */
sctp_ulp_notify(SCTP_NOTIFY_UNSENT_DG_FAIL, stcb, 0, chk, SCTP_SO_LOCKED);
} if (chk->data) {
sctp_m_freem(chk->data);
chk->data = NULL;
}
} if (chk->holds_key_ref)
sctp_auth_key_release(stcb, chk->auth_keyid, SCTP_SO_LOCKED); if (chk->whoTo) {
sctp_free_remote_addr(chk->whoTo);
chk->whoTo = NULL;
}
SCTP_ZONE_FREE(SCTP_BASE_INFO(ipi_zone_chunk), chk);
SCTP_DECR_CHK_COUNT(); /*sa_ignore FREED_MEMORY*/
} /* sent queue SHOULD be empty */
TAILQ_FOREACH_SAFE(chk, &asoc->sent_queue, sctp_next, nchk) { if (chk->sent != SCTP_DATAGRAM_NR_ACKED) { if (asoc->strmout[chk->rec.data.sid].chunks_on_queues > 0) {
asoc->strmout[chk->rec.data.sid].chunks_on_queues--; #ifdef INVARIANTS
} else {
panic("No chunks on the queues for sid %u.", chk->rec.data.sid); #endif
}
}
TAILQ_REMOVE(&asoc->sent_queue, chk, sctp_next); if (chk->data) { if (so) { /* Still a socket? */
sctp_ulp_notify(SCTP_NOTIFY_SENT_DG_FAIL, stcb, 0, chk, SCTP_SO_LOCKED);
} if (chk->data) {
sctp_m_freem(chk->data);
chk->data = NULL;
}
} if (chk->holds_key_ref)
sctp_auth_key_release(stcb, chk->auth_keyid, SCTP_SO_LOCKED);
sctp_free_remote_addr(chk->whoTo);
SCTP_ZONE_FREE(SCTP_BASE_INFO(ipi_zone_chunk), chk);
SCTP_DECR_CHK_COUNT(); /*sa_ignore FREED_MEMORY*/
} #ifdef INVARIANTS for (i = 0; i < stcb->asoc.streamoutcnt; i++) { if (stcb->asoc.strmout[i].chunks_on_queues > 0) {
panic("%u chunks left for stream %u.", stcb->asoc.strmout[i].chunks_on_queues, i);
}
} #endif /* control queue MAY not be empty */
TAILQ_FOREACH_SAFE(chk, &asoc->control_send_queue, sctp_next, nchk) {
TAILQ_REMOVE(&asoc->control_send_queue, chk, sctp_next); if (chk->data) {
sctp_m_freem(chk->data);
chk->data = NULL;
} if (chk->holds_key_ref)
sctp_auth_key_release(stcb, chk->auth_keyid, SCTP_SO_LOCKED);
sctp_free_remote_addr(chk->whoTo);
SCTP_ZONE_FREE(SCTP_BASE_INFO(ipi_zone_chunk), chk);
SCTP_DECR_CHK_COUNT(); /*sa_ignore FREED_MEMORY*/
} /* ASCONF queue MAY not be empty */
TAILQ_FOREACH_SAFE(chk, &asoc->asconf_send_queue, sctp_next, nchk) {
TAILQ_REMOVE(&asoc->asconf_send_queue, chk, sctp_next); if (chk->data) {
sctp_m_freem(chk->data);
chk->data = NULL;
} if (chk->holds_key_ref)
sctp_auth_key_release(stcb, chk->auth_keyid, SCTP_SO_LOCKED);
sctp_free_remote_addr(chk->whoTo);
SCTP_ZONE_FREE(SCTP_BASE_INFO(ipi_zone_chunk), chk);
SCTP_DECR_CHK_COUNT(); /*sa_ignore FREED_MEMORY*/
} if (asoc->mapping_array) {
SCTP_FREE(asoc->mapping_array, SCTP_M_MAP);
asoc->mapping_array = NULL;
} if (asoc->nr_mapping_array) {
SCTP_FREE(asoc->nr_mapping_array, SCTP_M_MAP);
asoc->nr_mapping_array = NULL;
} /* the stream outs */ if (asoc->strmout) {
SCTP_FREE(asoc->strmout, SCTP_M_STRMO);
asoc->strmout = NULL;
}
asoc->strm_realoutsize = asoc->streamoutcnt = 0; if (asoc->strmin) { for (i = 0; i < asoc->streamincnt; i++) {
sctp_clean_up_stream(stcb, &asoc->strmin[i].inqueue);
sctp_clean_up_stream(stcb, &asoc->strmin[i].uno_inqueue);
}
SCTP_FREE(asoc->strmin, SCTP_M_STRMI);
asoc->strmin = NULL;
}
asoc->streamincnt = 0;
TAILQ_FOREACH_SAFE(net, &asoc->nets, sctp_next, nnet) { #ifdef INVARIANTS if (SCTP_BASE_INFO(ipi_count_raddr) == 0) {
panic("no net's left alloc'ed, or list points to itself");
} #endif
TAILQ_REMOVE(&asoc->nets, net, sctp_next);
sctp_free_remote_addr(net);
}
LIST_FOREACH_SAFE(laddr, &asoc->sctp_restricted_addrs, sctp_nxt_addr, naddr) { /*sa_ignore FREED_MEMORY*/
sctp_remove_laddr(laddr);
}
/* first clear the flag */
inp->ip_inp.inp.inp_vflag = 0; /* set the flag based on addresses on the ep list */
LIST_FOREACH(laddr, &inp->sctp_addr_list, sctp_nxt_addr) { if (laddr->ifa == NULL) {
SCTPDBG(SCTP_DEBUG_PCB1, "%s: NULL ifa\n",
__func__); continue;
}
if (inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) { /* You are already bound to all. You have it already */ return;
} #ifdef INET6 if (ifa->address.sa.sa_family == AF_INET6) { if (ifa->localifa_flags & SCTP_ADDR_IFA_UNUSEABLE) { /* Can't bind a non-useable addr. */ return;
}
} #endif /* first, is it already present? */
LIST_FOREACH(laddr, &inp->sctp_addr_list, sctp_nxt_addr) { if (laddr->ifa == ifa) {
fnd = 1; break;
}
}
if (fnd == 0) { /* Not in the ep list */
error = sctp_insert_laddr(&inp->sctp_addr_list, ifa, action); if (error != 0) return;
inp->laddr_count++; /* update inp_vflag flags */ switch (ifa->address.sa.sa_family) { #ifdef INET6 case AF_INET6:
inp->ip_inp.inp.inp_vflag |= INP_IPV6; break; #endif #ifdef INET case AF_INET:
inp->ip_inp.inp.inp_vflag |= INP_IPV4; break; #endif #ifdefined(__Userspace__) case AF_CONN:
inp->ip_inp.inp.inp_vflag |= INP_CONN; break; #endif default: break;
}
LIST_FOREACH(stcb, &inp->sctp_asoc_list, sctp_tcblist) {
sctp_add_local_addr_restricted(stcb, ifa);
}
} return;
}
TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) { /* for now, we'll just pick the first reachable one we find */ if (net->dest_state & SCTP_ADDR_UNCONFIRMED) continue; if (sctp_destination_is_reachable(stcb,
(struct sockaddr *)&net->ro._l_addr)) { /* found a reachable destination */
stcb->asoc.primary_destination = net;
}
} /* I can't there from here! ...we're gonna die shortly... */
}
fnd = 0; if (inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) { /* You are already bound to all. You have it already */ return;
}
LIST_FOREACH(laddr, &inp->sctp_addr_list, sctp_nxt_addr) { if (laddr->ifa == ifa) {
fnd = 1; break;
}
} if (fnd && (inp->laddr_count < 2)) { /* can't delete unless there are at LEAST 2 addresses */ return;
} if (fnd) { /* *cleanupanyuseofthisaddressgothroughour *associationsandclearanylast_used_addressthatmatch *thisoneforeachassoc,seeifanewprimary_destination *isneeded
*/ struct sctp_tcb *stcb;
/* clean up "next_addr_touse" */ if (inp->next_addr_touse == laddr) /* delete this address */
inp->next_addr_touse = NULL;
SCTP_TCB_LOCK(stcb); if (stcb->asoc.last_used_address == laddr) /* delete this address */
stcb->asoc.last_used_address = NULL; /* Now spin through all the nets and purge any ref to laddr */
TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) { if (net->ro._s_addr == laddr->ifa) { /* Yep, purge src address selected */ #ifdefined(__FreeBSD__) && !defined(__Userspace__)
RO_NHFREE(&net->ro); #else
sctp_rtentry_t *rt;
/* delete this address if cached */
rt = net->ro.ro_rt; if (rt != NULL) {
RTFREE(rt);
net->ro.ro_rt = NULL;
} #endif
sctp_free_ifa(net->ro._s_addr);
net->ro._s_addr = NULL;
net->src_addr_selected = 0;
}
}
SCTP_TCB_UNLOCK(stcb);
} /* for each tcb */ /* remove it from the ep list */
sctp_remove_laddr(laddr);
inp->laddr_count--; /* update inp_vflag flags */
sctp_update_ep_vflag(inp);
} return;
}
void
sctp_queue_to_mcore(struct mbuf *m, int off, int cpu_to_use)
{ /* Queue a packet to a processor for the specified core */ struct sctp_mcore_queue *qent; struct sctp_mcore_ctrl *wkq; int need_wake = 0;
if (sctp_mcore_workers == NULL) { /* Something went way bad during setup */
sctp_input_with_port(m, off, 0); return;
}
SCTP_MALLOC(qent, struct sctp_mcore_queue *,
(sizeof(struct sctp_mcore_queue)),
SCTP_M_MCORE); if (qent == NULL) { /* This is trouble */
sctp_input_with_port(m, off, 0); return;
}
qent->vn = curvnet;
qent->m = m;
qent->off = off;
qent->v6 = 0;
wkq = &sctp_mcore_workers[cpu_to_use];
SCTP_MCORE_QLOCK(wkq);
TAILQ_INSERT_TAIL(&wkq->que, qent, next); if (wkq->running == 0) {
need_wake = 1;
}
SCTP_MCORE_QUNLOCK(wkq); if (need_wake) {
wakeup(&wkq->running);
}
}
SCTP_WQ_ADDR_INIT(); /* not sure if we need all the counts */
SCTP_BASE_INFO(ipi_count_ep) = 0; /* assoc/tcb zone info */
SCTP_BASE_INFO(ipi_count_asoc) = 0; /* local addrlist zone info */
SCTP_BASE_INFO(ipi_count_laddr) = 0; /* remote addrlist zone info */
SCTP_BASE_INFO(ipi_count_raddr) = 0; /* chunk info */
SCTP_BASE_INFO(ipi_count_chunk) = 0;
/* socket queue zone info */
SCTP_BASE_INFO(ipi_count_readq) = 0;
/* stream out queue cont */
SCTP_BASE_INFO(ipi_count_strmoq) = 0;
/* free the TIMEWAIT list elements malloc'd in the function *sctp_add_vtag_to_timewait()...
*/ for (i = 0; i < SCTP_STACK_VTAG_HASH_SIZE; i++) {
chain = &SCTP_BASE_INFO(vtag_timewait)[i]; if (!LIST_EMPTY(chain)) {
prev_twait_block = NULL;
LIST_FOREACH(twait_block, chain, sctp_nxt_tagblock) { if (prev_twait_block) {
SCTP_FREE(prev_twait_block, SCTP_M_TIMW);
}
prev_twait_block = twait_block;
}
SCTP_FREE(prev_twait_block, SCTP_M_TIMW);
}
}
/* First get the destination address setup too. */ #ifdef INET
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET; #ifdef HAVE_SIN_LEN
sin.sin_len = sizeof(sin); #endif
sin.sin_port = stcb->rport; #endif #ifdef INET6
memset(&sin6, 0, sizeof(sin6));
sin6.sin6_family = AF_INET6; #ifdef HAVE_SIN6_LEN
sin6.sin6_len = sizeof(struct sockaddr_in6); #endif
sin6.sin6_port = stcb->rport; #endif if (altsa) {
sa = altsa;
} else {
sa = src;
}
peer_supports_idata = 0;
peer_supports_ecn = 0;
peer_supports_prsctp = 0;
peer_supports_auth = 0;
peer_supports_asconf = 0;
peer_supports_asconf_ack = 0;
peer_supports_reconfig = 0;
peer_supports_nrsack = 0;
peer_supports_pktdrop = 0;
TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) { /* mark all addresses that we have currently on the list */
net->dest_state |= SCTP_ADDR_NOT_IN_ASSOC;
} /* does the source address already exist? if so skip it */
inp = stcb->sctp_ep;
atomic_add_int(&stcb->asoc.refcnt, 1);
stcb_tmp = sctp_findassociation_ep_addr(&inp, sa, &net_tmp, dst, stcb);
atomic_subtract_int(&stcb->asoc.refcnt, 1);
if ((stcb_tmp == NULL && inp == stcb->sctp_ep) || inp == NULL) { /* we must add the source address */ /* no scope set here since we have a tcb already. */ switch (sa->sa_family) { #ifdef INET case AF_INET: if (stcb->asoc.scope.ipv4_addr_legal) { if (sctp_add_remote_addr(stcb, sa, NULL, port, SCTP_DONOT_SETSCOPE, SCTP_LOAD_ADDR_2)) { return (-1);
}
} break; #endif #ifdef INET6 case AF_INET6: if (stcb->asoc.scope.ipv6_addr_legal) { if (sctp_add_remote_addr(stcb, sa, NULL, port, SCTP_DONOT_SETSCOPE, SCTP_LOAD_ADDR_3)) { return (-2);
}
} break; #endif #ifdefined(__Userspace__) case AF_CONN: if (stcb->asoc.scope.conn_addr_legal) { if (sctp_add_remote_addr(stcb, sa, NULL, port, SCTP_DONOT_SETSCOPE, SCTP_LOAD_ADDR_3)) { return (-2);
}
} break; #endif default: break;
}
} else { if (net_tmp != NULL && stcb_tmp == stcb) {
net_tmp->dest_state &= ~SCTP_ADDR_NOT_IN_ASSOC;
} elseif (stcb_tmp != stcb) { /* It belongs to another association? */ if (stcb_tmp)
SCTP_TCB_UNLOCK(stcb_tmp); return (-3);
}
} if (stcb->asoc.state == 0) { /* the assoc was freed? */ return (-4);
} /* now we must go through each of the params. */
phdr = sctp_get_next_param(m, offset, ¶m_buf, sizeof(param_buf)); while (phdr) {
ptype = ntohs(phdr->param_type);
plen = ntohs(phdr->param_length); /* *SCTP_PRINTF("ptype=>%0x,plen=>%d\n",(uint32_t)ptype, *(int)plen);
*/ if (offset + plen > limit) { break;
} if (plen < sizeof(struct sctp_paramhdr)) { break;
} #ifdef INET if (ptype == SCTP_IPV4_ADDRESS) { if (stcb->asoc.scope.ipv4_addr_legal) { struct sctp_ipv4addr_param *p4, p4_buf;
/* ok get the v4 address and check/add */
phdr = sctp_get_next_param(m, offset,
(struct sctp_paramhdr *)&p4_buf, sizeof(p4_buf)); if (plen != sizeof(struct sctp_ipv4addr_param) ||
phdr == NULL) { return (-5);
}
p4 = (struct sctp_ipv4addr_param *)phdr;
sin.sin_addr.s_addr = p4->addr; if (IN_MULTICAST(ntohl(sin.sin_addr.s_addr))) { /* Skip multi-cast addresses */ goto next_param;
} if ((sin.sin_addr.s_addr == INADDR_BROADCAST) ||
(sin.sin_addr.s_addr == INADDR_ANY)) { goto next_param;
}
sa = (struct sockaddr *)&sin;
inp = stcb->sctp_ep;
atomic_add_int(&stcb->asoc.refcnt, 1);
stcb_tmp = sctp_findassociation_ep_addr(&inp, sa, &net,
dst, stcb);
atomic_subtract_int(&stcb->asoc.refcnt, 1);
if ((stcb_tmp == NULL && inp == stcb->sctp_ep) ||
inp == NULL) { /* we must add the source address */ /* *noscopesetsincewehaveatcb *already
*/
/* *wemustvalidatethestateagain *here
*/
add_it_now: if (stcb->asoc.state == 0) { /* the assoc was freed? */ return (-7);
} if (sctp_add_remote_addr(stcb, sa, NULL, port, SCTP_DONOT_SETSCOPE, SCTP_LOAD_ADDR_4)) { return (-8);
}
} elseif (stcb_tmp == stcb) { if (stcb->asoc.state == 0) { /* the assoc was freed? */ return (-10);
} if (net != NULL) { /* clear flag */
net->dest_state &=
~SCTP_ADDR_NOT_IN_ASSOC;
}
} else { /* *strange,addressisinanother *assoc?straightenoutlocks.
*/ if (stcb_tmp) { if (SCTP_GET_STATE(stcb_tmp) == SCTP_STATE_COOKIE_WAIT) { struct mbuf *op_err; char msg[SCTP_DIAG_INFO_LEN];
/* in setup state we abort this guy */
SCTP_SNPRINTF(msg, sizeof(msg), "%s:%d at %s", __FILE__, __LINE__, __func__);
op_err = sctp_generate_cause(SCTP_BASE_SYSCTL(sctp_diag_info_code),
msg);
sctp_abort_an_association(stcb_tmp->sctp_ep,
stcb_tmp, op_err, false,
SCTP_SO_NOT_LOCKED); goto add_it_now;
}
SCTP_TCB_UNLOCK(stcb_tmp);
}
if (stcb->asoc.state == 0) { /* the assoc was freed? */ return (-12);
} return (-13);
}
}
} else #endif #ifdef INET6 if (ptype == SCTP_IPV6_ADDRESS) { if (stcb->asoc.scope.ipv6_addr_legal) { /* ok get the v6 address and check/add */ struct sctp_ipv6addr_param *p6, p6_buf;
phdr = sctp_get_next_param(m, offset,
(struct sctp_paramhdr *)&p6_buf, sizeof(p6_buf)); if (plen != sizeof(struct sctp_ipv6addr_param) ||
phdr == NULL) { return (-14);
}
p6 = (struct sctp_ipv6addr_param *)phdr;
memcpy((caddr_t)&sin6.sin6_addr, p6->addr, sizeof(p6->addr)); if (IN6_IS_ADDR_MULTICAST(&sin6.sin6_addr)) { /* Skip multi-cast addresses */ goto next_param;
} if (IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr)) { /* Link local make no sense without scope */ goto next_param;
}
sa = (struct sockaddr *)&sin6;
inp = stcb->sctp_ep;
atomic_add_int(&stcb->asoc.refcnt, 1);
stcb_tmp = sctp_findassociation_ep_addr(&inp, sa, &net,
dst, stcb);
atomic_subtract_int(&stcb->asoc.refcnt, 1); if (stcb_tmp == NULL &&
(inp == stcb->sctp_ep || inp == NULL)) { /* *wemustvalidatethestateagain *here
*/
add_it_now6: if (stcb->asoc.state == 0) { /* the assoc was freed? */ return (-16);
} /* *wemustaddtheaddress,noscope *set
*/ if (sctp_add_remote_addr(stcb, sa, NULL, port, SCTP_DONOT_SETSCOPE, SCTP_LOAD_ADDR_5)) { return (-17);
}
} elseif (stcb_tmp == stcb) { /* *wemustvalidatethestateagain *here
*/ if (stcb->asoc.state == 0) { /* the assoc was freed? */ return (-19);
} if (net != NULL) { /* clear flag */
net->dest_state &=
~SCTP_ADDR_NOT_IN_ASSOC;
}
} else { /* *strange,addressisinanother *assoc?straightenoutlocks.
*/ if (stcb_tmp) { if (SCTP_GET_STATE(stcb_tmp) == SCTP_STATE_COOKIE_WAIT) { struct mbuf *op_err; char msg[SCTP_DIAG_INFO_LEN];
/* in setup state we abort this guy */
SCTP_SNPRINTF(msg, sizeof(msg), "%s:%d at %s", __FILE__, __LINE__, __func__);
op_err = sctp_generate_cause(SCTP_BASE_SYSCTL(sctp_diag_info_code),
msg);
sctp_abort_an_association(stcb_tmp->sctp_ep,
stcb_tmp, op_err, false,
SCTP_SO_NOT_LOCKED); goto add_it_now6;
}
SCTP_TCB_UNLOCK(stcb_tmp);
} if (stcb->asoc.state == 0) { /* the assoc was freed? */ return (-21);
} return (-22);
}
}
} else #endif if (ptype == SCTP_ECN_CAPABLE) {
peer_supports_ecn = 1;
} elseif (ptype == SCTP_ULP_ADAPTATION) { if (stcb->asoc.state != SCTP_STATE_OPEN) { struct sctp_adaptation_layer_indication ai, *aip;
if (plen > sizeof(local_store)) { return (-35);
}
phdr = sctp_get_next_param(m, offset,
(struct sctp_paramhdr *)&local_store, plen); if (phdr == NULL) { return (-25);
}
pr_supported = (struct sctp_supported_chunk_types_param *)phdr;
num_ent = plen - sizeof(struct sctp_paramhdr); for (i = 0; i < num_ent; i++) { switch (pr_supported->chunk_types[i]) { case SCTP_ASCONF:
peer_supports_asconf = 1; break; case SCTP_ASCONF_ACK:
peer_supports_asconf_ack = 1; break; case SCTP_FORWARD_CUM_TSN:
peer_supports_prsctp = 1; break; case SCTP_PACKET_DROPPED:
peer_supports_pktdrop = 1; break; case SCTP_NR_SELECTIVE_ACK:
peer_supports_nrsack = 1; break; case SCTP_STREAM_RESET:
peer_supports_reconfig = 1; break; case SCTP_AUTHENTICATION:
peer_supports_auth = 1; break; case SCTP_IDATA:
peer_supports_idata = 1; break; default: /* one I have not learned yet */ break;
}
}
} elseif (ptype == SCTP_RANDOM) { if (plen > sizeof(random_store)) break; if (got_random) { /* already processed a RANDOM */ goto next_param;
}
phdr = sctp_get_next_param(m, offset,
(struct sctp_paramhdr *)random_store,
plen); if (phdr == NULL) return (-26);
p_random = (struct sctp_auth_random *)phdr;
random_len = plen - sizeof(*p_random); /* enforce the random length */ if (random_len != SCTP_AUTH_RANDOM_SIZE_REQUIRED) {
SCTPDBG(SCTP_DEBUG_AUTH1, "SCTP: invalid RANDOM len\n"); return (-27);
}
got_random = 1;
} elseif (ptype == SCTP_HMAC_LIST) {
uint16_t num_hmacs;
uint16_t i;
if (plen > sizeof(hmacs_store)) break; if (got_hmacs) { /* already processed a HMAC list */ goto next_param;
}
phdr = sctp_get_next_param(m, offset,
(struct sctp_paramhdr *)hmacs_store,
plen); if (phdr == NULL) return (-28);
hmacs = (struct sctp_auth_hmac_algo *)phdr;
hmacs_len = plen - sizeof(*hmacs);
num_hmacs = hmacs_len / sizeof(hmacs->hmac_ids[0]); /* validate the hmac list */ if (sctp_verify_hmac_param(hmacs, num_hmacs)) { return (-29);
} if (stcb->asoc.peer_hmacs != NULL)
sctp_free_hmaclist(stcb->asoc.peer_hmacs);
stcb->asoc.peer_hmacs = sctp_alloc_hmaclist(num_hmacs); if (stcb->asoc.peer_hmacs != NULL) { for (i = 0; i < num_hmacs; i++) {
(void)sctp_auth_add_hmacid(stcb->asoc.peer_hmacs,
ntohs(hmacs->hmac_ids[i]));
}
}
got_hmacs = 1;
} elseif (ptype == SCTP_CHUNK_LIST) { int i;
if (plen > sizeof(chunks_store)) break; if (got_chklist) { /* already processed a Chunks list */ goto next_param;
}
phdr = sctp_get_next_param(m, offset,
(struct sctp_paramhdr *)chunks_store,
plen); if (phdr == NULL) return (-30);
chunks = (struct sctp_auth_chunk_list *)phdr;
num_chunks = plen - sizeof(*chunks); if (stcb->asoc.peer_auth_chunks != NULL)
sctp_clear_chunklist(stcb->asoc.peer_auth_chunks); else
stcb->asoc.peer_auth_chunks = sctp_alloc_chunklist(); for (i = 0; i < num_chunks; i++) {
(void)sctp_auth_add_chunk(chunks->chunk_types[i],
stcb->asoc.peer_auth_chunks); /* record asconf/asconf-ack if listed */ if (chunks->chunk_types[i] == SCTP_ASCONF)
saw_asconf = 1; if (chunks->chunk_types[i] == SCTP_ASCONF_ACK)
saw_asconf_ack = 1;
}
got_chklist = 1;
} elseif ((ptype == SCTP_HEARTBEAT_INFO) ||
(ptype == SCTP_STATE_COOKIE) ||
(ptype == SCTP_UNRECOG_PARAM) ||
(ptype == SCTP_COOKIE_PRESERVE) ||
(ptype == SCTP_SUPPORTED_ADDRTYPE) ||
(ptype == SCTP_ADD_IP_ADDRESS) ||
(ptype == SCTP_DEL_IP_ADDRESS) ||
(ptype == SCTP_ERROR_CAUSE_IND) ||
(ptype == SCTP_SUCCESS_REPORT)) { /* don't care */
} else { if ((ptype & 0x8000) == 0x0000) { /* *muststopprocessingtherestofthe *param's.Anyreportbitswerehandled *withthecallto *sctp_arethere_unrecognized_parameters() *whentheINITorINIT-ACKwasfirstseen.
*/ break;
}
}
next_param:
offset += SCTP_SIZE32(plen); if (offset >= limit) { break;
}
phdr = sctp_get_next_param(m, offset, ¶m_buf, sizeof(param_buf));
} /* Now check to see if we need to purge any addresses */
TAILQ_FOREACH_SAFE(net, &stcb->asoc.nets, sctp_next, nnet) { if ((net->dest_state & SCTP_ADDR_NOT_IN_ASSOC) ==
SCTP_ADDR_NOT_IN_ASSOC) { /* This address has been removed from the asoc */ /* remove and free it */
stcb->asoc.numnets--;
TAILQ_REMOVE(&stcb->asoc.nets, net, sctp_next); if (net == stcb->asoc.alternate) {
sctp_free_remote_addr(stcb->asoc.alternate);
stcb->asoc.alternate = NULL;
} if (net == stcb->asoc.primary_destination) {
stcb->asoc.primary_destination = NULL;
sctp_select_primary_destination(stcb);
}
sctp_free_remote_addr(net);
}
} if ((stcb->asoc.ecn_supported == 1) &&
(peer_supports_ecn == 0)) {
stcb->asoc.ecn_supported = 0;
} if ((stcb->asoc.prsctp_supported == 1) &&
(peer_supports_prsctp == 0)) {
stcb->asoc.prsctp_supported = 0;
} if ((stcb->asoc.auth_supported == 1) &&
((peer_supports_auth == 0) ||
(got_random == 0) || (got_hmacs == 0))) {
stcb->asoc.auth_supported = 0;
} if ((stcb->asoc.asconf_supported == 1) &&
((peer_supports_asconf == 0) || (peer_supports_asconf_ack == 0) ||
(stcb->asoc.auth_supported == 0) ||
(saw_asconf == 0) || (saw_asconf_ack == 0))) {
stcb->asoc.asconf_supported = 0;
} if ((stcb->asoc.reconfig_supported == 1) &&
(peer_supports_reconfig == 0)) {
stcb->asoc.reconfig_supported = 0;
} if ((stcb->asoc.idata_supported == 1) &&
(peer_supports_idata == 0)) {
stcb->asoc.idata_supported = 0;
} if ((stcb->asoc.nrsack_supported == 1) &&
(peer_supports_nrsack == 0)) {
stcb->asoc.nrsack_supported = 0;
} if ((stcb->asoc.pktdrop_supported == 1) &&
(peer_supports_pktdrop == 0)) {
stcb->asoc.pktdrop_supported = 0;
} /* validate authentication required parameters */ if ((peer_supports_auth == 0) && (got_chklist == 1)) { /* peer does not support auth but sent a chunks list? */ return (-31);
} if ((peer_supports_asconf == 1) && (peer_supports_auth == 0)) { /* peer supports asconf but not auth? */ return (-32);
} elseif ((peer_supports_asconf == 1) &&
(peer_supports_auth == 1) &&
((saw_asconf == 0) || (saw_asconf_ack == 0))) { return (-33);
} /* concatenate the full random key */
keylen = sizeof(*p_random) + random_len + sizeof(*hmacs) + hmacs_len; if (chunks != NULL) {
keylen += sizeof(*chunks) + num_chunks;
}
new_key = sctp_alloc_key(keylen); if (new_key != NULL) { /* copy in the RANDOM */ if (p_random != NULL) {
keylen = sizeof(*p_random) + random_len;
memcpy(new_key->key, p_random, keylen);
} else {
keylen = 0;
} /* append in the AUTH chunks */ if (chunks != NULL) {
memcpy(new_key->key + keylen, chunks, sizeof(*chunks) + num_chunks);
keylen += sizeof(*chunks) + num_chunks;
} /* append in the HMACs */ if (hmacs != NULL) {
memcpy(new_key->key + keylen, hmacs, sizeof(*hmacs) + hmacs_len);
}
} else { /* failed to get memory for the key */ return (-34);
} if (stcb->asoc.authinfo.peer_random != NULL)
sctp_free_key(stcb->asoc.authinfo.peer_random);
stcb->asoc.authinfo.peer_random = new_key;
sctp_clear_cachedkeys(stcb, stcb->asoc.authinfo.assoc_keyid);
sctp_clear_cachedkeys(stcb, stcb->asoc.authinfo.recv_keyid);
return (0);
}
int
sctp_set_primary_addr(struct sctp_tcb *stcb, struct sockaddr *sa, struct sctp_nets *net)
{ /* make sure the requested primary address exists in the assoc */ if (net == NULL && sa)
net = sctp_findnet(stcb, sa);
if (net == NULL) { /* didn't find the requested primary address! */ return (-1);
} else { /* set the primary address */ if (net->dest_state & SCTP_ADDR_UNCONFIRMED) { /* Must be confirmed, so queue to set */
net->dest_state |= SCTP_ADDR_REQ_PRIMARY; return (0);
}
stcb->asoc.primary_destination = net; if (((net->dest_state & SCTP_ADDR_PF) == 0) &&
(stcb->asoc.alternate != NULL)) {
sctp_free_remote_addr(stcb->asoc.alternate);
stcb->asoc.alternate = NULL;
}
net = TAILQ_FIRST(&stcb->asoc.nets); if (net != stcb->asoc.primary_destination) { /* first one on the list is NOT the primary *sctp_cmpaddr()ismuchmoreefficientif *theprimaryisthefirstonthelist,makeit *so.
*/
TAILQ_REMOVE(&stcb->asoc.nets, stcb->asoc.primary_destination, sctp_next);
TAILQ_INSERT_HEAD(&stcb->asoc.nets, stcb->asoc.primary_destination, sctp_next);
} return (0);
}
}
head = &SCTP_BASE_INFO(sctp_asochash)[SCTP_PCBHASH_ASOC(tag, SCTP_BASE_INFO(hashasocmark))];
LIST_FOREACH(stcb, head, sctp_asocs) { /* We choose not to lock anything here. TCB's can't be *removedsincewehavethereadlock,sotheycan't *befreedonus,samethingfortheINP.Imay *bewrongwiththisassumption,butwewillgo *withitfornow:-)
*/ if (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_SOCKET_ALLGONE) { continue;
} if (stcb->asoc.my_vtag == tag) { /* candidate */ if (stcb->rport != rport) { continue;
} if (stcb->sctp_ep->sctp_lport != lport) { continue;
} /* The tag is currently used, so don't use it. */ return (false);
}
} return (!sctp_is_in_timewait(tag, lport, rport, now->tv_sec));
}
do {
old_flags = inp->sctp_flags;
new_flags = old_flags | flags;
} while (atomic_cmpset_int(&inp->sctp_flags, old_flags, new_flags) == 0);
}
Messung V0.5 in Prozent
¤ 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.322Bemerkung:
¤
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.