Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  sctp_usrreq.c   Sprache: C

 
/*-
 * 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.
 */


#include <netinet/sctp_os.h>
#if defined(__FreeBSD__) && !defined(__Userspace__)
#include <sys/proc.h>
#endif
#include <netinet/sctp_pcb.h>
#include <netinet/sctp_header.h>
#include <netinet/sctp_var.h>
#ifdef INET6
#include <netinet6/sctp6_var.h>
#endif
#include <netinet/sctp_sysctl.h>
#include <netinet/sctp_output.h>
#include <netinet/sctp_uio.h>
#include <netinet/sctp_asconf.h>
#include <netinet/sctputil.h>
#include <netinet/sctp_indata.h>
#include <netinet/sctp_timer.h>
#include <netinet/sctp_auth.h>
#include <netinet/sctp_bsd_addr.h>
#if defined(__Userspace__)
#include <netinet/sctp_callout.h>
#else
#include <netinet/udp.h>
#endif
#if defined(__FreeBSD__) && !defined(__Userspace__)
#include <sys/eventhandler.h>
#endif
#if defined(HAVE_SCTP_PEELOFF_SOCKOPT)
#include <netinet/sctp_peeloff.h>
#endif    /* HAVE_SCTP_PEELOFF_SOCKOPT */

extern const struct sctp_cc_functions sctp_cc_functions[];
extern const struct sctp_ss_functions sctp_ss_functions[];

#if defined(__Userspace__)
void
sctp_init(uint16_t port,
          int (*conn_output)(void *addr, void *buffer, size_t length, uint8_t tos, uint8_t set_df),
          void (*debug_printf)(const char *format, ...), int start_threads)
#elif defined(__APPLE__) && (!defined(APPLE_LEOPARD) && !defined(APPLE_SNOWLEOPARD) &&!defined(APPLE_LION) && !defined(APPLE_MOUNTAINLION))
void
sctp_init(struct protosw *pp SCTP_UNUSED, struct domain *dp SCTP_UNUSED)
#elif defined(__FreeBSD__)
static void
sctp_init(void *arg SCTP_UNUSED)
#else
void
sctp_init(void)
#endif
{
#if !defined(__Userspace__)
 u_long sb_max_adj;

#else
 init_random();
#endif
 /* Initialize and modify the sysctled variables */
 sctp_init_sysctls();
#if defined(__Userspace__)
 SCTP_BASE_SYSCTL(sctp_udp_tunneling_port) = port;
#else
#if defined(__APPLE__) && !defined(__Userspace__)
 sb_max_adj = (u_long)((u_quad_t) (sb_max) * MCLBYTES / (MSIZE + MCLBYTES));
 SCTP_BASE_SYSCTL(sctp_sendspace) = sb_max_adj;
#else
 if ((nmbclusters / 8) > SCTP_ASOC_MAX_CHUNKS_ON_QUEUE)
  SCTP_BASE_SYSCTL(sctp_max_chunks_on_queue) = (nmbclusters / 8);
 /*
 * Allow a user to take no more than 1/2 the number of clusters or
 * the SB_MAX, whichever is smaller, for the send window.
 */

 sb_max_adj = (u_long)((u_quad_t) (SB_MAX) * MCLBYTES / (MSIZE + MCLBYTES));
 SCTP_BASE_SYSCTL(sctp_sendspace) = min(sb_max_adj,
     (((uint32_t)nmbclusters / 2) * MCLBYTES));
#endif
 /*
 * Now for the recv window, should we take the same amount? or
 * should I do 1/2 the SB_MAX instead in the SB_MAX min above. For
 * now I will just copy.
 */

 SCTP_BASE_SYSCTL(sctp_recvspace) = SCTP_BASE_SYSCTL(sctp_sendspace);
#endif
 SCTP_BASE_VAR(first_time) = 0;
 SCTP_BASE_VAR(sctp_pcb_initialized) = 0;
#if defined(__Userspace__)
#if !defined(_WIN32)
#if defined(INET) || defined(INET6)
 SCTP_BASE_VAR(userspace_route) = -1;
#endif
#endif
#ifdef INET
 SCTP_BASE_VAR(userspace_rawsctp) = -1;
 SCTP_BASE_VAR(userspace_udpsctp) = -1;
#endif
#ifdef INET6
 SCTP_BASE_VAR(userspace_rawsctp6) = -1;
 SCTP_BASE_VAR(userspace_udpsctp6) = -1;
#endif
 SCTP_BASE_VAR(timer_thread_should_exit) = 0;
 SCTP_BASE_VAR(conn_output) = conn_output;
 SCTP_BASE_VAR(debug_printf) = debug_printf;
 SCTP_BASE_VAR(crc32c_offloaded) = 0;
 SCTP_BASE_VAR(iterator_thread_started) = 0;
 SCTP_BASE_VAR(timer_thread_started) = 0;
#endif
#if defined(__Userspace__)
 sctp_pcb_init(start_threads);
 if (start_threads) {
  sctp_start_timer_thread();
 }
#else
 sctp_pcb_init();
#endif
#if defined(SCTP_PACKET_LOGGING)
 SCTP_BASE_VAR(packet_log_writers) = 0;
 SCTP_BASE_VAR(packet_log_end) = 0;
 memset(&SCTP_BASE_VAR(packet_log_buffer), 0, SCTP_PACKET_LOG_SIZE);
#endif
#if defined(__APPLE__) && !defined(__Userspace__)
 SCTP_BASE_VAR(sctp_main_timer_ticks) = 0;
 sctp_start_main_timer();
 timeout(sctp_delayed_startup, NULL, 1);
#endif
#if defined(__FreeBSD__) && !defined(__Userspace__)
 SCTP_BASE_VAR(eh_tag) = EVENTHANDLER_REGISTER(rt_addrmsg,
     sctp_addr_change_event_handler, NULL, EVENTHANDLER_PRI_FIRST);
#endif
}
#if defined(__FreeBSD__) && !defined(__Userspace__)
VNET_SYSINIT(sctp_init, SI_SUB_PROTO_DOMAIN, SI_ORDER_THIRD, sctp_init, NULL);

#ifdef VIMAGE
static void
sctp_finish(void *unused __unused)
{
 EVENTHANDLER_DEREGISTER(rt_addrmsg, SCTP_BASE_VAR(eh_tag));
 sctp_pcb_finish();
}
VNET_SYSUNINIT(sctp, SI_SUB_PROTO_DOMAIN, SI_ORDER_FOURTH, sctp_finish, NULL);
#endif
#else
void
sctp_finish(void)
{
#if defined(__APPLE__) && !defined(__Userspace__)
 untimeout(sctp_delayed_startup, NULL);
 sctp_over_udp_stop();
 sctp_address_monitor_stop();
 sctp_stop_main_timer();
#endif
#if defined(__Userspace__)
#if defined(INET) || defined(INET6)
 recv_thread_destroy();
#endif
 sctp_stop_timer_thread();
#endif
 sctp_pcb_finish();
#if defined(_WIN32) && !defined(__Userspace__)
 sctp_finish_sysctls();
#endif
#if defined(__Userspace__)
 finish_random();
#endif
}
#endif

void
sctp_pathmtu_adjustment(struct sctp_tcb *stcb, uint32_t mtu, bool resend)
{
 struct sctp_association *asoc;
 struct sctp_tmit_chunk *chk;
 uint32_t overhead;

 asoc = &stcb->asoc;
 KASSERT(mtu < asoc->smallest_mtu,
         ("Currently only reducing association MTU %u supported (MTU %u)",
         asoc->smallest_mtu, mtu));
 asoc->smallest_mtu = mtu;
 if (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) {
  overhead = SCTP_MIN_OVERHEAD;
 } else {
#if defined(__Userspace__)
  if (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_BOUND_CONN) {
   overhead = sizeof(struct sctphdr);
  } else {
   overhead = SCTP_MIN_V4_OVERHEAD;
  }
#else
  overhead = SCTP_MIN_V4_OVERHEAD;
#endif
 }
 if (asoc->idata_supported) {
  if (sctp_auth_is_required_chunk(SCTP_IDATA, asoc->peer_auth_chunks)) {
   overhead += sctp_get_auth_chunk_len(asoc->peer_hmac_id);
  }
 } else {
  if (sctp_auth_is_required_chunk(SCTP_DATA, asoc->peer_auth_chunks)) {
   overhead += sctp_get_auth_chunk_len(asoc->peer_hmac_id);
  }
 }
 KASSERT(overhead % 4 == 0,
         ("overhead (%u) not a multiple of 4", overhead));
 TAILQ_FOREACH(chk, &asoc->send_queue, sctp_next) {
  if (((uint32_t)chk->send_size + overhead) > mtu) {
   chk->flags |= CHUNK_FLAGS_FRAGMENT_OK;
  }
 }
 TAILQ_FOREACH(chk, &asoc->sent_queue, sctp_next) {
  if (((uint32_t)chk->send_size + overhead) > mtu) {
   chk->flags |= CHUNK_FLAGS_FRAGMENT_OK;
   if (resend && chk->sent < SCTP_DATAGRAM_RESEND) {
    /*
 * If requested, mark the chunk for immediate
 * resend, since we sent it being too big.
 */

    sctp_flight_size_decrease(chk);
    sctp_total_flight_decrease(stcb, chk);
    chk->sent = SCTP_DATAGRAM_RESEND;
    sctp_ucount_incr(asoc->sent_queue_retran_cnt);
    chk->rec.data.doing_fast_retransmit = 0;
    if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_FLIGHT_LOGGING_ENABLE) {
     sctp_misc_ints(SCTP_FLIGHT_LOG_DOWN_PMTU,
                    chk->whoTo->flight_size,
                    chk->book_size,
                    (uint32_t)(uintptr_t)chk->whoTo,
                    chk->rec.data.tsn);
    }
    /* Clear any time, so NO RTT is being done. */
    if (chk->do_rtt == 1) {
     chk->do_rtt = 0;
     chk->whoTo->rto_needed = 1;
    }
   }
  }
 }
}

