/*- * 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.
*/
tmp_a = a->sin6_addr; if (in6_embedscope(&tmp_a, a) != 0) { return (0);
}
tmp_b = b->sin6_addr; if (in6_embedscope(&tmp_b, b) != 0) { return (0);
} return (IN6_ARE_ADDR_EQUAL(&tmp_a, &tmp_b)); #endif #else return (IN6_ARE_ADDR_EQUAL(&(a->sin6_addr), &(b->sin6_addr))); #endif/* SCTP_EMBEDDED_V6_SCOPE */
} #endif
void
sctp_fill_pcbinfo(struct sctp_pcbinfo *spcb)
{ /* * We really don't need to lock this, but I will just because it * does not hurt.
*/
SCTP_INP_INFO_RLOCK();
spcb->ep_count = SCTP_BASE_INFO(ipi_count_ep);
spcb->asoc_count = SCTP_BASE_INFO(ipi_count_asoc);
spcb->laddr_count = SCTP_BASE_INFO(ipi_count_laddr);
spcb->raddr_count = SCTP_BASE_INFO(ipi_count_raddr);
spcb->chk_count = SCTP_BASE_INFO(ipi_count_chunk);
spcb->readq_count = SCTP_BASE_INFO(ipi_count_readq);
spcb->stream_oque = SCTP_BASE_INFO(ipi_count_strmoq);
spcb->free_chunks = SCTP_BASE_INFO(ipi_free_chunks);
SCTP_INP_INFO_RUNLOCK();
}
/*- * Addresses are added to VRF's (Virtual Router's). For BSD we * have only the default VRF 0. We maintain a hash list of * VRF's. Each VRF has its own list of sctp_ifn's. Each of * these has a list of addresses. When we add a new address * to a VRF we lookup the ifn/ifn_index, if the ifn does * not exist we create it and add it to the list of IFN's * within the VRF. Once we have the sctp_ifn, we add the * address to the list. So we look something like: * * hash-vrf-table * vrf-> ifn-> ifn -> ifn * vrf | * ... +--ifa-> ifa -> ifa * vrf * * We keep these separate lists since the SCTP subsystem will * point to these from its source address selection nets structure. * When an address is deleted it does not happen right away on * the SCTP side, it gets scheduled. What we do when a * delete happens is immediately remove the address from * the master list and decrement the refcount. As our * addip iterator works through and frees the src address * selection pointing to the sctp_ifa, eventually the refcount * will reach 0 and we will delete it. Note that it is assumed * that any locking on system level ifn/ifa is done at the * caller of these functions and these routines will only * lock the SCTP structures as they add or delete things. * * Other notes on VRF concepts. * - An endpoint can be in multiple VRF's * - An association lives within a VRF and only one VRF. * - Any incoming packet we can deduce the VRF for by * looking at the mbuf/pak inbound (for BSD its VRF=0 :D) * - Any downward send call or connect call must supply the * VRF via ancillary data or via some sort of set default * VRF socket option call (again for BSD no brainer since * the VRF is always 0). * - An endpoint may add multiple VRF's to it. * - Listening sockets can accept associations in any * of the VRF's they are in but the assoc will end up * in only one VRF (gotten from the packet or connect/send). *
*/
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);
}
/*- * Add an ifa to an ifn. * Register the interface as necessary. * NOTE: ADDR write lock MUST be held.
*/ staticvoid
sctp_add_ifa_to_ifn(struct sctp_ifn *sctp_ifnp, struct sctp_ifa *sctp_ifap)
{ int ifa_af;
/*- * Remove an ifa from its ifn. * If no more addresses exist, remove the ifn too. Otherwise, re-register * the interface based on the remaining address families left. * NOTE: ADDR write lock MUST be held.
*/ staticvoid
sctp_remove_ifa_from_ifn(struct sctp_ifa *sctp_ifap)
{
LIST_REMOVE(sctp_ifap, next_ifa); if (sctp_ifap->ifn_p) { /* update address counts */
sctp_ifap->ifn_p->ifa_count--; switch (sctp_ifap->address.sa.sa_family) { #ifdef INET case AF_INET:
sctp_ifap->ifn_p->num_v4--; break; #endif #ifdef INET6 case AF_INET6:
sctp_ifap->ifn_p->num_v6--; break; #endif default: break;
}
if (LIST_EMPTY(&sctp_ifap->ifn_p->ifalist)) { /* remove the ifn, possibly freeing it */
sctp_delete_ifn(sctp_ifap->ifn_p, SCTP_ADDR_LOCKED);
} else { /* re-register address family type, if needed */ if ((sctp_ifap->ifn_p->num_v6 == 0) &&
(sctp_ifap->ifn_p->registered_af == AF_INET6)) {
sctp_ifap->ifn_p->registered_af = AF_INET;
} elseif ((sctp_ifap->ifn_p->num_v4 == 0) &&
(sctp_ifap->ifn_p->registered_af == AF_INET)) {
sctp_ifap->ifn_p->registered_af = AF_INET6;
} /* free the ifn refcount */
sctp_free_ifn(sctp_ifap->ifn_p);
}
sctp_ifap->ifn_p = NULL;
}
}
if (dynamic_add) { /* Bump up the refcount so that when the timer * completes it will drop back down.
*/ struct sctp_laddr *wi;
atomic_add_int(&sctp_ifap->refcount, 1);
wi = SCTP_ZONE_GET(SCTP_BASE_INFO(ipi_zone_laddr), struct sctp_laddr); if (wi == NULL) { /* * Gak, what can we do? We have lost an address * change can you say HOSED?
*/
SCTPDBG(SCTP_DEBUG_PCB4, "Lost an address change?\n"); /* Opps, must decrement the count */
sctp_del_addr_from_vrf(vrf_id, addr, ifn_index,
if_name); return (NULL);
}
SCTP_INCR_LADDR_COUNT();
memset(wi, 0, sizeof(*wi));
(void)SCTP_GETTIME_TIMEVAL(&wi->start_time);
wi->ifa = sctp_ifap;
wi->action = SCTP_ADD_IP_ADDRESS;
#ifdef SCTP_DEBUG
SCTPDBG(SCTP_DEBUG_PCB4, "vrf_id 0x%x: deleting address:", vrf_id);
SCTPDBG_ADDR(SCTP_DEBUG_PCB4, addr); #endif
sctp_ifap = sctp_find_ifa_by_addr(addr, vrf->vrf_id, SCTP_ADDR_LOCKED); if (sctp_ifap) { /* Validate the delete */ if (sctp_ifap->ifn_p) { int valid = 0; /*- * The name has priority over the ifn_index * if its given.
*/ if (if_name) { if (strncmp(if_name, sctp_ifap->ifn_p->ifn_name, SCTP_IFNAMSIZ) == 0) { /* They match its a correct delete */
valid = 1;
}
} if (!valid) { /* last ditch check ifn_index */ if (ifn_index == sctp_ifap->ifn_p->ifn_index) {
valid = 1;
}
} if (!valid) {
SCTPDBG(SCTP_DEBUG_PCB4, "ifn:%d ifname:%s does not match addresses\n",
ifn_index, ((if_name == NULL) ? "NULL" : if_name));
SCTPDBG(SCTP_DEBUG_PCB4, "ifn:%d ifname:%s - ignoring delete\n",
sctp_ifap->ifn_p->ifn_index, sctp_ifap->ifn_p->ifn_name);
SCTP_IPI_ADDR_WUNLOCK(); return;
}
}
SCTPDBG(SCTP_DEBUG_PCB4, "Deleting ifa %p\n", (void *)sctp_ifap);
sctp_ifap->localifa_flags &= SCTP_ADDR_VALID; /* * We don't set the flag. This means that the structure will * hang around in EP's that have bound specific to it until * they close. This gives us TCP like behavior if someone * removes an address (or for that matter adds it right back).
*/ /* sctp_ifap->localifa_flags |= SCTP_BEING_DELETED; */
vrf->total_ifa_count--;
LIST_REMOVE(sctp_ifap, next_bucket);
sctp_remove_ifa_from_ifn(sctp_ifap);
} #ifdef SCTP_DEBUG else {
SCTPDBG(SCTP_DEBUG_PCB4, "Del Addr-ifn:%d Could not find address:",
ifn_index);
SCTPDBG_ADDR(SCTP_DEBUG_PCB1, addr);
} #endif
out_now:
SCTP_IPI_ADDR_WUNLOCK(); if (sctp_ifap) { struct sctp_laddr *wi;
wi = SCTP_ZONE_GET(SCTP_BASE_INFO(ipi_zone_laddr), struct sctp_laddr); if (wi == NULL) { /* * Gak, what can we do? We have lost an address * change can you say HOSED?
*/
SCTPDBG(SCTP_DEBUG_PCB4, "Lost an address change?\n");
/* Oops, must decrement the count */
sctp_free_ifa(sctp_ifap); return;
}
SCTP_INCR_LADDR_COUNT();
memset(wi, 0, sizeof(*wi));
(void)SCTP_GETTIME_TIMEVAL(&wi->start_time);
wi->ifa = sctp_ifap;
wi->action = SCTP_DEL_IP_ADDRESS;
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;
}
staticint
sctp_does_stcb_own_this_addr(struct sctp_tcb *stcb, struct sockaddr *to)
{ 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;
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) {
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) &&
(!sctp_is_addr_pending(stcb, sctp_ifa))) { /* We allow pending addresses, where we * have sent an asconf-add to be considered * valid.
*/ continue;
} if (sctp_ifa->address.sa.sa_family != to->sa_family) { continue;
} switch (sctp_ifa->address.sa.sa_family) { #ifdef INET case AF_INET: if (ipv4_addr_legal) { struct sockaddr_in *sin, *rsin;
sin = &sctp_ifa->address.sin;
rsin = (struct sockaddr_in *)to; if ((ipv4_local_scope == 0) &&
IN4_ISPRIVATE_ADDRESS(&sin->sin_addr)) { continue;
} #ifdefined(__FreeBSD__) && !defined(__Userspace__) if (prison_check_ip4(stcb->sctp_ep->ip_inp.inp.inp_cred,
&sin->sin_addr) != 0) { continue;
} #endif if (sin->sin_addr.s_addr == rsin->sin_addr.s_addr) {
SCTP_IPI_ADDR_RUNLOCK(); return (1);
}
} break; #endif #ifdef INET6 case AF_INET6: if (ipv6_addr_legal) { struct sockaddr_in6 *sin6, *rsin6; #ifdefined(SCTP_EMBEDDED_V6_SCOPE) && !defined(SCTP_KAME) struct sockaddr_in6 lsa6; #endif
sin6 = &sctp_ifa->address.sin6;
rsin6 = (struct sockaddr_in6 *)to; #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) continue; #else
lsa6 = *sin6; if (in6_recoverscope(&lsa6,
&lsa6.sin6_addr,
NULL)) continue;
sin6 = &lsa6; #endif/* SCTP_KAME */
} #endif/* SCTP_EMBEDDED_V6_SCOPE */
} if ((site_scope == 0) &&
(IN6_IS_ADDR_SITELOCAL(&sin6->sin6_addr))) { continue;
} if (SCTP6_ARE_ADDR_EQUAL(sin6, rsin6)) {
SCTP_IPI_ADDR_RUNLOCK(); return (1);
}
} break; #endif #ifdefined(__Userspace__) case AF_CONN: if (conn_addr_legal) { struct sockaddr_conn *sconn, *rsconn;
LIST_FOREACH(laddr, &stcb->sctp_ep->sctp_addr_list, sctp_nxt_addr) { if (laddr->ifa->localifa_flags & SCTP_BEING_DELETED) {
SCTPDBG(SCTP_DEBUG_PCB1, "ifa being deleted\n"); continue;
} if (sctp_is_addr_restricted(stcb, laddr->ifa) &&
(!sctp_is_addr_pending(stcb, laddr->ifa))) { /* We allow pending addresses, where we * have sent an asconf-add to be considered * valid.
*/ continue;
} if (laddr->ifa->address.sa.sa_family != to->sa_family) { continue;
} switch (to->sa_family) { #ifdef INET case AF_INET:
{ struct sockaddr_in *sin, *rsin;
sin = &laddr->ifa->address.sin;
rsin = (struct sockaddr_in *)to; if (sin->sin_addr.s_addr == rsin->sin_addr.s_addr) {
SCTP_IPI_ADDR_RUNLOCK(); return (1);
} break;
} #endif #ifdef INET6 case AF_INET6:
{ struct sockaddr_in6 *sin6, *rsin6;
staticstruct sctp_tcb *
sctp_tcb_special_locate(struct sctp_inpcb **inp_p, struct sockaddr *from, struct sockaddr *to, struct sctp_nets **netp, uint32_t vrf_id)
{ /**** ASSUMES THE CALLER holds the INP_INFO_RLOCK */ /* * If we support the TCP model, then we must now dig through to see * if we can find our endpoint in the list of tcp ep's.
*/
uint16_t lport, rport; struct sctppcbhead *ephead; struct sctp_inpcb *inp; struct sctp_laddr *laddr; struct sctp_tcb *stcb; struct sctp_nets *net; #ifdef SCTP_MVRF int fnd, i; #endif
switch (to->sa_family) { #ifdef INET case AF_INET: if (from->sa_family == AF_INET) {
lport = ((struct sockaddr_in *)to)->sin_port;
rport = ((struct sockaddr_in *)from)->sin_port;
} else { return (NULL);
} break; #endif #ifdef INET6 case AF_INET6: if (from->sa_family == AF_INET6) {
lport = ((struct sockaddr_in6 *)to)->sin6_port;
rport = ((struct sockaddr_in6 *)from)->sin6_port;
} else { return (NULL);
} break; #endif #ifdefined(__Userspace__) case AF_CONN: if (from->sa_family == AF_CONN) {
lport = ((struct sockaddr_conn *)to)->sconn_port;
rport = ((struct sockaddr_conn *)from)->sconn_port;
} else { return (NULL);
} break; #endif default: return (NULL);
}
ephead = &SCTP_BASE_INFO(sctp_tcpephash)[SCTP_PCBHASH_ALLADDR((lport | rport), SCTP_BASE_INFO(hashtcpmark))]; /* * Ok now for each of the guys in this bucket we must look and see: * - Does the remote port match. - Does there single association's * addresses match this address (to). If so we update p_ep to point * to this ep and return the tcb from it.
*/
LIST_FOREACH(inp, ephead, sctp_hash) {
SCTP_INP_RLOCK(inp); if (inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_ALLGONE) {
SCTP_INP_RUNLOCK(inp); continue;
} if (lport != inp->sctp_lport) {
SCTP_INP_RUNLOCK(inp); continue;
} #ifdefined(__FreeBSD__) && !defined(__Userspace__) switch (to->sa_family) { #ifdef INET case AF_INET:
{ struct sockaddr_in *sin;
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;
}
} /* * Ok if we hit here the ep has the address, does it hold * the tcb?
*/ /* 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;
/* * rules for use * * 1) If I return a NULL you must decrement any INP ref cnt. 2) If I find an * stcb, both will be locked (locked_tcb and stcb) but decrement will be done * (if locked == NULL). 3) Decrement happens on return ONLY if locked == * NULL.
*/
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-lock so we can do proper locking here this occurs when * called from load_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)) { /*- * Now either this guy is our listener or it's the * connector. If it is the one that issued the connect, then * it's only chance is to be the first TCB in the list. If * it is the acceptor, then do the special_lookup to hash * and find the real inp.
*/ 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 /*- * MVRF is tricky, we must look in every VRF * the endpoint has.
*/ 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);
}
sin6 = (struct sockaddr_in6 *)&net->ro._l_addr;
rsin6 = (struct sockaddr_in6 *)remote; if (SCTP6_ARE_ADDR_EQUAL(sin6,
rsin6)) { /* 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);
}
SCTP_INP_WUNLOCK(inp);
SCTP_INP_INFO_RUNLOCK(); return (stcb);
} break;
} #endif #ifdefined(__Userspace__) case AF_CONN:
{ struct sockaddr_conn *sconn, *rsconn;
sconn = (struct sockaddr_conn *)&net->ro._l_addr;
rsconn = (struct sockaddr_conn *)remote; if (sconn->sconn_addr == rsconn->sconn_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);
}
SCTP_INP_WUNLOCK(inp);
SCTP_INP_INFO_RUNLOCK(); return (stcb);
} break;
} #endif default: /* TSNH */ break;
}
}
SCTP_TCB_UNLOCK(stcb);
}
} else {
SCTP_INP_WLOCK(inp); if (inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_ALLGONE) { goto null_return;
}
head = &inp->sctp_tcbhash[SCTP_PCBHASH_ALLADDR(rport,
inp->sctp_hashmark)];
LIST_FOREACH(stcb, head, sctp_tcbhash) { if (stcb->rport != rport) { /* remote port does not match */ continue;
}
SCTP_TCB_LOCK(stcb); if (stcb->asoc.state & SCTP_STATE_ABOUT_TO_BE_FREED) {
SCTP_TCB_UNLOCK(stcb); continue;
} if (local && !sctp_does_stcb_own_this_addr(stcb, local)) {
SCTP_TCB_UNLOCK(stcb); continue;
} /* 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);
}
SCTP_INP_WUNLOCK(inp);
SCTP_INP_INFO_RUNLOCK(); 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 *)remote; if (SCTP6_ARE_ADDR_EQUAL(sin6,
rsin6)) { /* 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);
}
SCTP_INP_WUNLOCK(inp);
SCTP_INP_INFO_RUNLOCK(); return (stcb);
} break;
} #endif #ifdefined(__Userspace__) case AF_CONN:
{ struct sockaddr_conn *sconn, *rsconn;
sconn = (struct sockaddr_conn *)&net->ro._l_addr;
rsconn = (struct sockaddr_conn *)remote; if (sconn->sconn_addr == rsconn->sconn_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);
}
SCTP_INP_WUNLOCK(inp);
SCTP_INP_INFO_RUNLOCK(); return (stcb);
} break;
} #endif default: /* TSNH */ break;
}
}
SCTP_TCB_UNLOCK(stcb);
}
}
null_return: /* clean up for returning null */ if (locked_tcb) {
SCTP_TCB_LOCK(locked_tcb);
atomic_subtract_int(&locked_tcb->asoc.refcnt, 1);
}
SCTP_INP_WUNLOCK(inp);
SCTP_INP_INFO_RUNLOCK(); /* not found */ return (NULL);
}
/* * Find an association for a specific endpoint using the association id given * out in the COMM_UP notification
*/ struct sctp_tcb *
sctp_findasoc_ep_asocid_locked(struct sctp_inpcb *inp, sctp_assoc_t asoc_id, int want_lock)
{ /* * Use my the assoc_id to find a endpoint
*/ struct sctpasochead *head; struct sctp_tcb *stcb;
uint32_t id;
if (inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_ALLGONE) {
SCTP_PRINTF("TSNH ep_associd0\n"); return (NULL);
}
id = (uint32_t)asoc_id;
head = &inp->sctp_asocidhash[SCTP_PCBHASH_ASOC(id, inp->hashasocidmark)]; if (head == NULL) { /* invalid id TSNH */
SCTP_PRINTF("TSNH ep_associd1\n"); return (NULL);
}
LIST_FOREACH(stcb, head, sctp_tcbasocidhash) { if (stcb->asoc.assoc_id == id) { if (inp != stcb->sctp_ep) { /* * some other guy has the same id active (id * collision ??).
*/
SCTP_PRINTF("TSNH ep_associd2\n"); continue;
} if (stcb->asoc.state & SCTP_STATE_ABOUT_TO_BE_FREED) { continue;
} if (want_lock) {
SCTP_TCB_LOCK(stcb);
} return (stcb);
}
} return (NULL);
}
#ifdef INET
sin = NULL; #endif #ifdef INET6
sin6 = NULL; #endif #ifdefined(__Userspace__)
sconn = NULL; #endif switch (nam->sa_family) { #ifdef INET case AF_INET:
sin = (struct sockaddr_in *)nam; break; #endif #ifdef INET6 case AF_INET6:
sin6 = (struct sockaddr_in6 *)nam; break; #endif #ifdefined(__Userspace__) case AF_CONN:
sconn = (struct sockaddr_conn *)nam; break; #endif default: /* unsupported family */ return (NULL);
}
if (head == NULL) return (NULL);
LIST_FOREACH(inp, head, sctp_hash) {
SCTP_INP_RLOCK(inp); if (inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_ALLGONE) {
SCTP_INP_RUNLOCK(inp); continue;
} if ((inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) &&
(inp->sctp_lport == lport)) { /* got it */ switch (nam->sa_family) { #ifdef INET case AF_INET: if ((inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) &&
SCTP_IPV6_V6ONLY(inp)) { /* IPv4 on a IPv6 socket with ONLY IPv6 set */
SCTP_INP_RUNLOCK(inp); continue;
} #ifdefined(__FreeBSD__) && !defined(__Userspace__) if (prison_check_ip4(inp->ip_inp.inp.inp_cred,
&sin->sin_addr) != 0) {
SCTP_INP_RUNLOCK(inp); continue;
} #endif break; #endif #ifdef INET6 case AF_INET6: /* A V6 address and the endpoint is NOT bound V6 */ if ((inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) == 0) {
SCTP_INP_RUNLOCK(inp); continue;
} #ifdefined(__FreeBSD__) && !defined(__Userspace__) if (prison_check_ip6(inp->ip_inp.inp.inp_cred,
&sin6->sin6_addr) != 0) {
SCTP_INP_RUNLOCK(inp); continue;
} #endif break; #endif default: break;
} /* does a VRF id match? */
fnd = 0; #ifdef SCTP_MVRF for (i = 0; i < inp->num_vrfs; i++) { if (inp->m_vrf_ids[i] == vrf_id) {
fnd = 1; break;
}
} #else if (inp->def_vrf_id == vrf_id)
fnd = 1; #endif
SCTP_INP_RUNLOCK(inp); if (!fnd) continue; return (inp);
}
SCTP_INP_RUNLOCK(inp);
} switch (nam->sa_family) { #ifdef INET case AF_INET: if (sin->sin_addr.s_addr == INADDR_ANY) { /* Can't hunt for one that has no address specified */ return (NULL);
} break; #endif #ifdef INET6 case AF_INET6: if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { /* Can't hunt for one that has no address specified */ return (NULL);
} break; #endif #ifdefined(__Userspace__) case AF_CONN: if (sconn->sconn_addr == NULL) { return (NULL);
} break; #endif default: break;
} /* * ok, not bound to all so see if we can find a EP bound to this * address.
*/
LIST_FOREACH(inp, head, sctp_hash) {
SCTP_INP_RLOCK(inp); if (inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_ALLGONE) {
SCTP_INP_RUNLOCK(inp); continue;
} if ((inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL)) {
SCTP_INP_RUNLOCK(inp); continue;
} /* * Ok this could be a likely candidate, look at all of its * addresses
*/ if (inp->sctp_lport != lport) {
SCTP_INP_RUNLOCK(inp); continue;
} /* does a VRF id match? */
fnd = 0; #ifdef SCTP_MVRF for (i = 0; i < inp->num_vrfs; i++) { if (inp->m_vrf_ids[i] == vrf_id) {
fnd = 1; break;
}
} #else if (inp->def_vrf_id == vrf_id)
fnd = 1;
#endif if (!fnd) {
SCTP_INP_RUNLOCK(inp); continue;
}
LIST_FOREACH(laddr, &inp->sctp_addr_list, sctp_nxt_addr) { if (laddr->ifa == NULL) {
SCTPDBG(SCTP_DEBUG_PCB1, "%s: NULL ifa\n",
__func__); continue;
}
SCTPDBG(SCTP_DEBUG_PCB1, "Ok laddr->ifa:%p is possible, ",
(void *)laddr->ifa); if (laddr->ifa->localifa_flags & SCTP_BEING_DELETED) {
SCTPDBG(SCTP_DEBUG_PCB1, "Huh IFA being deleted\n"); continue;
} if (laddr->ifa->address.sa.sa_family == nam->sa_family) { /* possible, see if it matches */ switch (nam->sa_family) { #ifdef INET case AF_INET: #ifdefined(__APPLE__) && !defined(__Userspace__) if (sin == NULL) { /* TSNH */ break;
} #endif if (sin->sin_addr.s_addr ==
laddr->ifa->address.sin.sin_addr.s_addr) {
SCTP_INP_RUNLOCK(inp); return (inp);
} break; #endif #ifdef INET6 case AF_INET6:
intf_addr6 = &laddr->ifa->address.sin6; if (SCTP6_ARE_ADDR_EQUAL(sin6,
intf_addr6)) {
SCTP_INP_RUNLOCK(inp); return (inp);
} break; #endif #ifdefined(__Userspace__) case AF_CONN: if (sconn->sconn_addr == laddr->ifa->address.sconn.sconn_addr) {
SCTP_INP_RUNLOCK(inp); return (inp);
} break; #endif
}
}
}
SCTP_INP_RUNLOCK(inp);
} return (NULL);
}
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);
}
struct sctp_inpcb *
sctp_pcb_findep(struct sockaddr *nam, int find_tcp_pool, int have_lock,
uint32_t vrf_id)
{ /* * First we check the hash table to see if someone has this port * bound with just the port.
*/ struct sctp_inpcb *inp; struct sctppcbhead *head; int lport; unsignedint i; #ifdef INET struct sockaddr_in *sin; #endif #ifdef INET6 struct sockaddr_in6 *sin6; #endif #ifdefined(__Userspace__) struct sockaddr_conn *sconn; #endif
switch (nam->sa_family) { #ifdef INET case AF_INET:
sin = (struct sockaddr_in *)nam;
lport = sin->sin_port; break; #endif #ifdef INET6 case AF_INET6:
sin6 = (struct sockaddr_in6 *)nam;
lport = sin6->sin6_port; break; #endif #ifdefined(__Userspace__) case AF_CONN:
sconn = (struct sockaddr_conn *)nam;
lport = sconn->sconn_port; break; #endif default: return (NULL);
} /* * I could cheat here and just cast to one of the types but we will * do it right. It also provides the check against an Unsupported * type too.
*/ /* Find the head of the ALLADDR chain */ if (have_lock == 0) {
SCTP_INP_INFO_RLOCK();
}
head = &SCTP_BASE_INFO(sctp_ephash)[SCTP_PCBHASH_ALLADDR(lport,
SCTP_BASE_INFO(hashmark))];
inp = sctp_endpoint_probe(nam, head, lport, vrf_id);
/* * If the TCP model exists it could be that the main listening * endpoint is gone but there still exists a connected socket for this * guy. If so we can return the first one that we find. This may NOT * be the correct one so the caller should be wary on the returned INP. * Currently the only caller that sets find_tcp_pool is in bindx where * we are verifying that a user CAN bind the address. He either * has bound it already, or someone else has, or its open to bind, * so this is good enough.
*/ if (inp == NULL && find_tcp_pool) { for (i = 0; i < SCTP_BASE_INFO(hashtcpmark) + 1; i++) {
head = &SCTP_BASE_INFO(sctp_tcpephash)[i];
inp = sctp_endpoint_probe(nam, head, lport, vrf_id); if (inp) { break;
}
}
} if (inp) {
SCTP_INP_INCR_REF(inp);
} if (have_lock == 0) {
SCTP_INP_INFO_RUNLOCK();
} return (inp);
}
/* * Find an association for an endpoint with the pointer to whom you want to * send to and the endpoint pointer. The address can be IPv4 or IPv6. We may * need to change the *to to some other struct like a mbuf...
*/ struct sctp_tcb *
sctp_findassociation_addr_sa(struct sockaddr *from, struct sockaddr *to, struct sctp_inpcb **inp_p, struct sctp_nets **netp, int find_tcp_pool,
uint32_t vrf_id)
{ struct sctp_inpcb *inp = NULL; struct sctp_tcb *stcb;
SCTP_INP_INFO_RLOCK(); if (find_tcp_pool) { if (inp_p != NULL) {
stcb = sctp_tcb_special_locate(inp_p, from, to, netp,
vrf_id);
} else {
stcb = sctp_tcb_special_locate(&inp, from, to, netp,
vrf_id);
} if (stcb != NULL) {
SCTP_INP_INFO_RUNLOCK(); return (stcb);
}
}
inp = sctp_pcb_findep(to, 0, 1, vrf_id); if (inp_p != NULL) {
*inp_p = inp;
}
SCTP_INP_INFO_RUNLOCK(); if (inp == NULL) { return (NULL);
} /* * ok, we have an endpoint, now lets find the assoc for it (if any) * we now place the source address or from in the to of the find * endpoint call. Since in reality this chain is used from the * inbound packet side.
*/ if (inp_p != NULL) {
stcb = sctp_findassociation_ep_addr(inp_p, from, netp, to,
NULL);
} else {
stcb = sctp_findassociation_ep_addr(&inp, from, netp, to,
NULL);
} return (stcb);
}
/* * This routine will grub through the mbuf that is a INIT or INIT-ACK and * find all addresses that the sender has specified in any address list. Each * address will be used to lookup the TCB and see if one exits.
*/ staticstruct sctp_tcb *
sctp_findassociation_special_addr(struct mbuf *m, int offset, struct sctphdr *sh, struct sctp_inpcb **inp_p, struct sctp_nets **netp, struct sockaddr *dst)
{ struct sctp_paramhdr *phdr, param_buf; #ifdefined(INET) || defined(INET6) struct sctp_tcb *stcb;
uint16_t ptype; #endif
uint16_t plen; #ifdef INET struct sockaddr_in sin4; #endif #ifdef INET6 struct sockaddr_in6 sin6; #endif
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;
staticstruct sctp_tcb *
sctp_findassoc_by_vtag(struct sockaddr *from, struct sockaddr *to, uint32_t vtag, struct sctp_inpcb **inp_p, struct sctp_nets **netp, uint16_t rport,
uint16_t lport, int skip_src_check, uint32_t vrf_id, uint32_t remote_tag)
{ /* * Use my vtag to hash. If we find it we then verify the source addr * is in the assoc. If all goes well we save a bit on rec of a * packet.
*/ struct sctpasochead *head; struct sctp_nets *net; struct sctp_tcb *stcb; #ifdef SCTP_MVRF unsignedint i; #endif
SCTP_INP_INFO_RLOCK();
head = &SCTP_BASE_INFO(sctp_asochash)[SCTP_PCBHASH_ASOC(vtag,
SCTP_BASE_INFO(hashasocmark))];
LIST_FOREACH(stcb, head, sctp_asocs) {
SCTP_INP_RLOCK(stcb->sctp_ep); if (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_SOCKET_ALLGONE) {
SCTP_INP_RUNLOCK(stcb->sctp_ep); continue;
} #ifdef SCTP_MVRF for (i = 0; i < stcb->sctp_ep->num_vrfs; i++) { if (stcb->sctp_ep->m_vrf_ids[i] == vrf_id) { break;
}
} if (i == stcb->sctp_ep->num_vrfs) {
SCTP_INP_RUNLOCK(inp); continue;
} #else if (stcb->sctp_ep->def_vrf_id != vrf_id) {
SCTP_INP_RUNLOCK(stcb->sctp_ep); continue;
} #endif
SCTP_TCB_LOCK(stcb);
SCTP_INP_RUNLOCK(stcb->sctp_ep); if (stcb->asoc.my_vtag == vtag) { /* candidate */ if (stcb->rport != rport) {
SCTP_TCB_UNLOCK(stcb); continue;
} if (stcb->sctp_ep->sctp_lport != lport) {
SCTP_TCB_UNLOCK(stcb); continue;
} if (stcb->asoc.state & SCTP_STATE_ABOUT_TO_BE_FREED) {
SCTP_TCB_UNLOCK(stcb); continue;
} /* RRS:Need toaddr check here */ if (sctp_does_stcb_own_this_addr(stcb, to) == 0) { /* Endpoint does not own this address */
SCTP_TCB_UNLOCK(stcb); continue;
} if (remote_tag) { /* If we have both vtags that's all we match on */ if (stcb->asoc.peer_vtag == remote_tag) { /* If both tags match we consider it conclusive * and check NO source/destination addresses
*/ goto conclusive;
}
} if (skip_src_check) {
conclusive: if (from) {
*netp = sctp_findnet(stcb, from);
} else {
*netp = NULL; /* unknown */
} if (inp_p)
*inp_p = stcb->sctp_ep;
SCTP_INP_INFO_RUNLOCK(); return (stcb);
}
net = sctp_findnet(stcb, from); if (net) {
--> --------------------
--> maximum size reached
--> --------------------
Messung V0.5
¤ Dauer der Verarbeitung: 0.54 Sekunden
(vorverarbeitet)
¤
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.