#ifdef INET
#if !defined(__Userspace__)
void
sctp_notify(struct sctp_inpcb *inp,
            struct sctp_tcb *stcb,
            struct sctp_nets *net,
            uint8_t icmp_type,
            uint8_t icmp_code,
            uint16_t ip_len,
            uint32_t next_mtu)
{
#if defined(__APPLE__) && !defined(__Userspace__)
 struct socket *so;
#endif
 int timer_stopped;

 if (icmp_type != ICMP_UNREACH) {
  /* We only care about unreachable */
  SCTP_TCB_UNLOCK(stcb);
  return;
 }
 if ((icmp_code == ICMP_UNREACH_NET) ||
     (icmp_code == ICMP_UNREACH_HOST) ||
     (icmp_code == ICMP_UNREACH_NET_UNKNOWN) ||
     (icmp_code == ICMP_UNREACH_HOST_UNKNOWN) ||
     (icmp_code == ICMP_UNREACH_ISOLATED) ||
     (icmp_code == ICMP_UNREACH_NET_PROHIB) ||
     (icmp_code == ICMP_UNREACH_HOST_PROHIB) ||
#if defined(__NetBSD__)
     (icmp_code == ICMP_UNREACH_ADMIN_PROHIBIT)) {
#else
     (icmp_code == ICMP_UNREACH_FILTER_PROHIB)) {
#endif
  /* Mark the net unreachable. */
  if (net->dest_state & SCTP_ADDR_REACHABLE) {
   /* OK, that destination is NOT reachable. */
   net->dest_state &= ~SCTP_ADDR_REACHABLE;
   net->dest_state &= ~SCTP_ADDR_PF;
   sctp_ulp_notify(SCTP_NOTIFY_INTERFACE_DOWN,
                   stcb, 0,
                   (void *)net, SCTP_SO_NOT_LOCKED);
  }
  SCTP_TCB_UNLOCK(stcb);
 } else  if ((icmp_code == ICMP_UNREACH_PROTOCOL) ||
      (icmp_code == ICMP_UNREACH_PORT)) {
  /* Treat it like an ABORT. */
  sctp_abort_notification(stcb, truefalse, 0, NULL, SCTP_SO_NOT_LOCKED);
#if defined(__APPLE__) && !defined(__Userspace__)
  so = SCTP_INP_SO(inp);
  atomic_add_int(&stcb->asoc.refcnt, 1);
  SCTP_TCB_UNLOCK(stcb);
  SCTP_SOCKET_LOCK(so, 1);
  SCTP_TCB_LOCK(stcb);
  atomic_subtract_int(&stcb->asoc.refcnt, 1);
#endif
  (void)sctp_free_assoc(inp, stcb, SCTP_NORMAL_PROC,
                        SCTP_FROM_SCTP_USRREQ + SCTP_LOC_2);
#if defined(__APPLE__) && !defined(__Userspace__)
  SCTP_SOCKET_UNLOCK(so, 1);
  /* SCTP_TCB_UNLOCK(stcb); MT: I think this is not needed.*/
#endif
  /* no need to unlock here, since the TCB is gone */
 } else if (icmp_code == ICMP_UNREACH_NEEDFRAG) {
  if (net->dest_state & SCTP_ADDR_NO_PMTUD) {
   SCTP_TCB_UNLOCK(stcb);
   return;
  }
  /* Find the next (smaller) MTU */
  if (next_mtu == 0) {
   /*
 * Old type router that does not tell us what the next
 * MTU is.
 * Rats we will have to guess (in a educated fashion
 * of course).
 */

   next_mtu = sctp_get_prev_mtu(ip_len);
  }
  /* Stop the PMTU timer. */
  if (SCTP_OS_TIMER_PENDING(&net->pmtu_timer.timer)) {
   timer_stopped = 1;
   sctp_timer_stop(SCTP_TIMER_TYPE_PATHMTURAISE, inp, stcb, net,
                   SCTP_FROM_SCTP_USRREQ + SCTP_LOC_1);
  } else {
   timer_stopped = 0;
  }
  /* Update the path MTU. */
  if (net->port) {
   next_mtu -= sizeof(struct udphdr);
  }
  if (net->mtu > next_mtu) {
   net->mtu = next_mtu;
#if defined(__FreeBSD__) && !defined(__Userspace__)
   if (net->port) {
    sctp_hc_set_mtu(&net->ro._l_addr, inp->fibnum, next_mtu + sizeof(struct udphdr));
   } else {
    sctp_hc_set_mtu(&net->ro._l_addr, inp->fibnum, next_mtu);
   }
#endif
  }
  /* Update the association MTU */
  if (stcb->asoc.smallest_mtu > next_mtu) {
   sctp_pathmtu_adjustment(stcb, next_mtu, true);
  }
  /* Finally, start the PMTU timer if it was running before. */
  if (timer_stopped) {
   sctp_timer_start(SCTP_TIMER_TYPE_PATHMTURAISE, inp, stcb, net);
  }
  SCTP_TCB_UNLOCK(stcb);
 } else {
  SCTP_TCB_UNLOCK(stcb);
 }
}
#endif

#if !defined(__Userspace__)
#if defined(__FreeBSD__)
void sctp_ctlinput(struct icmp *icmp)
{
 struct ip *inner_ip, *outer_ip;
 struct sctphdr *sh;
 struct sctp_inpcb *inp;
 struct sctp_tcb *stcb;
 struct sctp_nets *net;
 struct sctp_init_chunk *ch;
 struct sockaddr_in src, dst;

 if (icmp_errmap(icmp) == 0)
  return;

 outer_ip = (struct ip *)((caddr_t)icmp - sizeof(struct ip));
 inner_ip = &icmp->icmp_ip;
 sh = (struct sctphdr *)((caddr_t)inner_ip + (inner_ip->ip_hl << 2));
 memset(&src, 0, sizeof(struct sockaddr_in));
 src.sin_family = AF_INET;
 src.sin_len = sizeof(struct sockaddr_in);
 src.sin_port = sh->src_port;
 src.sin_addr = inner_ip->ip_src;
 memset(&dst, 0, sizeof(struct sockaddr_in));
 dst.sin_family = AF_INET;
 dst.sin_len = sizeof(struct sockaddr_in);
 dst.sin_port = sh->dest_port;
 dst.sin_addr = inner_ip->ip_dst;
 /*
 * 'dst' holds the dest of the packet that failed to be sent.
 * 'src' holds our local endpoint address. Thus we reverse
 * the dst and the src in the lookup.
 */

 inp = NULL;
 net = NULL;
 stcb = sctp_findassociation_addr_sa((struct sockaddr *)&dst,
                                     (struct sockaddr *)&src,
                                     &inp, &net, 1,
                                     SCTP_DEFAULT_VRFID);
 if ((stcb != NULL) &&
     (net != NULL) &&
     (inp != NULL)) {
  /* Check the verification tag */
  if (ntohl(sh->v_tag) != 0) {
   /*
 * This must be the verification tag used for
 * sending out packets. We don't consider
 * packets reflecting the verification tag.
 */

   if (ntohl(sh->v_tag) != stcb->asoc.peer_vtag) {
    SCTP_TCB_UNLOCK(stcb);
    return;
   }
  } else {
   if (ntohs(outer_ip->ip_len) >=
       sizeof(struct ip) +
       8 + (inner_ip->ip_hl << 2) + 20) {
    /*
 * In this case we can check if we
 * got an INIT chunk and if the
 * initiate tag matches.
 */

    ch = (struct sctp_init_chunk *)(sh + 1);
    if ((ch->ch.chunk_type != SCTP_INITIATION) ||
        (ntohl(ch->init.initiate_tag) != stcb->asoc.my_vtag)) {
     SCTP_TCB_UNLOCK(stcb);
     return;
    }
   } else {
    SCTP_TCB_UNLOCK(stcb);
    return;
   }
  }
  sctp_notify(inp, stcb, net,
              icmp->icmp_type,
              icmp->icmp_code,
              ntohs(inner_ip->ip_len),
              (uint32_t)ntohs(icmp->icmp_nextmtu));
 } else {
  if ((stcb == NULL) && (inp != NULL)) {
   /* reduce ref-count */
   SCTP_INP_WLOCK(inp);
   SCTP_INP_DECR_REF(inp);
   SCTP_INP_WUNLOCK(inp);
  }
  if (stcb) {
   SCTP_TCB_UNLOCK(stcb);
  }
 }
}
#else
void
#if defined(__APPLE__) && !defined(APPLE_LEOPARD) && !defined(APPLE_SNOWLEOPARD) && !defined(APPLE_LION) && !defined(APPLE_MOUNTAINLION) && !defined(APPLE_ELCAPITAN)
sctp_ctlinput(int cmd, struct sockaddr *sa, void *vip, struct ifnet *ifp SCTP_UNUSED)
#else
sctp_ctlinput(int cmd, struct sockaddr *sa, void *vip)
#endif
{
 struct ip *inner_ip;
 struct sctphdr *sh;
 struct icmp *icmp;
 struct sctp_inpcb *inp;
 struct sctp_tcb *stcb;
 struct sctp_nets *net;
 struct sockaddr_in src, dst;

#if !defined(__FreeBSD__) && !defined(__Userspace__)
 if (sa->sa_family != AF_INET ||
     ((struct sockaddr_in *)sa)->sin_addr.s_addr == INADDR_ANY) {
  return;
 }
#endif
 if (PRC_IS_REDIRECT(cmd)) {
  vip = NULL;
 } else if ((unsigned)cmd >= PRC_NCMDS || inetctlerrmap[cmd] == 0) {
  return;
 }
 if (vip != NULL) {
  inner_ip = (struct ip *)vip;
  icmp = (struct icmp *)((caddr_t)inner_ip -
      (sizeof(struct icmp) - sizeof(struct ip)));
  sh = (struct sctphdr *)((caddr_t)inner_ip + (inner_ip->ip_hl << 2));
  memset(&src, 0, sizeof(struct sockaddr_in));
  src.sin_family = AF_INET;
#ifdef HAVE_SIN_LEN
  src.sin_len = sizeof(struct sockaddr_in);
#endif
  src.sin_port = sh->src_port;
  src.sin_addr = inner_ip->ip_src;
  memset(&dst, 0, sizeof(struct sockaddr_in));
  dst.sin_family = AF_INET;
#ifdef HAVE_SIN_LEN
  dst.sin_len = sizeof(struct sockaddr_in);
#endif
  dst.sin_port = sh->dest_port;
  dst.sin_addr = inner_ip->ip_dst;
  /*
 * 'dst' holds the dest of the packet that failed to be sent.
 * 'src' holds our local endpoint address. Thus we reverse
 * the dst and the src in the lookup.
 */

  inp = NULL;
  net = NULL;
  stcb = sctp_findassociation_addr_sa((struct sockaddr *)&dst,
                                      (struct sockaddr *)&src,
                                      &inp, &net, 1,
                                      SCTP_DEFAULT_VRFID);
  if ((stcb != NULL) &&
      (net != NULL) &&
      (inp != NULL)) {
   /* Check the verification tag */
   if (ntohl(sh->v_tag) != 0) {
    /*
 * This must be the verification tag used for
 * sending out packets. We don't consider
 * packets reflecting the verification tag.
 */

    if (ntohl(sh->v_tag) != stcb->asoc.peer_vtag) {
     SCTP_TCB_UNLOCK(stcb);
     return;
    }
   } else {
    SCTP_TCB_UNLOCK(stcb);
    return;
   }
   sctp_notify(inp, stcb, net,
               icmp->icmp_type,
               icmp->icmp_code,
               inner_ip->ip_len,
               (uint32_t)ntohs(icmp->icmp_nextmtu));
#if defined(__Userspace__)
   if (((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) == 0) &&
       (stcb->sctp_socket != NULL)) {
    struct socket *upcall_socket;

    upcall_socket = stcb->sctp_socket;
    SOCK_LOCK(upcall_socket);
    soref(upcall_socket);
    SOCK_UNLOCK(upcall_socket);
    if ((upcall_socket->so_upcall != NULL) &&
        (upcall_socket->so_error != 0)) {
     (*upcall_socket->so_upcall)(upcall_socket, upcall_socket->so_upcallarg, M_NOWAIT);
    }
    ACCEPT_LOCK();
    SOCK_LOCK(upcall_socket);
    sorele(upcall_socket);
   }
#endif
  } else {
   if ((stcb == NULL) && (inp != NULL)) {
    /* reduce ref-count */
    SCTP_INP_WLOCK(inp);
    SCTP_INP_DECR_REF(inp);
    SCTP_INP_WUNLOCK(inp);
   }
   if (stcb) {
    SCTP_TCB_UNLOCK(stcb);
   }
  }
 }
 return;
}
#endif
#endif
#endif

#if defined(__FreeBSD__) && !defined(__Userspace__)
static int
sctp_getcred(SYSCTL_HANDLER_ARGS)
{
 struct xucred xuc;
 struct sockaddr_in addrs[2];
 struct sctp_inpcb *inp;
 struct sctp_nets *net;
 struct sctp_tcb *stcb;
 int error;
 uint32_t vrf_id;

 /* FIX, for non-bsd is this right? */
 vrf_id = SCTP_DEFAULT_VRFID;

 error = priv_check(req->td, PRIV_NETINET_GETCRED);

 if (error)
  return (error);

 error = SYSCTL_IN(req, addrs, sizeof(addrs));
 if (error)
  return (error);

 stcb = sctp_findassociation_addr_sa(sintosa(&addrs[1]),
     sintosa(&addrs[0]),
     &inp, &net, 1, vrf_id);
 if (stcb == NULL || inp == NULL || inp->sctp_socket == NULL) {
  if ((inp != NULL) && (stcb == NULL)) {
   /* reduce ref-count */
   SCTP_INP_WLOCK(inp);
   SCTP_INP_DECR_REF(inp);
   goto cred_can_cont;
  }

  SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, ENOENT);
  error = ENOENT;
  goto out;
 }
 SCTP_TCB_UNLOCK(stcb);
 /* We use the write lock here, only
 * since in the error leg we need it.
 * If we used RLOCK, then we would have
 * to wlock/decr/unlock/rlock. Which
 * in theory could create a hole. Better
 * to use higher wlock.
 */

 SCTP_INP_WLOCK(inp);
 cred_can_cont:
 error = cr_canseesocket(req->td->td_ucred, inp->sctp_socket);
 if (error) {
  SCTP_INP_WUNLOCK(inp);
  goto out;
 }
 cru2x(inp->sctp_socket->so_cred, &xuc);
 SCTP_INP_WUNLOCK(inp);
 error = SYSCTL_OUT(req, &xuc, sizeof(struct xucred));
out:
 return (error);
}

SYSCTL_PROC(_net_inet_sctp, OID_AUTO, getcred,
    CTLTYPE_OPAQUE | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
    0, 0, sctp_getcred, "S,ucred",
    "Get the ucred of a SCTP connection");
#endif

#if defined(__FreeBSD__) && !defined(__Userspace__)
void
#else
int
#endif
sctp_abort(struct socket *so)
{
#if defined(__FreeBSD__) && !defined(__Userspace__)
 struct epoch_tracker et;
#endif
 struct sctp_inpcb *inp;

 inp = (struct sctp_inpcb *)so->so_pcb;
 if (inp == NULL) {
#if defined(__FreeBSD__) && !defined(__Userspace__)
  return;
#else
  SCTP_LTRACE_ERR_RET(NULL, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL);
  return (EINVAL);
#endif
 }

 SCTP_INP_WLOCK(inp);
#if defined(__FreeBSD__) && !defined(__Userspace__)
 NET_EPOCH_ENTER(et);
#endif
#ifdef SCTP_LOG_CLOSING
 sctp_log_closing(inp, NULL, 17);
#endif
 if (((inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) == 0)) {
  inp->sctp_flags |= SCTP_PCB_FLAGS_SOCKET_GONE | SCTP_PCB_FLAGS_CLOSE_IP;
#ifdef SCTP_LOG_CLOSING
  sctp_log_closing(inp, NULL, 16);
#endif
  SCTP_INP_WUNLOCK(inp);
  sctp_inpcb_free(inp, SCTP_FREE_SHOULD_USE_ABORT,
                  SCTP_CALLED_AFTER_CMPSET_OFCLOSE);
  SOCK_LOCK(so);
#if defined(__FreeBSD__) && !defined(__Userspace__)
  KASSERT(!SOLISTENING(so),
          ("sctp_abort: called on listening socket %p", so));
#endif
  SCTP_SB_CLEAR(so->so_snd);
  SCTP_SB_CLEAR(so->so_rcv);
#if defined(__APPLE__) && !defined(__Userspace__)
  so->so_usecount--;
#else
  /* Now null out the reference, we are completely detached. */
  so->so_pcb = NULL;
#endif
  SOCK_UNLOCK(so);
 } else {
  SCTP_INP_WUNLOCK(inp);
 }
#if defined(__FreeBSD__) && !defined(__Userspace__)
 NET_EPOCH_EXIT(et);
#else
 return (0);
#endif
}

#ifdef INET
#if defined(__Userspace__)
int
#else
static int
#endif
#if defined(__Userspace__)
sctp_attach(struct socket *so, int proto SCTP_UNUSED, uint32_t vrf_id)
#elif defined(__FreeBSD__)
sctp_attach(struct socket *so, int proto SCTP_UNUSED, struct thread *p SCTP_UNUSED)
#elif defined(_WIN32)
sctp_attach(struct socket *so, int proto SCTP_UNUSED, PKTHREAD p SCTP_UNUSED)
#else
sctp_attach(struct socket *so, int proto SCTP_UNUSED, struct proc *p SCTP_UNUSED)
#endif
{
 struct sctp_inpcb *inp;
 struct inpcb *ip_inp;
 int error;
#if !defined(__Userspace__)
 uint32_t vrf_id = SCTP_DEFAULT_VRFID;
#endif

 inp = (struct sctp_inpcb *)so->so_pcb;
 if (inp != NULL) {
  SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL);
  return (EINVAL);
 }
 if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) {
  error = SCTP_SORESERVE(so, SCTP_BASE_SYSCTL(sctp_sendspace), SCTP_BASE_SYSCTL(sctp_recvspace));
  if (error) {
   return (error);
  }
 }
 error = sctp_inpcb_alloc(so, vrf_id);
 if (error) {
  return (error);
 }
 inp = (struct sctp_inpcb *)so->so_pcb;
 SCTP_INP_WLOCK(inp);
 inp->sctp_flags &= ~SCTP_PCB_FLAGS_BOUND_V6; /* I'm not v6! */
 ip_inp = &inp->ip_inp.inp;
 ip_inp->inp_vflag |= INP_IPV4;
 ip_inp->inp_ip_ttl = MODULE_GLOBAL(ip_defttl);
 SCTP_INP_WUNLOCK(inp);
 return (0);
}

#if defined(__Userspace__)
int
sctp_bind(struct socket *so, struct sockaddr *addr) {
 void *p = NULL;
#elif defined(__FreeBSD__)
static int
sctp_bind(struct socket *so, struct sockaddr *addr, struct thread *p)
{
#elif defined(__APPLE__)
static int
sctp_bind(struct socket *so, struct sockaddr *addr, struct proc *p) {
#elif defined(_WIN32)
static int
sctp_bind(struct socket *so, struct sockaddr *addr, PKTHREAD p) {
#else
static int
sctp_bind(struct socket *so, struct mbuf *nam, struct proc *p)
{
 struct sockaddr *addr = nam ? mtod(nam, struct sockaddr *): NULL;

#endif
 struct sctp_inpcb *inp;

 inp = (struct sctp_inpcb *)so->so_pcb;
 if (inp == NULL) {
  SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL);
  return (EINVAL);
 }
 if (addr != NULL) {
#ifdef HAVE_SA_LEN
  if ((addr->sa_family != AF_INET) ||
      (addr->sa_len != sizeof(struct sockaddr_in))) {
#else
  if (addr->sa_family != AF_INET) {
#endif
   SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL);
   return (EINVAL);
  }
 }
 return (sctp_inpcb_bind(so, addr, NULL, p));
}

#endif
#if defined(__Userspace__)

int
sctpconn_attach(struct socket *so, int proto SCTP_UNUSED, uint32_t vrf_id)
{
 struct sctp_inpcb *inp;
 struct inpcb *ip_inp;
 int error;

 inp = (struct sctp_inpcb *)so->so_pcb;
 if (inp != NULL) {
  SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL);
  return (EINVAL);
 }
 if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) {
  error = SCTP_SORESERVE(so, SCTP_BASE_SYSCTL(sctp_sendspace), SCTP_BASE_SYSCTL(sctp_recvspace));
  if (error) {
   return (error);
  }
 }
 error = sctp_inpcb_alloc(so, vrf_id);
 if (error) {
  return (error);
 }
 inp = (struct sctp_inpcb *)so->so_pcb;
 SCTP_INP_WLOCK(inp);
 inp->sctp_flags &= ~SCTP_PCB_FLAGS_BOUND_V6;
 inp->sctp_flags |= SCTP_PCB_FLAGS_BOUND_CONN;
 ip_inp = &inp->ip_inp.inp;
 ip_inp->inp_vflag |= INP_CONN;
 ip_inp->inp_ip_ttl = MODULE_GLOBAL(ip_defttl);
 SCTP_INP_WUNLOCK(inp);
 return (0);
}

int
sctpconn_bind(struct socket *so, struct sockaddr *addr)
{
 struct sctp_inpcb *inp;

 inp = (struct sctp_inpcb *)so->so_pcb;
 if (inp == NULL) {
  SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL);
  return (EINVAL);
 }
 if (addr != NULL) {
#ifdef HAVE_SA_LEN
  if ((addr->sa_family != AF_CONN) ||
      (addr->sa_len != sizeof(struct sockaddr_conn))) {
#else
  if (addr->sa_family != AF_CONN) {
#endif
   SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL);
   return (EINVAL);
  }
 }
 return (sctp_inpcb_bind(so, addr, NULL, NULL));
}

#endif
#if defined(__FreeBSD__) || defined(_WIN32) || defined(__Userspace__)
void
sctp_close(struct socket *so)
{
#if defined(__FreeBSD__) && !defined(__Userspace__)
 struct epoch_tracker et;
#endif
 struct sctp_inpcb *inp;

 inp = (struct sctp_inpcb *)so->so_pcb;
 if (inp == NULL)
  return;

 /* Inform all the lower layer assoc that we
 * are done.
 */

 SCTP_INP_WLOCK(inp);
#if defined(__FreeBSD__) && !defined(__Userspace__)
 NET_EPOCH_ENTER(et);
#endif
#ifdef SCTP_LOG_CLOSING
 sctp_log_closing(inp, NULL, 17);
#endif
 if ((inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) == 0) {
  inp->sctp_flags |= SCTP_PCB_FLAGS_SOCKET_GONE | SCTP_PCB_FLAGS_CLOSE_IP;
#if defined(__Userspace__)
  if (((so->so_options & SCTP_SO_LINGER) && (so->so_linger == 0)) ||
#else
  if (((so->so_options & SO_LINGER) && (so->so_linger == 0)) ||
#endif
      (SCTP_SBAVAIL(&so->so_rcv) > 0)) {
#ifdef SCTP_LOG_CLOSING
   sctp_log_closing(inp, NULL, 13);
#endif
   SCTP_INP_WUNLOCK(inp);
   sctp_inpcb_free(inp, SCTP_FREE_SHOULD_USE_ABORT,
                   SCTP_CALLED_AFTER_CMPSET_OFCLOSE);
  } else {
#ifdef SCTP_LOG_CLOSING
   sctp_log_closing(inp, NULL, 14);
#endif
   SCTP_INP_WUNLOCK(inp);
   sctp_inpcb_free(inp, SCTP_FREE_SHOULD_USE_GRACEFUL_CLOSE,
                   SCTP_CALLED_AFTER_CMPSET_OFCLOSE);
  }
  /* The socket is now detached, no matter what
 * the state of the SCTP association.
 */

  SOCK_LOCK(so);
#if defined(__FreeBSD__) && !defined(__Userspace__)
  if (!SOLISTENING(so)) {
   SCTP_SB_CLEAR(so->so_snd);
   SCTP_SB_CLEAR(so->so_rcv);
  }
#else
  SCTP_SB_CLEAR(so->so_snd);
  SCTP_SB_CLEAR(so->so_rcv);
#endif
#if !(defined(__APPLE__) && !defined(__Userspace__))
  /* Now null out the reference, we are completely detached. */
  so->so_pcb = NULL;
#endif
  SOCK_UNLOCK(so);
 } else {
  SCTP_INP_WUNLOCK(inp);
 }
#if defined(__FreeBSD__) && !defined(__Userspace__)
 NET_EPOCH_EXIT(et);
#endif
}
#else
int
sctp_detach(struct socket *so)
{
 struct sctp_inpcb *inp;
 uint32_t flags;

 inp = (struct sctp_inpcb *)so->so_pcb;
 if (inp == NULL) {
  SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL);
  return (EINVAL);
 }
 sctp_must_try_again:
 flags = inp->sctp_flags;
#ifdef SCTP_LOG_CLOSING
 sctp_log_closing(inp, NULL, 17);
#endif
 if (((flags & SCTP_PCB_FLAGS_SOCKET_GONE) == 0) &&
     (atomic_cmpset_int(&inp->sctp_flags, flags, (flags | SCTP_PCB_FLAGS_SOCKET_GONE | SCTP_PCB_FLAGS_CLOSE_IP)))) {
  if (((so->so_options & SO_LINGER) && (so->so_linger == 0)) ||
      (SCTP_SBAVAIL(&so->so_rcv) > 0)) {
#ifdef SCTP_LOG_CLOSING
   sctp_log_closing(inp, NULL, 13);
#endif
   sctp_inpcb_free(inp, SCTP_FREE_SHOULD_USE_ABORT,
                   SCTP_CALLED_AFTER_CMPSET_OFCLOSE);
  } else {
#ifdef SCTP_LOG_CLOSING
   sctp_log_closing(inp, NULL, 13);
#endif
   sctp_inpcb_free(inp, SCTP_FREE_SHOULD_USE_GRACEFUL_CLOSE,
                   SCTP_CALLED_AFTER_CMPSET_OFCLOSE);
  }
  /* The socket is now detached, no matter what
 * the state of the SCTP association.
 */

  SCTP_SB_CLEAR(so->so_snd);
  /* same for the rcv ones, they are only
 * here for the accounting/select.
 */

  SCTP_SB_CLEAR(so->so_rcv);
#if !(defined(__APPLE__) && !defined(__Userspace__))
  /* Now disconnect */
  so->so_pcb = NULL;
#endif
 } else {
  flags = inp->sctp_flags;
  if ((flags & SCTP_PCB_FLAGS_SOCKET_GONE) == 0) {
   goto sctp_must_try_again;
  }
 }
 return (0);
}
#endif

#if defined(__Userspace__)
/* __Userspace__ is not calling sctp_sendm */
#endif
#if !(defined(_WIN32) && !defined(__Userspace__))
int
#if defined(__FreeBSD__) && !defined(__Userspace__)
sctp_sendm(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr,
    struct mbuf *control, struct thread *p);

#else
sctp_sendm(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr,
    struct mbuf *control, struct proc *p);

#endif
int
#if defined(__FreeBSD__) && !defined(__Userspace__)
sctp_sendm(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr,
    struct mbuf *control, struct thread *p)
{
#else
sctp_sendm(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr,
    struct mbuf *control, struct proc *p)
{
#endif
 struct sctp_inpcb *inp;
 int error;

 inp = (struct sctp_inpcb *)so->so_pcb;
 if (inp == NULL) {
  if (control) {
   sctp_m_freem(control);
   control = NULL;
  }
  SCTP_LTRACE_ERR_RET_PKT(m, inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL);
  sctp_m_freem(m);
  return (EINVAL);
 }
 /* Got to have an to address if we are NOT a connected socket */
 if ((addr == NULL) &&
     ((inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) ||
     (inp->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE))) {
  goto connected_type;
 }

 error = 0;
 if (addr == NULL) {
  SCTP_LTRACE_ERR_RET_PKT(m, inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EDESTADDRREQ);
  error = EDESTADDRREQ;
 } else if (addr->sa_family != AF_INET) {
  SCTP_LTRACE_ERR_RET_PKT(m, inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EAFNOSUPPORT);
  error = EAFNOSUPPORT;
#if defined(HAVE_SA_LEN)
 } else if (addr->sa_len != sizeof(struct sockaddr_in)) {
  SCTP_LTRACE_ERR_RET_PKT(m, inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL);
  error = EINVAL;
#endif
 }
 if (error != 0) {
  sctp_m_freem(m);
  if (control) {
   sctp_m_freem(control);
   control = NULL;
  }
  return (error);
 }
connected_type:
 /* now what about control */
 if (control) {
  if (inp->control) {
   sctp_m_freem(inp->control);
   inp->control = NULL;
  }
  inp->control = control;
 }
 /* Place the data */
 if (inp->pkt) {
  SCTP_BUF_NEXT(inp->pkt_last) = m;
  inp->pkt_last = m;
 } else {
  inp->pkt_last = inp->pkt = m;
 }
 if (
#if (defined(__FreeBSD__) || defined(__APPLE__)) && !defined(__Userspace__)
 /* FreeBSD uses a flag passed */
     ((flags & PRUS_MORETOCOME) == 0)
#else
     1   /* Open BSD does not have any "more to come"
 * indication */

#endif
     ) {
  /*
 * note with the current version this code will only be used
 * by OpenBSD-- NetBSD, FreeBSD, and MacOS have methods for
 * re-defining sosend to use the sctp_sosend. One can
 * optionally switch back to this code (by changing back the
 * definitions) but this is not advisable. This code is used
 * by FreeBSD when sending a file with sendfile() though.
 */

#if defined(__FreeBSD__) && !defined(__Userspace__)
  struct epoch_tracker et;
#endif
  int ret;

#if defined(__FreeBSD__) && !defined(__Userspace__)
 NET_EPOCH_ENTER(et);
#endif
  ret = sctp_output(inp, inp->pkt, addr, inp->control, p, flags);
#if defined(__FreeBSD__) && !defined(__Userspace__)
 NET_EPOCH_EXIT(et);
#endif
  inp->pkt = NULL;
  inp->control = NULL;
  return (ret);
 } else {
  return (0);
 }
}
#endif

int
sctp_disconnect(struct socket *so)
{
#if defined(__FreeBSD__) && !defined(__Userspace__)
 struct epoch_tracker et;
#endif
 struct sctp_inpcb *inp;
 struct sctp_association *asoc;
 struct sctp_tcb *stcb;

 inp = (struct sctp_inpcb *)so->so_pcb;
 if (inp == NULL) {
  SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, ENOTCONN);
  return (ENOTCONN);
 }
 SCTP_INP_RLOCK(inp);
 KASSERT(inp->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE ||
         inp->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL,
         ("Not a one-to-one style socket"));
 stcb = LIST_FIRST(&inp->sctp_asoc_list);
 if (stcb == NULL) {
  SCTP_INP_RUNLOCK(inp);
  SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, ENOTCONN);
  return (ENOTCONN);
 }
 SCTP_TCB_LOCK(stcb);
 asoc = &stcb->asoc;
 if (asoc->state & SCTP_STATE_ABOUT_TO_BE_FREED) {
  /* We are about to be freed, out of here */
  SCTP_TCB_UNLOCK(stcb);
  SCTP_INP_RUNLOCK(inp);
  return (0);
 }
#if defined(__FreeBSD__) && !defined(__Userspace__)
 NET_EPOCH_ENTER(et);
#endif
#if defined(__Userspace__)
 if (((so->so_options & SCTP_SO_LINGER) && (so->so_linger == 0)) ||
     (SCTP_SBAVAIL(&so->so_rcv) > 0)) {
#else
 if (((so->so_options & SO_LINGER) && (so->so_linger == 0)) ||
     (SCTP_SBAVAIL(&so->so_rcv) > 0)) {
#endif
  if (SCTP_GET_STATE(stcb) != SCTP_STATE_COOKIE_WAIT) {
   /* Left with Data unread */
   struct mbuf *op_err;

   op_err = sctp_generate_cause(SCTP_CAUSE_USER_INITIATED_ABT, "");
   sctp_send_abort_tcb(stcb, op_err, SCTP_SO_LOCKED);
   SCTP_STAT_INCR_COUNTER32(sctps_aborted);
  }
  SCTP_INP_RUNLOCK(inp);
  if ((SCTP_GET_STATE(stcb) == SCTP_STATE_OPEN) ||
      (SCTP_GET_STATE(stcb) == SCTP_STATE_SHUTDOWN_RECEIVED)) {
   SCTP_STAT_DECR_GAUGE32(sctps_currestab);
  }
  (void)sctp_free_assoc(inp, stcb, SCTP_NORMAL_PROC,
                        SCTP_FROM_SCTP_USRREQ + SCTP_LOC_3);
  /* No unlock tcb assoc is gone */
#if defined(__FreeBSD__) && !defined(__Userspace__)
  NET_EPOCH_EXIT(et);
#endif
  return (0);
 }
 if (TAILQ_EMPTY(&asoc->send_queue) &&
     TAILQ_EMPTY(&asoc->sent_queue) &&
     (asoc->stream_queue_cnt == 0)) {
  /* there is nothing queued to send, so done */
  if ((*asoc->ss_functions.sctp_ss_is_user_msgs_incomplete)(stcb, asoc)) {
   goto abort_anyway;
  }
  if ((SCTP_GET_STATE(stcb) != SCTP_STATE_SHUTDOWN_SENT) &&
      (SCTP_GET_STATE(stcb) != SCTP_STATE_SHUTDOWN_ACK_SENT)) {
   /* only send SHUTDOWN 1st time thru */
   struct sctp_nets *netp;

   if ((SCTP_GET_STATE(stcb) == SCTP_STATE_OPEN) ||
       (SCTP_GET_STATE(stcb) == SCTP_STATE_SHUTDOWN_RECEIVED)) {
    SCTP_STAT_DECR_GAUGE32(sctps_currestab);
   }
   SCTP_SET_STATE(stcb, SCTP_STATE_SHUTDOWN_SENT);
   sctp_stop_timers_for_shutdown(stcb);
   if (stcb->asoc.alternate) {
    netp = stcb->asoc.alternate;
   } else {
    netp = stcb->asoc.primary_destination;
   }
   sctp_send_shutdown(stcb,netp);
   sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWN,
                    stcb->sctp_ep, stcb, netp);
   sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWNGUARD,
                    stcb->sctp_ep, stcb, NULL);
   sctp_chunk_output(stcb->sctp_ep, stcb, SCTP_OUTPUT_FROM_CLOSING, SCTP_SO_LOCKED);
  }
 } else {
  /*
 * we still got (or just got) data to send,
 * so set SHUTDOWN_PENDING
 */

  /*
 * XXX sockets draft says that SCTP_EOF
 * should be sent with no data. currently,
 * we will allow user data to be sent first
 * and move to SHUTDOWN-PENDING
 */

  SCTP_ADD_SUBSTATE(stcb, SCTP_STATE_SHUTDOWN_PENDING);
  if ((*asoc->ss_functions.sctp_ss_is_user_msgs_incomplete)(stcb, asoc)) {
   SCTP_ADD_SUBSTATE(stcb, SCTP_STATE_PARTIAL_MSG_LEFT);
  }
  if (TAILQ_EMPTY(&asoc->send_queue) &&
      TAILQ_EMPTY(&asoc->sent_queue) &&
      (asoc->state & SCTP_STATE_PARTIAL_MSG_LEFT)) {
   struct mbuf *op_err;
  abort_anyway:
   op_err = sctp_generate_cause(SCTP_CAUSE_USER_INITIATED_ABT, "");
   stcb->sctp_ep->last_abort_code = SCTP_FROM_SCTP_USRREQ + SCTP_LOC_4;
   sctp_send_abort_tcb(stcb, op_err, SCTP_SO_LOCKED);
   SCTP_STAT_INCR_COUNTER32(sctps_aborted);
   if ((SCTP_GET_STATE(stcb) == SCTP_STATE_OPEN) ||
       (SCTP_GET_STATE(stcb) == SCTP_STATE_SHUTDOWN_RECEIVED)) {
    SCTP_STAT_DECR_GAUGE32(sctps_currestab);
   }
   SCTP_INP_RUNLOCK(inp);
   (void)sctp_free_assoc(inp, stcb, SCTP_NORMAL_PROC,
                         SCTP_FROM_SCTP_USRREQ + SCTP_LOC_5);
#if defined(__FreeBSD__) && !defined(__Userspace__)
   NET_EPOCH_EXIT(et);
#endif
   return (0);
  } else {
   sctp_chunk_output(inp, stcb, SCTP_OUTPUT_FROM_CLOSING, SCTP_SO_LOCKED);
  }
 }
 soisdisconnecting(so);
#if defined(__FreeBSD__) && !defined(__Userspace__)
 NET_EPOCH_EXIT(et);
#endif
 SCTP_TCB_UNLOCK(stcb);
 SCTP_INP_RUNLOCK(inp);
 return (0);
}

#if defined(__FreeBSD__) || defined(_WIN32) || defined(__Userspace__)
int
sctp_flush(struct socket *so, int how)
{
#if defined(__FreeBSD__) && !defined(__Userspace__)
 struct epoch_tracker et;
#endif
 struct sctp_tcb *stcb;
 struct sctp_queued_to_read *control, *ncontrol;
 struct sctp_inpcb *inp;
 struct mbuf *m, *op_err;
 bool need_to_abort = false;

 /*
 * For 1-to-1 style sockets, flush the read queue and trigger an
 * ungraceful shutdown of the association, if and only if user messages
 * are lost. Loosing notifications does not need to be signalled to the
 * peer.
 */

 if (how == PRU_FLUSH_WR) {
  /* This function is only relevant for the read directions. */
  return (0);
 }
 inp = (struct sctp_inpcb *)so->so_pcb;
 if (inp == NULL) {
  SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL);
  return (EINVAL);
 }
 SCTP_INP_WLOCK(inp);
 if (inp->sctp_flags & SCTP_PCB_FLAGS_UDPTYPE) {
  /* For 1-to-many style sockets this function does nothing. */
  SCTP_INP_WUNLOCK(inp);
  return (0);
 }
 stcb = LIST_FIRST(&inp->sctp_asoc_list);
 if (stcb != NULL) {
  SCTP_TCB_LOCK(stcb);
 }
 SCTP_INP_READ_LOCK(inp);
 inp->sctp_flags |= SCTP_PCB_FLAGS_SOCKET_CANT_READ;
 SOCK_LOCK(so);
 TAILQ_FOREACH_SAFE(control, &inp->read_queue, next, ncontrol) {
  if ((control->spec_flags & M_NOTIFICATION) == 0) {
   need_to_abort = true;
  }
  TAILQ_REMOVE(&inp->read_queue, control, next);
  control->on_read_q = 0;
  for (m = control->data; m; m = SCTP_BUF_NEXT(m)) {
   sctp_sbfree(control, control->stcb, &so->so_rcv, m);
  }
  if (control->on_strm_q == 0) {
   sctp_free_remote_addr(control->whoFrom);
   if (control->data) {
    sctp_m_freem(control->data);
    control->data = NULL;
   }
   sctp_free_a_readq(stcb, control);
  } else {
   if (stcb != NULL) {
    stcb->asoc.size_on_all_streams += control->length;
   }
  }
 }
 SOCK_UNLOCK(so);
 SCTP_INP_READ_UNLOCK(inp);
 if (need_to_abort && (stcb != NULL)) {
  inp->last_abort_code = SCTP_FROM_SCTP_USRREQ + SCTP_LOC_6;
  SCTP_INP_WUNLOCK(inp);
  op_err = sctp_generate_cause(SCTP_CAUSE_OUT_OF_RESC, "");
#if defined(__FreeBSD__) && !defined(__Userspace__)
  NET_EPOCH_ENTER(et);
#endif
  sctp_abort_an_association(inp, stcb, op_err, false, SCTP_SO_LOCKED);
#if defined(__FreeBSD__) && !defined(__Userspace__)
  NET_EPOCH_EXIT(et);
#endif
  return (ECONNABORTED);
 }
 if (stcb != NULL) {
  SCTP_TCB_UNLOCK(stcb);
 }
 SCTP_INP_WUNLOCK(inp);
 return (0);
}
#endif

int
sctp_shutdown(struct socket *so)
{
 struct sctp_inpcb *inp;

 inp = (struct sctp_inpcb *)so->so_pcb;
 if (inp == NULL) {
  SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL);
  return (EINVAL);
 }
 SCTP_INP_RLOCK(inp);
 /* For UDP model this is a invalid call */
 if (!((inp->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) ||
       (inp->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL))) {
  /* Restore the flags that the soshutdown took away. */
#if (defined(__FreeBSD__) || defined(_WIN32)) && !defined(__Userspace__)
  SOCKBUF_LOCK(&so->so_rcv);
  so->so_rcv.sb_state &= ~SBS_CANTRCVMORE;
  SOCKBUF_UNLOCK(&so->so_rcv);
#else
  SOCK_LOCK(so);
  so->so_state &= ~SS_CANTRCVMORE;
  SOCK_UNLOCK(so);
#endif
  /* This proc will wakeup for read and do nothing (I hope) */
  SCTP_INP_RUNLOCK(inp);
  SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EOPNOTSUPP);
  return (EOPNOTSUPP);
 } else {
  /*
 * Ok, if we reach here its the TCP model and it is either
 * a SHUT_WR or SHUT_RDWR.
 * This means we put the shutdown flag against it.
 */

#if defined(__FreeBSD__) && !defined(__Userspace__)
  struct epoch_tracker et;
#endif
  struct sctp_tcb *stcb;
  struct sctp_association *asoc;
  struct sctp_nets *netp;

  if ((so->so_state &
       (SS_ISCONNECTED|SS_ISCONNECTING|SS_ISDISCONNECTING)) == 0) {
   SCTP_INP_RUNLOCK(inp);
   return (ENOTCONN);
  }
  socantsendmore(so);

  stcb = LIST_FIRST(&inp->sctp_asoc_list);
  if (stcb == NULL) {
   /*
 * Ok, we hit the case that the shutdown call was
 * made after an abort or something. Nothing to do
 * now.
 */

   SCTP_INP_RUNLOCK(inp);
   return (0);
  }
  SCTP_TCB_LOCK(stcb);
  asoc = &stcb->asoc;
  if (asoc->state & SCTP_STATE_ABOUT_TO_BE_FREED) {
   SCTP_TCB_UNLOCK(stcb);
   SCTP_INP_RUNLOCK(inp);
   return (0);
  }
  if ((SCTP_GET_STATE(stcb) != SCTP_STATE_COOKIE_WAIT) &&
      (SCTP_GET_STATE(stcb) != SCTP_STATE_COOKIE_ECHOED) &&
      (SCTP_GET_STATE(stcb) != SCTP_STATE_OPEN)) {
   /* If we are not in or before ESTABLISHED, there is
 * no protocol action required.
 */

   SCTP_TCB_UNLOCK(stcb);
   SCTP_INP_RUNLOCK(inp);
   return (0);
  }
#if defined(__FreeBSD__) && !defined(__Userspace__)
  NET_EPOCH_ENTER(et);
#endif
  if (stcb->asoc.alternate) {
   netp = stcb->asoc.alternate;
  } else {
   netp = stcb->asoc.primary_destination;
  }
  if ((SCTP_GET_STATE(stcb) == SCTP_STATE_OPEN) &&
      TAILQ_EMPTY(&asoc->send_queue) &&
      TAILQ_EMPTY(&asoc->sent_queue) &&
      (asoc->stream_queue_cnt == 0)) {
   if ((*asoc->ss_functions.sctp_ss_is_user_msgs_incomplete)(stcb, asoc)) {
    goto abort_anyway;
   }
   /* there is nothing queued to send, so I'm done... */
   SCTP_STAT_DECR_GAUGE32(sctps_currestab);
   SCTP_SET_STATE(stcb, SCTP_STATE_SHUTDOWN_SENT);
   sctp_stop_timers_for_shutdown(stcb);
   sctp_send_shutdown(stcb, netp);
   sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWN,
                    stcb->sctp_ep, stcb, netp);
   sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWNGUARD,
                    stcb->sctp_ep, stcb, NULL);
  } else {
   /*
 * We still got (or just got) data to send, so set
 * SHUTDOWN_PENDING.
 */

   SCTP_ADD_SUBSTATE(stcb, SCTP_STATE_SHUTDOWN_PENDING);
   if ((*asoc->ss_functions.sctp_ss_is_user_msgs_incomplete)(stcb, asoc)) {
    SCTP_ADD_SUBSTATE(stcb, SCTP_STATE_PARTIAL_MSG_LEFT);
   }
   if (TAILQ_EMPTY(&asoc->send_queue) &&
       TAILQ_EMPTY(&asoc->sent_queue) &&
       (asoc->state & SCTP_STATE_PARTIAL_MSG_LEFT)) {
    struct mbuf *op_err;
   abort_anyway:
    op_err = sctp_generate_cause(SCTP_CAUSE_USER_INITIATED_ABT, "");
    stcb->sctp_ep->last_abort_code = SCTP_FROM_SCTP_USRREQ + SCTP_LOC_6;
    SCTP_INP_RUNLOCK(inp);
    sctp_abort_an_association(stcb->sctp_ep, stcb,
                              op_err, false, SCTP_SO_LOCKED);
#if defined(__FreeBSD__) && !defined(__Userspace__)
    NET_EPOCH_EXIT(et);
#endif
    return (0);
   }
  }
  /* XXX: Why do this in the case where we have still data queued? */
  sctp_chunk_output(inp, stcb, SCTP_OUTPUT_FROM_CLOSING, SCTP_SO_LOCKED);
  SCTP_TCB_UNLOCK(stcb);
  SCTP_INP_RUNLOCK(inp);
#if defined(__FreeBSD__) && !defined(__Userspace__)
  NET_EPOCH_EXIT(et);
#endif
  return (0);
 }
}

/*
 * copies a "user" presentable address and removes embedded scope, etc.
 * returns 0 on success, 1 on error
 */

static uint32_t
sctp_fill_user_address(struct sockaddr *dst, struct sockaddr *src)
{
#ifdef INET6
#if defined(SCTP_EMBEDDED_V6_SCOPE)
 struct sockaddr_in6 lsa6;

 src = (struct sockaddr *)sctp_recover_scope((struct sockaddr_in6 *)src,
     &lsa6);
#endif
#endif
#ifdef HAVE_SA_LEN
 memcpy(dst, src, src->sa_len);
#else
 switch (src->sa_family) {
#ifdef INET
 case AF_INET:
  memcpy(dst, src, sizeof(struct sockaddr_in));
  break;
#endif
#ifdef INET6
 case AF_INET6:
  memcpy(dst, src, sizeof(struct sockaddr_in6));
  break;
#endif
#if defined(__Userspace__)
 case AF_CONN:
  memcpy(dst, src, sizeof(struct sockaddr_conn));
  break;
#endif
 default:
  /* TSNH */
  break;
 }
#endif
 return (0);
}

static size_t
sctp_fill_up_addresses_vrf(struct sctp_inpcb *inp,
                           struct sctp_tcb *stcb,
                           size_t limit,
                           struct sockaddr *addr,
                           uint32_t vrf_id)
{
 struct sctp_ifn *sctp_ifn;
 struct sctp_ifa *sctp_ifa;
 size_t actual;
 int loopback_scope;
#if defined(INET)
 int ipv4_local_scope, ipv4_addr_legal;
#endif
#if defined(INET6)
 int local_scope, site_scope, ipv6_addr_legal;
#endif
#if defined(__Userspace__)
 int conn_addr_legal;
#endif
 struct sctp_vrf *vrf;

 SCTP_IPI_ADDR_LOCK_ASSERT();
 actual = 0;
 if (limit == 0)
  return (actual);

 if (stcb) {
  /* Turn on all the appropriate scope */
  loopback_scope = stcb->asoc.scope.loopback_scope;
#if defined(INET)
  ipv4_local_scope = stcb->asoc.scope.ipv4_local_scope;
  ipv4_addr_legal = stcb->asoc.scope.ipv4_addr_legal;
#endif
#if defined(INET6)
  local_scope = stcb->asoc.scope.local_scope;
  site_scope = stcb->asoc.scope.site_scope;
  ipv6_addr_legal = stcb->asoc.scope.ipv6_addr_legal;
#endif
#if defined(__Userspace__)
  conn_addr_legal = stcb->asoc.scope.conn_addr_legal;
#endif
 } else {
  /* Use generic values for endpoints. */
  loopback_scope = 1;
#if defined(INET)
  ipv4_local_scope = 1;
#endif
#if defined(INET6)
  local_scope = 1;
  site_scope = 1;
#endif
  if (inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) {
#if defined(INET6)
   ipv6_addr_legal = 1;
#endif
#if defined(INET)
   if (SCTP_IPV6_V6ONLY(inp)) {
    ipv4_addr_legal = 0;
   } else {
    ipv4_addr_legal = 1;
   }
#endif
#if defined(__Userspace__)
   conn_addr_legal = 0;
#endif
  } else {
#if defined(INET6)
   ipv6_addr_legal = 0;
#endif
#if defined(__Userspace__)
   if (inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_CONN) {
    conn_addr_legal = 1;
#if defined(INET)
    ipv4_addr_legal = 0;
#endif
   } else {
    conn_addr_legal = 0;
#if defined(INET)
    ipv4_addr_legal = 1;
#endif
   }
#else
#if defined(INET)
   ipv4_addr_legal = 1;
#endif
#endif
  }
 }
 vrf = sctp_find_vrf(vrf_id);
 if (vrf == NULL) {
  return (0);
 }
 if (inp->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)) {
    /* Skip loopback if loopback_scope not set */
    continue;
   }
   LIST_FOREACH(sctp_ifa, &sctp_ifn->ifalist, next_ifa) {
    if (stcb) {
     /*
 * For the BOUND-ALL case, the list
 * associated with a TCB is Always
 * considered a reverse list.. i.e.
 * it lists addresses that are NOT
 * part of the association. If this
 * is one of those we must skip it.
 */

     if (sctp_is_addr_restricted(stcb,
                                 sctp_ifa)) {
      continue;
     }
    }
    switch (sctp_ifa->address.sa.sa_family) {
#ifdef INET
    case AF_INET:
     if (ipv4_addr_legal) {
      struct sockaddr_in *sin;

      sin = &sctp_ifa->address.sin;
      if (sin->sin_addr.s_addr == 0) {
       /*
 * we skip unspecified
 * addresses
 */

       continue;
      }
#if defined(__FreeBSD__) && !defined(__Userspace__)
      if (prison_check_ip4(inp->ip_inp.inp.inp_cred,
                           &sin->sin_addr) != 0) {
       continue;
      }
#endif
      if ((ipv4_local_scope == 0) &&
          (IN4_ISPRIVATE_ADDRESS(&sin->sin_addr))) {
       continue;
      }
#ifdef INET6
      if (sctp_is_feature_on(inp, SCTP_PCB_FLAGS_NEEDS_MAPPED_V4)) {
       if (actual + sizeof(struct sockaddr_in6) > limit) {
        return (actual);
       }
       in6_sin_2_v4mapsin6(sin, (struct sockaddr_in6 *)addr);
       ((struct sockaddr_in6 *)addr)->sin6_port = inp->sctp_lport;
       addr = (struct sockaddr *)((caddr_t)addr + sizeof(struct sockaddr_in6));
       actual += sizeof(struct sockaddr_in6);
      } else {
#endif
       if (actual + sizeof(struct sockaddr_in) > limit) {
        return (actual);
       }
       memcpy(addr, sin, sizeof(struct sockaddr_in));
       ((struct sockaddr_in *)addr)->sin_port = inp->sctp_lport;
       addr = (struct sockaddr *)((caddr_t)addr + sizeof(struct sockaddr_in));
       actual += sizeof(struct sockaddr_in);
#ifdef INET6
      }
#endif
     } else {
      continue;
     }
     break;
#endif
#ifdef INET6
    case AF_INET6:
     if (ipv6_addr_legal) {
      struct sockaddr_in6 *sin6;

#if defined(SCTP_EMBEDDED_V6_SCOPE) && !defined(SCTP_KAME)
      struct sockaddr_in6 lsa6;
#endif
      sin6 = &sctp_ifa->address.sin6;
      if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
       /*
 * we skip unspecified
 * addresses
 */

       continue;
      }
#if defined(__FreeBSD__) && !defined(__Userspace__)
      if (prison_check_ip6(inp->ip_inp.inp.inp_cred,
                           &sin6->sin6_addr) != 0) {
       continue;
      }
#endif
      if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
       if (local_scope == 0)
        continue;
#if defined(SCTP_EMBEDDED_V6_SCOPE)
       if (sin6->sin6_scope_id == 0) {
#ifdef SCTP_KAME
        if (sa6_recoverscope(sin6) != 0)
         /*
 * bad link
 * local
 * address
 */

         continue;
#else
        lsa6 = *sin6;
        if (in6_recoverscope(&lsa6,
               &lsa6.sin6_addr,
               NULL))
         /*
 * bad link
 * local
 * address
 */

        continue;
        sin6 = &lsa6;
#endif    /* SCTP_KAME */
       }
#endif /* SCTP_EMBEDDED_V6_SCOPE */
      }
      if ((site_scope == 0) &&
          (IN6_IS_ADDR_SITELOCAL(&sin6->sin6_addr))) {
       continue;
      }
      if (actual + sizeof(struct sockaddr_in6) > limit) {
       return (actual);
      }
      memcpy(addr, sin6, sizeof(struct sockaddr_in6));
      ((struct sockaddr_in6 *)addr)->sin6_port = inp->sctp_lport;
      addr = (struct sockaddr *)((caddr_t)addr + sizeof(struct sockaddr_in6));
      actual += sizeof(struct sockaddr_in6);
     } else {
      continue;
     }
     break;
#endif
#if defined(__Userspace__)
    case AF_CONN:
     if (conn_addr_legal) {
      if (actual + sizeof(struct sockaddr_conn) > limit) {
       return (actual);
      }
      memcpy(addr, &sctp_ifa->address.sconn, sizeof(struct sockaddr_conn));
      ((struct sockaddr_conn *)addr)->sconn_port = inp->sctp_lport;
      addr = (struct sockaddr *)((caddr_t)addr + sizeof(struct sockaddr_conn));
      actual += sizeof(struct sockaddr_conn);
     } else {
      continue;
     }
#endif
    default:
     /* TSNH */
     break;
    }
   }
  }
 } else {
  struct sctp_laddr *laddr;
  size_t sa_len;

  LIST_FOREACH(laddr, &inp->sctp_addr_list, sctp_nxt_addr) {
   if (stcb) {
    if (sctp_is_addr_restricted(stcb, laddr->ifa)) {
     continue;
    }
   }
#ifdef HAVE_SA_LEN
   sa_len = laddr->ifa->address.sa.sa_len;
#else
   switch (laddr->ifa->address.sa.sa_family) {
#ifdef INET
   case AF_INET:
    sa_len = sizeof(struct sockaddr_in);
    break;
#endif
#ifdef INET6
   case AF_INET6:
    sa_len = sizeof(struct sockaddr_in6);
    break;
#endif
#if defined(__Userspace__)
   case AF_CONN:
    sa_len = sizeof(struct sockaddr_conn);
    break;
#endif
   default:
    /* TSNH */
    sa_len = 0;
    break;
   }
#endif
   if (actual + sa_len > limit) {
    return (actual);
   }
   if (sctp_fill_user_address(addr, &laddr->ifa->address.sa))
    continue;
   switch (laddr->ifa->address.sa.sa_family) {
#ifdef INET
   case AF_INET:
    ((struct sockaddr_in *)addr)->sin_port = inp->sctp_lport;
    break;
#endif
#ifdef INET6
   case AF_INET6:
    ((struct sockaddr_in6 *)addr)->sin6_port = inp->sctp_lport;
    break;
#endif
#if defined(__Userspace__)
   case AF_CONN:
    ((struct sockaddr_conn *)addr)->sconn_port = inp->sctp_lport;
    break;
#endif
   default:
    /* TSNH */
    break;
   }
   addr = (struct sockaddr *)((caddr_t)addr + sa_len);
   actual += sa_len;
  }
 }
 return (actual);
}

static size_t
sctp_fill_up_addresses(struct sctp_inpcb *inp,
                       struct sctp_tcb *stcb,
                       size_t limit,
                       struct sockaddr *addr)
{
 size_t size;
#ifdef SCTP_MVRF
 uint32_t id;
#endif

 SCTP_IPI_ADDR_RLOCK();
#ifdef SCTP_MVRF
/*
 * FIX ME: ?? this WILL report duplicate addresses if they appear
 * in more than one VRF.
 */

 /* fill up addresses for all VRFs on the endpoint */
 size = 0;
 for (id = 0; (id < inp->num_vrfs) && (size < limit); id++) {
  size += sctp_fill_up_addresses_vrf(inp, stcb, limit, addr,
                                     inp->m_vrf_ids[id]);
  addr = (struct sockaddr *)((caddr_t)addr + size);
 }
#else
 /* fill up addresses for the endpoint's default vrf */
 size = sctp_fill_up_addresses_vrf(inp, stcb, limit, addr,
                                   inp->def_vrf_id);
#endif
 SCTP_IPI_ADDR_RUNLOCK();
 return (size);
}

static size_t
sctp_max_size_addresses_vrf(struct sctp_inpcb *inp, uint32_t vrf_id)
{
 struct sctp_vrf *vrf;
 size_t size;

 /*
 * In both sub-set bound an bound_all cases we return the size of
 * the maximum number of addresses that you could get. In reality
 * the sub-set bound may have an exclusion list for a given TCB or
 * in the bound-all case a TCB may NOT include the loopback or other
 * addresses as well.
 */

 SCTP_IPI_ADDR_LOCK_ASSERT();
 vrf = sctp_find_vrf(vrf_id);
 if (vrf == NULL) {
  return (0);
 }
 size = 0;
 if (inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) {
  struct sctp_ifn *sctp_ifn;
  struct sctp_ifa *sctp_ifa;

  LIST_FOREACH(sctp_ifn, &vrf->ifnlist, next_ifn) {
   LIST_FOREACH(sctp_ifa, &sctp_ifn->ifalist, next_ifa) {
    /* Count them if they are the right type */
    switch (sctp_ifa->address.sa.sa_family) {
#ifdef INET
    case AF_INET:
#ifdef INET6
     if (sctp_is_feature_on(inp, SCTP_PCB_FLAGS_NEEDS_MAPPED_V4))
      size += sizeof(struct sockaddr_in6);
     else
      size += sizeof(struct sockaddr_in);
#else
     size += sizeof(struct sockaddr_in);
#endif
     break;
#endif
#ifdef INET6
    case AF_INET6:
     size += sizeof(struct sockaddr_in6);
     break;
#endif
#if defined(__Userspace__)
    case AF_CONN:
     size += sizeof(struct sockaddr_conn);
     break;
#endif
    default:
     break;
    }
   }
  }
 } else {
  struct sctp_laddr *laddr;

  LIST_FOREACH(laddr, &inp->sctp_addr_list, sctp_nxt_addr) {
   switch (laddr->ifa->address.sa.sa_family) {
#ifdef INET
   case AF_INET:
#ifdef INET6
    if (sctp_is_feature_on(inp, SCTP_PCB_FLAGS_NEEDS_MAPPED_V4))
     size += sizeof(struct sockaddr_in6);
    else
     size += sizeof(struct sockaddr_in);
#else
    size += sizeof(struct sockaddr_in);
#endif
    break;
#endif
#ifdef INET6
   case AF_INET6:
    size += sizeof(struct sockaddr_in6);
    break;
#endif
#if defined(__Userspace__)
   case AF_CONN:
    size += sizeof(struct sockaddr_conn);
    break;
#endif
   default:
    break;
   }
  }
 }
 return (size);
}

static size_t
sctp_max_size_addresses(struct sctp_inpcb *inp)
{
 size_t size;
#ifdef SCTP_MVRF
 int id;
#endif

 SCTP_IPI_ADDR_RLOCK();
#ifdef SCTP_MVRF
/*
 * FIX ME: ?? this WILL count duplicate addresses if they appear
 * in more than one VRF.
 */

 /* Maximum size of all addresses for all VRFs on the endpoint */
 size = 0;
 for (id = 0; id < inp->num_vrfs; id++) {
  size += sctp_max_size_addresses_vrf(inp, inp->m_vrf_ids[id]);
 }
#else
 /* Maximum size of all addresses for the endpoint's default VRF */
 size = sctp_max_size_addresses_vrf(inp, inp->def_vrf_id);
#endif
 SCTP_IPI_ADDR_RUNLOCK();
 return (size);
}

static int
sctp_do_connect_x(struct socket *so, struct sctp_inpcb *inp, void *optval,
    size_t optsize, void *p, int delay)
{
 int error;
 int creat_lock_on = 0;
 struct sctp_tcb *stcb = NULL;
 struct sockaddr *sa;
 unsigned int num_v6 = 0, num_v4 = 0, *totaddrp, totaddr;
 uint32_t vrf_id;
 sctp_assoc_t *a_id;

 SCTPDBG(SCTP_DEBUG_PCB1, "Connectx called\n");

 if ((inp->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) &&
     (inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED)) {
  /* We are already connected AND the TCP model */
  SCTP_LTRACE_ERR_RET(inp, stcb, NULL, SCTP_FROM_SCTP_USRREQ, EADDRINUSE);
  return (EADDRINUSE);
 }

 if ((inp->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL) &&
     (sctp_is_feature_off(inp, SCTP_PCB_FLAGS_PORTREUSE))) {
  SCTP_LTRACE_ERR_RET(inp, stcb, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL);
  return (EINVAL);
 }

 if (inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) {
  SCTP_INP_RLOCK(inp);
  stcb = LIST_FIRST(&inp->sctp_asoc_list);
  SCTP_INP_RUNLOCK(inp);
 }
 if (stcb) {
  SCTP_LTRACE_ERR_RET(inp, stcb, NULL, SCTP_FROM_SCTP_USRREQ, EALREADY);
  return (EALREADY);
 }
 SCTP_INP_INCR_REF(inp);
 SCTP_ASOC_CREATE_LOCK(inp);
 creat_lock_on = 1;
 if ((inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_ALLGONE) ||
     (inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE)) {
  SCTP_LTRACE_ERR_RET(inp, stcb, NULL, SCTP_FROM_SCTP_USRREQ, EFAULT);
  error = EFAULT;
  goto out_now;
 }
 totaddrp = (unsigned int *)optval;
 totaddr = *totaddrp;
 sa = (struct sockaddr *)(totaddrp + 1);
 error = sctp_connectx_helper_find(inp, sa, totaddr, &num_v4, &num_v6, (unsigned int)(optsize - sizeof(int)));
 if (error != 0) {
  /* Already have or am bring up an association */
  SCTP_ASOC_CREATE_UNLOCK(inp);
  creat_lock_on = 0;
  SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, error);
  goto out_now;
 }
#ifdef INET6
 if (((inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) == 0) &&
     (num_v6 > 0)) {
  error = EINVAL;
  goto out_now;
 }
 if ((inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) &&
     (num_v4 > 0)) {
  if (SCTP_IPV6_V6ONLY(inp)) {
   /*
 * if IPV6_V6ONLY flag, ignore connections destined
 * to a v4 addr or v4-mapped addr
 */

   SCTP_LTRACE_ERR_RET(inp, stcb, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL);
   error = EINVAL;
   goto out_now;
  }
 }
#endif    /* INET6 */
 if (inp->sctp_flags & SCTP_PCB_FLAGS_UNBOUND) {
  /* Bind a ephemeral port */
  error = sctp_inpcb_bind(so, NULL, NULL, p);
  if (error) {
   goto out_now;
  }
 }

 /* FIX ME: do we want to pass in a vrf on the connect call? */
 vrf_id = inp->def_vrf_id;

 /* We are GOOD to go */
 stcb = sctp_aloc_assoc_connected(inp, sa, &error, 0, 0, vrf_id,
                                  inp->sctp_ep.pre_open_stream_count,
                                  inp->sctp_ep.port,
#if defined(__FreeBSD__) && !defined(__Userspace__)
                                  (struct thread *)p,
#elif defined(_WIN32) && !defined(__Userspace__)
                                  (PKTHREAD)p,
#else
                                  (struct proc *)p,
#endif
                                  SCTP_INITIALIZE_AUTH_PARAMS);
 if (stcb == NULL) {
  /* Gak! no memory */
  goto out_now;
 }
 SCTP_SET_STATE(stcb, SCTP_STATE_COOKIE_WAIT);
 /* move to second address */
 switch (sa->sa_family) {
#ifdef INET
 case AF_INET:
  sa = (struct sockaddr *)((caddr_t)sa + sizeof(struct sockaddr_in));
  break;
#endif
#ifdef INET6
 case AF_INET6:
  sa = (struct sockaddr *)((caddr_t)sa + sizeof(struct sockaddr_in6));
  break;
#endif
 default:
  break;
 }

 error = 0;
 sctp_connectx_helper_add(stcb, sa, (totaddr-1), &error);
 /* Fill in the return id */
 if (error) {
  goto out_now;
 }
 a_id = (sctp_assoc_t *)optval;
 *a_id = sctp_get_associd(stcb);

 if (delay) {
  /* doing delayed connection */
  stcb->asoc.delayed_connection = 1;
  sctp_timer_start(SCTP_TIMER_TYPE_INIT, inp, stcb, stcb->asoc.primary_destination);
 } else {
  (void)SCTP_GETTIME_TIMEVAL(&stcb->asoc.time_entered);
  sctp_send_initiate(inp, stcb, SCTP_SO_LOCKED);
 }
 SCTP_TCB_UNLOCK(stcb);
 out_now:
 if (creat_lock_on) {
  SCTP_ASOC_CREATE_UNLOCK(inp);
 }
 SCTP_INP_DECR_REF(inp);
 return (error);
}

#define SCTP_FIND_STCB(inp, stcb, assoc_id) { \
 if ((inp->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) ||\
     (inp->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL)) { \
  SCTP_INP_RLOCK(inp); \
  stcb = LIST_FIRST(&inp->sctp_asoc_list); \
  if (stcb) { \
   SCTP_TCB_LOCK(stcb); \
  } \
  SCTP_INP_RUNLOCK(inp); \
 } else if (assoc_id > SCTP_ALL_ASSOC) { \
  stcb = sctp_findassociation_ep_asocid(inp, assoc_id, 1); \
  if (stcb == NULL) { \
   SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, ENOENT); \
   error = ENOENT; \
   break; \
  } \
 } else { \
  stcb = NULL; \
 } \
}

#define SCTP_CHECK_AND_CAST(destp, srcp, type, size) {\
 if (size < sizeof(type)) { \
  SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL); \
  error = EINVAL; \
  break; \
 } else { \
  destp = (type *)srcp; \
 } \
}

#if defined(__Userspace__)
int
#else
static int
#endif
sctp_getopt(struct socket *so, int optname, void *optval, size_t *optsize,
     void *p) {
 struct sctp_inpcb *inp = NULL;
 int error, val = 0;
 struct sctp_tcb *stcb = NULL;

 if (optval == NULL) {
  SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL);
  return (EINVAL);
 }

 inp = (struct sctp_inpcb *)so->so_pcb;
 if (inp == NULL) {
  SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL);
  return EINVAL;
 }
 error = 0;

 switch (optname) {
 case SCTP_NODELAY:
 case SCTP_AUTOCLOSE:
 case SCTP_EXPLICIT_EOR:
 case SCTP_AUTO_ASCONF:
 case SCTP_DISABLE_FRAGMENTS:
 case SCTP_I_WANT_MAPPED_V4_ADDR:
 case SCTP_USE_EXT_RCVINFO:
  SCTP_INP_RLOCK(inp);
  switch (optname) {
  case SCTP_DISABLE_FRAGMENTS:
   val = sctp_is_feature_on(inp, SCTP_PCB_FLAGS_NO_FRAGMENT);
   break;
  case SCTP_I_WANT_MAPPED_V4_ADDR:
   val = sctp_is_feature_on(inp, SCTP_PCB_FLAGS_NEEDS_MAPPED_V4);
   break;
  case SCTP_AUTO_ASCONF:
   if (inp->sctp_flags & SCTP_PCB_FLAGS_BOUNDALL) {
    /* only valid for bound all sockets */
    val = sctp_is_feature_on(inp, SCTP_PCB_FLAGS_AUTO_ASCONF);
   } else {
    SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL);
    error = EINVAL;
    goto flags_out;
   }
   break;
  case SCTP_EXPLICIT_EOR:
   val = sctp_is_feature_on(inp, SCTP_PCB_FLAGS_EXPLICIT_EOR);
   break;
  case SCTP_NODELAY:
   val = sctp_is_feature_on(inp, SCTP_PCB_FLAGS_NODELAY);
   break;
  case SCTP_USE_EXT_RCVINFO:
   val = sctp_is_feature_on(inp, SCTP_PCB_FLAGS_EXT_RCVINFO);
   break;
  case SCTP_AUTOCLOSE:
   if (sctp_is_feature_on(inp, SCTP_PCB_FLAGS_AUTOCLOSE))
    val = sctp_ticks_to_secs(inp->sctp_ep.auto_close_time);
   else
    val = 0;
   break;

  default:
   SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, ENOPROTOOPT);
   error = ENOPROTOOPT;
  } /* end switch (sopt->sopt_name) */
  if (*optsize < sizeof(val)) {
   SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL);
   error = EINVAL;
  }
 flags_out:
  SCTP_INP_RUNLOCK(inp);
  if (error == 0) {
   /* return the option value */
   *(int *)optval = val;
   *optsize = sizeof(val);
  }
  break;
        case SCTP_GET_PACKET_LOG:
 {
#ifdef  SCTP_PACKET_LOGGING
  uint8_t *target;
  int ret;

  SCTP_CHECK_AND_CAST(target, optval, uint8_t, *optsize);
  ret = sctp_copy_out_packet_log(target , (int)*optsize);
  *optsize = ret;
#else
  SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EOPNOTSUPP);
  error = EOPNOTSUPP;
#endif
  break;
 }
 case SCTP_REUSE_PORT:
 {
  uint32_t *value;

  if ((inp->sctp_flags & SCTP_PCB_FLAGS_UDPTYPE)) {
   /* Can't do this for a 1-m socket */
   error = EINVAL;
   break;
  }
  SCTP_CHECK_AND_CAST(value, optval, uint32_t, *optsize);
  *value = sctp_is_feature_on(inp, SCTP_PCB_FLAGS_PORTREUSE);
  *optsize = sizeof(uint32_t);
  break;
 }
 case SCTP_PARTIAL_DELIVERY_POINT:
 {
  uint32_t *value;

  SCTP_CHECK_AND_CAST(value, optval, uint32_t, *optsize);
  *value = inp->partial_delivery_point;
  *optsize = sizeof(uint32_t);
  break;
 }
 case SCTP_FRAGMENT_INTERLEAVE:
 {
  uint32_t *value;

  SCTP_CHECK_AND_CAST(value, optval, uint32_t, *optsize);
  if (sctp_is_feature_on(inp, SCTP_PCB_FLAGS_FRAG_INTERLEAVE)) {
   if (sctp_is_feature_on(inp, SCTP_PCB_FLAGS_INTERLEAVE_STRMS)) {
    *value = SCTP_FRAG_LEVEL_2;
   } else {
    *value = SCTP_FRAG_LEVEL_1;
   }
  } else {
   *value = SCTP_FRAG_LEVEL_0;
  }
  *optsize = sizeof(uint32_t);
  break;
 }
 case SCTP_INTERLEAVING_SUPPORTED:
 {
  struct sctp_assoc_value *av;

  SCTP_CHECK_AND_CAST(av, optval, struct sctp_assoc_value, *optsize);
  SCTP_FIND_STCB(inp, stcb, av->assoc_id);

  if (stcb) {
   av->assoc_value = stcb->asoc.idata_supported;
   SCTP_TCB_UNLOCK(stcb);
  } else {
   if ((inp->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) ||
       (inp->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL) ||
       ((inp->sctp_flags & SCTP_PCB_FLAGS_UDPTYPE) &&
        (av->assoc_id == SCTP_FUTURE_ASSOC))) {
    SCTP_INP_RLOCK(inp);
    if (inp->idata_supported) {
     av->assoc_value = 1;
    } else {
     av->assoc_value = 0;
    }
    SCTP_INP_RUNLOCK(inp);
   } else {
    SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL);
    error = EINVAL;
   }
  }
  if (error == 0) {
   *optsize = sizeof(struct sctp_assoc_value);
  }
  break;
 }
 case SCTP_CMT_ON_OFF:
 {
  struct sctp_assoc_value *av;

  SCTP_CHECK_AND_CAST(av, optval, struct sctp_assoc_value, *optsize);
  SCTP_FIND_STCB(inp, stcb, av->assoc_id);
  if (stcb) {
   av->assoc_value = stcb->asoc.sctp_cmt_on_off;
   SCTP_TCB_UNLOCK(stcb);
  } else {
   if ((inp->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) ||
       (inp->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL) ||
       ((inp->sctp_flags & SCTP_PCB_FLAGS_UDPTYPE) &&
        (av->assoc_id == SCTP_FUTURE_ASSOC))) {
    SCTP_INP_RLOCK(inp);
    av->assoc_value = inp->sctp_cmt_on_off;
    SCTP_INP_RUNLOCK(inp);
   } else {
    SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL);
    error = EINVAL;
   }
  }
  if (error == 0) {
   *optsize = sizeof(struct sctp_assoc_value);
  }
  break;
 }
 case SCTP_PLUGGABLE_CC:
 {
  struct sctp_assoc_value *av;

--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=98 H=99 G=98

¤ Dauer der Verarbeitung: 0.22 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

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.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge