Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Linux/net/sctp/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 266 kB image not shown  

Quelle  socket.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-or-later
/* SCTP kernel implementation
 * (C) Copyright IBM Corp. 2001, 2004
 * Copyright (c) 1999-2000 Cisco, Inc.
 * Copyright (c) 1999-2001 Motorola, Inc.
 * Copyright (c) 2001-2003 Intel Corp.
 * Copyright (c) 2001-2002 Nokia, Inc.
 * Copyright (c) 2001 La Monte H.P. Yarroll
 *
 * This file is part of the SCTP kernel implementation
 *
 * These functions interface with the sockets layer to implement the
 * SCTP Extensions for the Sockets API.
 *
 * Note that the descriptions from the specification are USER level
 * functions--this file is the functions which populate the struct proto
 * for SCTP which is the BOTTOM of the sockets interface.
 *
 * Please send any bug reports or fixes you make to the
 * email address(es):
 *    lksctp developers <linux-sctp@vger.kernel.org>
 *
 * Written or modified by:
 *    La Monte H.P. Yarroll <piggy@acm.org>
 *    Narasimha Budihal     <narsi@refcode.org>
 *    Karl Knutson          <karl@athena.chicago.il.us>
 *    Jon Grimm             <jgrimm@us.ibm.com>
 *    Xingang Guo           <xingang.guo@intel.com>
 *    Daisy Chang           <daisyc@us.ibm.com>
 *    Sridhar Samudrala     <samudrala@us.ibm.com>
 *    Inaky Perez-Gonzalez  <inaky.gonzalez@intel.com>
 *    Ardelle Fan     <ardelle.fan@intel.com>
 *    Ryan Layer     <rmlayer@us.ibm.com>
 *    Anup Pemmaiah         <pemmaiah@cc.usu.edu>
 *    Kevin Gao             <kevin.gao@intel.com>
 */


#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

#include <crypto/hash.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/wait.h>
#include <linux/time.h>
#include <linux/sched/signal.h>
#include <linux/ip.h>
#include <linux/capability.h>
#include <linux/fcntl.h>
#include <linux/poll.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/file.h>
#include <linux/compat.h>
#include <linux/rhashtable.h>

#include <net/ip.h>
#include <net/icmp.h>
#include <net/route.h>
#include <net/ipv6.h>
#include <net/inet_common.h>
#include <net/busy_poll.h>
#include <trace/events/sock.h>

#include <linux/socket.h> /* for sa_family_t */
#include <linux/export.h>
#include <net/sock.h>
#include <net/sctp/sctp.h>
#include <net/sctp/sm.h>
#include <net/sctp/stream_sched.h>
#include <net/rps.h>

/* Forward declarations for internal helper functions. */
static bool sctp_writeable(const struct sock *sk);
static void sctp_wfree(struct sk_buff *skb);
static int sctp_wait_for_sndbuf(struct sctp_association *asoc,
    struct sctp_transport *transport,
    long *timeo_p, size_t msg_len);
static int sctp_wait_for_packet(struct sock *sk, int *err, long *timeo_p);
static int sctp_wait_for_connect(struct sctp_association *, long *timeo_p);
static int sctp_wait_for_accept(struct sock *sk, long timeo);
static void sctp_wait_for_close(struct sock *sk, long timeo);
static void sctp_destruct_sock(struct sock *sk);
static struct sctp_af *sctp_sockaddr_af(struct sctp_sock *opt,
     union sctp_addr *addr, int len);
static int sctp_bindx_add(struct sock *, struct sockaddr *, int);
static int sctp_bindx_rem(struct sock *, struct sockaddr *, int);
static int sctp_send_asconf_add_ip(struct sock *, struct sockaddr *, int);
static int sctp_send_asconf_del_ip(struct sock *, struct sockaddr *, int);
static int sctp_send_asconf(struct sctp_association *asoc,
       struct sctp_chunk *chunk);
static int sctp_do_bind(struct sock *, union sctp_addr *, int);
static int sctp_autobind(struct sock *sk);
static int sctp_sock_migrate(struct sock *oldsk, struct sock *newsk,
        struct sctp_association *assoc,
        enum sctp_socket_type type);

static unsigned long sctp_memory_pressure;
static atomic_long_t sctp_memory_allocated;
static DEFINE_PER_CPU(int, sctp_memory_per_cpu_fw_alloc);
struct percpu_counter sctp_sockets_allocated;

static void sctp_enter_memory_pressure(struct sock *sk)
{
 WRITE_ONCE(sctp_memory_pressure, 1);
}


/* Get the sndbuf space available at the time on the association.  */
static inline int sctp_wspace(struct sctp_association *asoc)
{
 struct sock *sk = asoc->base.sk;

 return asoc->ep->sndbuf_policy ? sk->sk_sndbuf - asoc->sndbuf_used
           : sk_stream_wspace(sk);
}

/* Increment the used sndbuf space count of the corresponding association by
 * the size of the outgoing data chunk.
 * Also, set the skb destructor for sndbuf accounting later.
 *
 * Since it is always 1-1 between chunk and skb, and also a new skb is always
 * allocated for chunk bundling in sctp_packet_transmit(), we can use the
 * destructor in the data chunk skb for the purpose of the sndbuf space
 * tracking.
 */

static inline void sctp_set_owner_w(struct sctp_chunk *chunk)
{
 struct sctp_association *asoc = chunk->asoc;
 struct sock *sk = asoc->base.sk;

 /* The sndbuf space is tracked per association.  */
 sctp_association_hold(asoc);

 if (chunk->shkey)
  sctp_auth_shkey_hold(chunk->shkey);

 skb_set_owner_w(chunk->skb, sk);

 chunk->skb->destructor = sctp_wfree;
 /* Save the chunk pointer in skb for sctp_wfree to use later.  */
 skb_shinfo(chunk->skb)->destructor_arg = chunk;

 refcount_add(sizeof(struct sctp_chunk), &sk->sk_wmem_alloc);
 asoc->sndbuf_used += chunk->skb->truesize + sizeof(struct sctp_chunk);
 sk_wmem_queued_add(sk, chunk->skb->truesize + sizeof(struct sctp_chunk));
 sk_mem_charge(sk, chunk->skb->truesize);
}

static void sctp_clear_owner_w(struct sctp_chunk *chunk)
{
 skb_orphan(chunk->skb);
}

#define traverse_and_process() \
do {    \
 msg = chunk->msg; \
 if (msg == prev_msg) \
  continue; \
 list_for_each_entry(c, &msg->chunks, frag_list) { \
  if ((clear && asoc->base.sk == c->skb->sk) || \
      (!clear && asoc->base.sk != c->skb->sk)) \
   cb(c); \
 }   \
 prev_msg = msg;  \
while (0)

static void sctp_for_each_tx_datachunk(struct sctp_association *asoc,
           bool clear,
           void (*cb)(struct sctp_chunk *))

{
 struct sctp_datamsg *msg, *prev_msg = NULL;
 struct sctp_outq *q = &asoc->outqueue;
 struct sctp_chunk *chunk, *c;
 struct sctp_transport *t;

 list_for_each_entry(t, &asoc->peer.transport_addr_list, transports)
  list_for_each_entry(chunk, &t->transmitted, transmitted_list)
   traverse_and_process();

 list_for_each_entry(chunk, &q->retransmit, transmitted_list)
  traverse_and_process();

 list_for_each_entry(chunk, &q->sacked, transmitted_list)
  traverse_and_process();

 list_for_each_entry(chunk, &q->abandoned, transmitted_list)
  traverse_and_process();

 list_for_each_entry(chunk, &q->out_chunk_list, list)
  traverse_and_process();
}

static void sctp_for_each_rx_skb(struct sctp_association *asoc, struct sock *sk,
     void (*cb)(struct sk_buff *, struct sock *))

{
 struct sk_buff *skb, *tmp;

 sctp_skb_for_each(skb, &asoc->ulpq.lobby, tmp)
  cb(skb, sk);

 sctp_skb_for_each(skb, &asoc->ulpq.reasm, tmp)
  cb(skb, sk);

 sctp_skb_for_each(skb, &asoc->ulpq.reasm_uo, tmp)
  cb(skb, sk);
}

/* Verify that this is a valid address. */
static inline int sctp_verify_addr(struct sock *sk, union sctp_addr *addr,
       int len)
{
 struct sctp_af *af;

 /* Verify basic sockaddr. */
 af = sctp_sockaddr_af(sctp_sk(sk), addr, len);
 if (!af)
  return -EINVAL;

 /* Is this a valid SCTP address?  */
 if (!af->addr_valid(addr, sctp_sk(sk), NULL))
  return -EINVAL;

 if (!sctp_sk(sk)->pf->send_verify(sctp_sk(sk), (addr)))
  return -EINVAL;

 return 0;
}

/* Look up the association by its id.  If this is not a UDP-style
 * socket, the ID field is always ignored.
 */

struct sctp_association *sctp_id2assoc(struct sock *sk, sctp_assoc_t id)
{
 struct sctp_association *asoc = NULL;

 /* If this is not a UDP-style socket, assoc id should be ignored. */
 if (!sctp_style(sk, UDP)) {
  /* Return NULL if the socket state is not ESTABLISHED. It
 * could be a TCP-style listening socket or a socket which
 * hasn't yet called connect() to establish an association.
 */

  if (!sctp_sstate(sk, ESTABLISHED) && !sctp_sstate(sk, CLOSING))
   return NULL;

  /* Get the first and the only association from the list. */
  if (!list_empty(&sctp_sk(sk)->ep->asocs))
   asoc = list_entry(sctp_sk(sk)->ep->asocs.next,
       struct sctp_association, asocs);
  return asoc;
 }

 /* Otherwise this is a UDP-style socket. */
 if (id <= SCTP_ALL_ASSOC)
  return NULL;

 spin_lock_bh(&sctp_assocs_id_lock);
 asoc = (struct sctp_association *)idr_find(&sctp_assocs_id, (int)id);
 if (asoc && (asoc->base.sk != sk || asoc->base.dead))
  asoc = NULL;
 spin_unlock_bh(&sctp_assocs_id_lock);

 return asoc;
}

/* Look up the transport from an address and an assoc id. If both address and
 * id are specified, the associations matching the address and the id should be
 * the same.
 */

static struct sctp_transport *sctp_addr_id2transport(struct sock *sk,
           struct sockaddr_storage *addr,
           sctp_assoc_t id)
{
 struct sctp_association *addr_asoc = NULL, *id_asoc = NULL;
 struct sctp_af *af = sctp_get_af_specific(addr->ss_family);
 union sctp_addr *laddr = (union sctp_addr *)addr;
 struct sctp_transport *transport;

 if (!af || sctp_verify_addr(sk, laddr, af->sockaddr_len))
  return NULL;

 addr_asoc = sctp_endpoint_lookup_assoc(sctp_sk(sk)->ep,
            laddr,
            &transport);

 if (!addr_asoc)
  return NULL;

 id_asoc = sctp_id2assoc(sk, id);
 if (id_asoc && (id_asoc != addr_asoc))
  return NULL;

 sctp_get_pf_specific(sk->sk_family)->addr_to_user(sctp_sk(sk),
      (union sctp_addr *)addr);

 return transport;
}

/* API 3.1.2 bind() - UDP Style Syntax
 * The syntax of bind() is,
 *
 *   ret = bind(int sd, struct sockaddr *addr, int addrlen);
 *
 *   sd      - the socket descriptor returned by socket().
 *   addr    - the address structure (struct sockaddr_in or struct
 *             sockaddr_in6 [RFC 2553]),
 *   addr_len - the size of the address structure.
 */

static int sctp_bind(struct sock *sk, struct sockaddr *addr, int addr_len)
{
 int retval = 0;

 lock_sock(sk);

 pr_debug("%s: sk:%p, addr:%p, addr_len:%d\n", __func__, sk,
   addr, addr_len);

 /* Disallow binding twice. */
 if (!sctp_sk(sk)->ep->base.bind_addr.port)
  retval = sctp_do_bind(sk, (union sctp_addr *)addr,
          addr_len);
 else
  retval = -EINVAL;

 release_sock(sk);

 return retval;
}

static int sctp_get_port_local(struct sock *, union sctp_addr *);

/* Verify this is a valid sockaddr. */
static struct sctp_af *sctp_sockaddr_af(struct sctp_sock *opt,
     union sctp_addr *addr, int len)
{
 struct sctp_af *af;

 /* Check minimum size.  */
 if (len < sizeof (struct sockaddr))
  return NULL;

 if (!opt->pf->af_supported(addr->sa.sa_family, opt))
  return NULL;

 if (addr->sa.sa_family == AF_INET6) {
  if (len < SIN6_LEN_RFC2133)
   return NULL;
  /* V4 mapped address are really of AF_INET family */
  if (ipv6_addr_v4mapped(&addr->v6.sin6_addr) &&
      !opt->pf->af_supported(AF_INET, opt))
   return NULL;
 }

 /* If we get this far, af is valid. */
 af = sctp_get_af_specific(addr->sa.sa_family);

 if (len < af->sockaddr_len)
  return NULL;

 return af;
}

static void sctp_auto_asconf_init(struct sctp_sock *sp)
{
 struct net *net = sock_net(&sp->inet.sk);

 if (net->sctp.default_auto_asconf) {
  spin_lock_bh(&net->sctp.addr_wq_lock);
  list_add_tail(&sp->auto_asconf_list, &net->sctp.auto_asconf_splist);
  spin_unlock_bh(&net->sctp.addr_wq_lock);
  sp->do_auto_asconf = 1;
 }
}

/* Bind a local address either to an endpoint or to an association.  */
static int sctp_do_bind(struct sock *sk, union sctp_addr *addr, int len)
{
 struct net *net = sock_net(sk);
 struct sctp_sock *sp = sctp_sk(sk);
 struct sctp_endpoint *ep = sp->ep;
 struct sctp_bind_addr *bp = &ep->base.bind_addr;
 struct sctp_af *af;
 unsigned short snum;
 int ret = 0;

 /* Common sockaddr verification. */
 af = sctp_sockaddr_af(sp, addr, len);
 if (!af) {
  pr_debug("%s: sk:%p, newaddr:%p, len:%d EINVAL\n",
    __func__, sk, addr, len);
  return -EINVAL;
 }

 snum = ntohs(addr->v4.sin_port);

 pr_debug("%s: sk:%p, new addr:%pISc, port:%d, new port:%d, len:%d\n",
   __func__, sk, &addr->sa, bp->port, snum, len);

 /* PF specific bind() address verification. */
 if (!sp->pf->bind_verify(sp, addr))
  return -EADDRNOTAVAIL;

 /* We must either be unbound, or bind to the same port.
 * It's OK to allow 0 ports if we are already bound.
 * We'll just inhert an already bound port in this case
 */

 if (bp->port) {
  if (!snum)
   snum = bp->port;
  else if (snum != bp->port) {
   pr_debug("%s: new port %d doesn't match existing port "
     "%d\n", __func__, snum, bp->port);
   return -EINVAL;
  }
 }

 if (snum && inet_port_requires_bind_service(net, snum) &&
     !ns_capable(net->user_ns, CAP_NET_BIND_SERVICE))
  return -EACCES;

 /* See if the address matches any of the addresses we may have
 * already bound before checking against other endpoints.
 */

 if (sctp_bind_addr_match(bp, addr, sp))
  return -EINVAL;

 /* Make sure we are allowed to bind here.
 * The function sctp_get_port_local() does duplicate address
 * detection.
 */

 addr->v4.sin_port = htons(snum);
 if (sctp_get_port_local(sk, addr))
  return -EADDRINUSE;

 /* Refresh ephemeral port.  */
 if (!bp->port) {
  bp->port = inet_sk(sk)->inet_num;
  sctp_auto_asconf_init(sp);
 }

 /* Add the address to the bind address list.
 * Use GFP_ATOMIC since BHs will be disabled.
 */

 ret = sctp_add_bind_addr(bp, addr, af->sockaddr_len,
     SCTP_ADDR_SRC, GFP_ATOMIC);

 if (ret) {
  sctp_put_port(sk);
  return ret;
 }
 /* Copy back into socket for getsockname() use. */
 inet_sk(sk)->inet_sport = htons(inet_sk(sk)->inet_num);
 sp->pf->to_sk_saddr(addr, sk);

 return ret;
}

 /* ADDIP Section 4.1.1 Congestion Control of ASCONF Chunks
 *
 * R1) One and only one ASCONF Chunk MAY be in transit and unacknowledged
 * at any one time.  If a sender, after sending an ASCONF chunk, decides
 * it needs to transfer another ASCONF Chunk, it MUST wait until the
 * ASCONF-ACK Chunk returns from the previous ASCONF Chunk before sending a
 * subsequent ASCONF. Note this restriction binds each side, so at any
 * time two ASCONF may be in-transit on any given association (one sent
 * from each endpoint).
 */

static int sctp_send_asconf(struct sctp_association *asoc,
       struct sctp_chunk *chunk)
{
 int retval = 0;

 /* If there is an outstanding ASCONF chunk, queue it for later
 * transmission.
 */

 if (asoc->addip_last_asconf) {
  list_add_tail(&chunk->list, &asoc->addip_chunk_list);
  goto out;
 }

 /* Hold the chunk until an ASCONF_ACK is received. */
 sctp_chunk_hold(chunk);
 retval = sctp_primitive_ASCONF(asoc->base.net, asoc, chunk);
 if (retval)
  sctp_chunk_free(chunk);
 else
  asoc->addip_last_asconf = chunk;

out:
 return retval;
}

/* Add a list of addresses as bind addresses to local endpoint or
 * association.
 *
 * Basically run through each address specified in the addrs/addrcnt
 * array/length pair, determine if it is IPv6 or IPv4 and call
 * sctp_do_bind() on it.
 *
 * If any of them fails, then the operation will be reversed and the
 * ones that were added will be removed.
 *
 * Only sctp_setsockopt_bindx() is supposed to call this function.
 */

static int sctp_bindx_add(struct sock *sk, struct sockaddr *addrs, int addrcnt)
{
 int cnt;
 int retval = 0;
 void *addr_buf;
 struct sockaddr *sa_addr;
 struct sctp_af *af;

 pr_debug("%s: sk:%p, addrs:%p, addrcnt:%d\n", __func__, sk,
   addrs, addrcnt);

 addr_buf = addrs;
 for (cnt = 0; cnt < addrcnt; cnt++) {
  /* The list may contain either IPv4 or IPv6 address;
 * determine the address length for walking thru the list.
 */

  sa_addr = addr_buf;
  af = sctp_get_af_specific(sa_addr->sa_family);
  if (!af) {
   retval = -EINVAL;
   goto err_bindx_add;
  }

  retval = sctp_do_bind(sk, (union sctp_addr *)sa_addr,
          af->sockaddr_len);

  addr_buf += af->sockaddr_len;

err_bindx_add:
  if (retval < 0) {
   /* Failed. Cleanup the ones that have been added */
   if (cnt > 0)
    sctp_bindx_rem(sk, addrs, cnt);
   return retval;
  }
 }

 return retval;
}

/* Send an ASCONF chunk with Add IP address parameters to all the peers of the
 * associations that are part of the endpoint indicating that a list of local
 * addresses are added to the endpoint.
 *
 * If any of the addresses is already in the bind address list of the
 * association, we do not send the chunk for that association.  But it will not
 * affect other associations.
 *
 * Only sctp_setsockopt_bindx() is supposed to call this function.
 */

static int sctp_send_asconf_add_ip(struct sock  *sk,
       struct sockaddr *addrs,
       int    addrcnt)
{
 struct sctp_sock  *sp;
 struct sctp_endpoint  *ep;
 struct sctp_association  *asoc;
 struct sctp_bind_addr  *bp;
 struct sctp_chunk  *chunk;
 struct sctp_sockaddr_entry *laddr;
 union sctp_addr   *addr;
 union sctp_addr   saveaddr;
 void    *addr_buf;
 struct sctp_af   *af;
 struct list_head  *p;
 int     i;
 int     retval = 0;

 sp = sctp_sk(sk);
 ep = sp->ep;

 if (!ep->asconf_enable)
  return retval;

 pr_debug("%s: sk:%p, addrs:%p, addrcnt:%d\n",
   __func__, sk, addrs, addrcnt);

 list_for_each_entry(asoc, &ep->asocs, asocs) {
  if (!asoc->peer.asconf_capable)
   continue;

  if (asoc->peer.addip_disabled_mask & SCTP_PARAM_ADD_IP)
   continue;

  if (!sctp_state(asoc, ESTABLISHED))
   continue;

  /* Check if any address in the packed array of addresses is
 * in the bind address list of the association. If so,
 * do not send the asconf chunk to its peer, but continue with
 * other associations.
 */

  addr_buf = addrs;
  for (i = 0; i < addrcnt; i++) {
   addr = addr_buf;
   af = sctp_get_af_specific(addr->v4.sin_family);
   if (!af) {
    retval = -EINVAL;
    goto out;
   }

   if (sctp_assoc_lookup_laddr(asoc, addr))
    break;

   addr_buf += af->sockaddr_len;
  }
  if (i < addrcnt)
   continue;

  /* Use the first valid address in bind addr list of
 * association as Address Parameter of ASCONF CHUNK.
 */

  bp = &asoc->base.bind_addr;
  p = bp->address_list.next;
  laddr = list_entry(p, struct sctp_sockaddr_entry, list);
  chunk = sctp_make_asconf_update_ip(asoc, &laddr->a, addrs,
         addrcnt, SCTP_PARAM_ADD_IP);
  if (!chunk) {
   retval = -ENOMEM;
   goto out;
  }

  /* Add the new addresses to the bind address list with
 * use_as_src set to 0.
 */

  addr_buf = addrs;
  for (i = 0; i < addrcnt; i++) {
   addr = addr_buf;
   af = sctp_get_af_specific(addr->v4.sin_family);
   memcpy(&saveaddr, addr, af->sockaddr_len);
   retval = sctp_add_bind_addr(bp, &saveaddr,
          sizeof(saveaddr),
          SCTP_ADDR_NEW, GFP_ATOMIC);
   addr_buf += af->sockaddr_len;
  }
  if (asoc->src_out_of_asoc_ok) {
   struct sctp_transport *trans;

   list_for_each_entry(trans,
       &asoc->peer.transport_addr_list, transports) {
    trans->cwnd = min(4*asoc->pathmtu, max_t(__u32,
        2*asoc->pathmtu, 4380));
    trans->ssthresh = asoc->peer.i.a_rwnd;
    trans->rto = asoc->rto_initial;
    sctp_max_rto(asoc, trans);
    trans->rtt = trans->srtt = trans->rttvar = 0;
    /* Clear the source and route cache */
    sctp_transport_route(trans, NULL,
           sctp_sk(asoc->base.sk));
   }
  }
  retval = sctp_send_asconf(asoc, chunk);
 }

out:
 return retval;
}

/* Remove a list of addresses from bind addresses list.  Do not remove the
 * last address.
 *
 * Basically run through each address specified in the addrs/addrcnt
 * array/length pair, determine if it is IPv6 or IPv4 and call
 * sctp_del_bind() on it.
 *
 * If any of them fails, then the operation will be reversed and the
 * ones that were removed will be added back.
 *
 * At least one address has to be left; if only one address is
 * available, the operation will return -EBUSY.
 *
 * Only sctp_setsockopt_bindx() is supposed to call this function.
 */

static int sctp_bindx_rem(struct sock *sk, struct sockaddr *addrs, int addrcnt)
{
 struct sctp_sock *sp = sctp_sk(sk);
 struct sctp_endpoint *ep = sp->ep;
 int cnt;
 struct sctp_bind_addr *bp = &ep->base.bind_addr;
 int retval = 0;
 void *addr_buf;
 union sctp_addr *sa_addr;
 struct sctp_af *af;

 pr_debug("%s: sk:%p, addrs:%p, addrcnt:%d\n",
   __func__, sk, addrs, addrcnt);

 addr_buf = addrs;
 for (cnt = 0; cnt < addrcnt; cnt++) {
  /* If the bind address list is empty or if there is only one
 * bind address, there is nothing more to be removed (we need
 * at least one address here).
 */

  if (list_empty(&bp->address_list) ||
      (sctp_list_single_entry(&bp->address_list))) {
   retval = -EBUSY;
   goto err_bindx_rem;
  }

  sa_addr = addr_buf;
  af = sctp_get_af_specific(sa_addr->sa.sa_family);
  if (!af) {
   retval = -EINVAL;
   goto err_bindx_rem;
  }

  if (!af->addr_valid(sa_addr, sp, NULL)) {
   retval = -EADDRNOTAVAIL;
   goto err_bindx_rem;
  }

  if (sa_addr->v4.sin_port &&
      sa_addr->v4.sin_port != htons(bp->port)) {
   retval = -EINVAL;
   goto err_bindx_rem;
  }

  if (!sa_addr->v4.sin_port)
   sa_addr->v4.sin_port = htons(bp->port);

  /* FIXME - There is probably a need to check if sk->sk_saddr and
 * sk->sk_rcv_addr are currently set to one of the addresses to
 * be removed. This is something which needs to be looked into
 * when we are fixing the outstanding issues with multi-homing
 * socket routing and failover schemes. Refer to comments in
 * sctp_do_bind(). -daisy
 */

  retval = sctp_del_bind_addr(bp, sa_addr);

  addr_buf += af->sockaddr_len;
err_bindx_rem:
  if (retval < 0) {
   /* Failed. Add the ones that has been removed back */
   if (cnt > 0)
    sctp_bindx_add(sk, addrs, cnt);
   return retval;
  }
 }

 return retval;
}

/* Send an ASCONF chunk with Delete IP address parameters to all the peers of
 * the associations that are part of the endpoint indicating that a list of
 * local addresses are removed from the endpoint.
 *
 * If any of the addresses is already in the bind address list of the
 * association, we do not send the chunk for that association.  But it will not
 * affect other associations.
 *
 * Only sctp_setsockopt_bindx() is supposed to call this function.
 */

static int sctp_send_asconf_del_ip(struct sock  *sk,
       struct sockaddr *addrs,
       int   addrcnt)
{
 struct sctp_sock *sp;
 struct sctp_endpoint *ep;
 struct sctp_association *asoc;
 struct sctp_transport *transport;
 struct sctp_bind_addr *bp;
 struct sctp_chunk *chunk;
 union sctp_addr  *laddr;
 void   *addr_buf;
 struct sctp_af  *af;
 struct sctp_sockaddr_entry *saddr;
 int    i;
 int    retval = 0;
 int   stored = 0;

 chunk = NULL;
 sp = sctp_sk(sk);
 ep = sp->ep;

 if (!ep->asconf_enable)
  return retval;

 pr_debug("%s: sk:%p, addrs:%p, addrcnt:%d\n",
   __func__, sk, addrs, addrcnt);

 list_for_each_entry(asoc, &ep->asocs, asocs) {

  if (!asoc->peer.asconf_capable)
   continue;

  if (asoc->peer.addip_disabled_mask & SCTP_PARAM_DEL_IP)
   continue;

  if (!sctp_state(asoc, ESTABLISHED))
   continue;

  /* Check if any address in the packed array of addresses is
 * not present in the bind address list of the association.
 * If so, do not send the asconf chunk to its peer, but
 * continue with other associations.
 */

  addr_buf = addrs;
  for (i = 0; i < addrcnt; i++) {
   laddr = addr_buf;
   af = sctp_get_af_specific(laddr->v4.sin_family);
   if (!af) {
    retval = -EINVAL;
    goto out;
   }

   if (!sctp_assoc_lookup_laddr(asoc, laddr))
    break;

   addr_buf += af->sockaddr_len;
  }
  if (i < addrcnt)
   continue;

  /* Find one address in the association's bind address list
 * that is not in the packed array of addresses. This is to
 * make sure that we do not delete all the addresses in the
 * association.
 */

  bp = &asoc->base.bind_addr;
  laddr = sctp_find_unmatch_addr(bp, (union sctp_addr *)addrs,
            addrcnt, sp);
  if ((laddr == NULL) && (addrcnt == 1)) {
   if (asoc->asconf_addr_del_pending)
    continue;
   asoc->asconf_addr_del_pending =
       kzalloc(sizeof(union sctp_addr), GFP_ATOMIC);
   if (asoc->asconf_addr_del_pending == NULL) {
    retval = -ENOMEM;
    goto out;
   }
   asoc->asconf_addr_del_pending->sa.sa_family =
        addrs->sa_family;
   asoc->asconf_addr_del_pending->v4.sin_port =
        htons(bp->port);
   if (addrs->sa_family == AF_INET) {
    struct sockaddr_in *sin;

    sin = (struct sockaddr_in *)addrs;
    asoc->asconf_addr_del_pending->v4.sin_addr.s_addr = sin->sin_addr.s_addr;
   } else if (addrs->sa_family == AF_INET6) {
    struct sockaddr_in6 *sin6;

    sin6 = (struct sockaddr_in6 *)addrs;
    asoc->asconf_addr_del_pending->v6.sin6_addr = sin6->sin6_addr;
   }

   pr_debug("%s: keep the last address asoc:%p %pISc at %p\n",
     __func__, asoc, &asoc->asconf_addr_del_pending->sa,
     asoc->asconf_addr_del_pending);

   asoc->src_out_of_asoc_ok = 1;
   stored = 1;
   goto skip_mkasconf;
  }

  if (laddr == NULL)
   return -EINVAL;

  /* We do not need RCU protection throughout this loop
 * because this is done under a socket lock from the
 * setsockopt call.
 */

  chunk = sctp_make_asconf_update_ip(asoc, laddr, addrs, addrcnt,
         SCTP_PARAM_DEL_IP);
  if (!chunk) {
   retval = -ENOMEM;
   goto out;
  }

skip_mkasconf:
  /* Reset use_as_src flag for the addresses in the bind address
 * list that are to be deleted.
 */

  addr_buf = addrs;
  for (i = 0; i < addrcnt; i++) {
   laddr = addr_buf;
   af = sctp_get_af_specific(laddr->v4.sin_family);
   list_for_each_entry(saddr, &bp->address_list, list) {
    if (sctp_cmp_addr_exact(&saddr->a, laddr))
     saddr->state = SCTP_ADDR_DEL;
   }
   addr_buf += af->sockaddr_len;
  }

  /* Update the route and saddr entries for all the transports
 * as some of the addresses in the bind address list are
 * about to be deleted and cannot be used as source addresses.
 */

  list_for_each_entry(transport, &asoc->peer.transport_addr_list,
     transports) {
   sctp_transport_route(transport, NULL,
          sctp_sk(asoc->base.sk));
  }

  if (stored)
   /* We don't need to transmit ASCONF */
   continue;
  retval = sctp_send_asconf(asoc, chunk);
 }
out:
 return retval;
}

/* set addr events to assocs in the endpoint.  ep and addr_wq must be locked */
int sctp_asconf_mgmt(struct sctp_sock *sp, struct sctp_sockaddr_entry *addrw)
{
 struct sock *sk = sctp_opt2sk(sp);
 union sctp_addr *addr;
 struct sctp_af *af;

 /* It is safe to write port space in caller. */
 addr = &addrw->a;
 addr->v4.sin_port = htons(sp->ep->base.bind_addr.port);
 af = sctp_get_af_specific(addr->sa.sa_family);
 if (!af)
  return -EINVAL;
 if (sctp_verify_addr(sk, addr, af->sockaddr_len))
  return -EINVAL;

 if (addrw->state == SCTP_ADDR_NEW)
  return sctp_send_asconf_add_ip(sk, (struct sockaddr *)addr, 1);
 else
  return sctp_send_asconf_del_ip(sk, (struct sockaddr *)addr, 1);
}

/* Helper for tunneling sctp_bindx() requests through sctp_setsockopt()
 *
 * API 8.1
 * int sctp_bindx(int sd, struct sockaddr *addrs, int addrcnt,
 *                int flags);
 *
 * If sd is an IPv4 socket, the addresses passed must be IPv4 addresses.
 * If the sd is an IPv6 socket, the addresses passed can either be IPv4
 * or IPv6 addresses.
 *
 * A single address may be specified as INADDR_ANY or IN6ADDR_ANY, see
 * Section 3.1.2 for this usage.
 *
 * addrs is a pointer to an array of one or more socket addresses. Each
 * address is contained in its appropriate structure (i.e. struct
 * sockaddr_in or struct sockaddr_in6) the family of the address type
 * must be used to distinguish the address length (note that this
 * representation is termed a "packed array" of addresses). The caller
 * specifies the number of addresses in the array with addrcnt.
 *
 * On success, sctp_bindx() returns 0. On failure, sctp_bindx() returns
 * -1, and sets errno to the appropriate error code.
 *
 * For SCTP, the port given in each socket address must be the same, or
 * sctp_bindx() will fail, setting errno to EINVAL.
 *
 * The flags parameter is formed from the bitwise OR of zero or more of
 * the following currently defined flags:
 *
 * SCTP_BINDX_ADD_ADDR
 *
 * SCTP_BINDX_REM_ADDR
 *
 * SCTP_BINDX_ADD_ADDR directs SCTP to add the given addresses to the
 * association, and SCTP_BINDX_REM_ADDR directs SCTP to remove the given
 * addresses from the association. The two flags are mutually exclusive;
 * if both are given, sctp_bindx() will fail with EINVAL. A caller may
 * not remove all addresses from an association; sctp_bindx() will
 * reject such an attempt with EINVAL.
 *
 * An application can use sctp_bindx(SCTP_BINDX_ADD_ADDR) to associate
 * additional addresses with an endpoint after calling bind().  Or use
 * sctp_bindx(SCTP_BINDX_REM_ADDR) to remove some addresses a listening
 * socket is associated with so that no new association accepted will be
 * associated with those addresses. If the endpoint supports dynamic
 * address a SCTP_BINDX_REM_ADDR or SCTP_BINDX_ADD_ADDR may cause a
 * endpoint to send the appropriate message to the peer to change the
 * peers address lists.
 *
 * Adding and removing addresses from a connected association is
 * optional functionality. Implementations that do not support this
 * functionality should return EOPNOTSUPP.
 *
 * Basically do nothing but copying the addresses from user to kernel
 * land and invoking either sctp_bindx_add() or sctp_bindx_rem() on the sk.
 * This is used for tunneling the sctp_bindx() request through sctp_setsockopt()
 * from userspace.
 *
 * On exit there is no need to do sockfd_put(), sys_setsockopt() does
 * it.
 *
 * sk        The sk of the socket
 * addrs     The pointer to the addresses
 * addrssize Size of the addrs buffer
 * op        Operation to perform (add or remove, see the flags of
 *           sctp_bindx)
 *
 * Returns 0 if ok, <0 errno code on error.
 */

static int sctp_setsockopt_bindx(struct sock *sk, struct sockaddr *addrs,
     int addrs_size, int op)
{
 int err;
 int addrcnt = 0;
 int walk_size = 0;
 struct sockaddr *sa_addr;
 void *addr_buf = addrs;
 struct sctp_af *af;

 pr_debug("%s: sk:%p addrs:%p addrs_size:%d opt:%d\n",
   __func__, sk, addr_buf, addrs_size, op);

 if (unlikely(addrs_size <= 0))
  return -EINVAL;

 /* Walk through the addrs buffer and count the number of addresses. */
 while (walk_size < addrs_size) {
  if (walk_size + sizeof(sa_family_t) > addrs_size)
   return -EINVAL;

  sa_addr = addr_buf;
  af = sctp_get_af_specific(sa_addr->sa_family);

  /* If the address family is not supported or if this address
 * causes the address buffer to overflow return EINVAL.
 */

  if (!af || (walk_size + af->sockaddr_len) > addrs_size)
   return -EINVAL;
  addrcnt++;
  addr_buf += af->sockaddr_len;
  walk_size += af->sockaddr_len;
 }

 /* Do the work. */
 switch (op) {
 case SCTP_BINDX_ADD_ADDR:
  /* Allow security module to validate bindx addresses. */
  err = security_sctp_bind_connect(sk, SCTP_SOCKOPT_BINDX_ADD,
       addrs, addrs_size);
  if (err)
   return err;
  err = sctp_bindx_add(sk, addrs, addrcnt);
  if (err)
   return err;
  return sctp_send_asconf_add_ip(sk, addrs, addrcnt);
 case SCTP_BINDX_REM_ADDR:
  err = sctp_bindx_rem(sk, addrs, addrcnt);
  if (err)
   return err;
  return sctp_send_asconf_del_ip(sk, addrs, addrcnt);

 default:
  return -EINVAL;
 }
}

static int sctp_bind_add(struct sock *sk, struct sockaddr *addrs,
  int addrlen)
{
 int err;

 lock_sock(sk);
 err = sctp_setsockopt_bindx(sk, addrs, addrlen, SCTP_BINDX_ADD_ADDR);
 release_sock(sk);
 return err;
}

static int sctp_connect_new_asoc(struct sctp_endpoint *ep,
     const union sctp_addr *daddr,
     const struct sctp_initmsg *init,
     struct sctp_transport **tp)
{
 struct sctp_association *asoc;
 struct sock *sk = ep->base.sk;
 struct net *net = sock_net(sk);
 enum sctp_scope scope;
 int err;

 if (sctp_endpoint_is_peeled_off(ep, daddr))
  return -EADDRNOTAVAIL;

 if (!ep->base.bind_addr.port) {
  if (sctp_autobind(sk))
   return -EAGAIN;
 } else {
  if (inet_port_requires_bind_service(net, ep->base.bind_addr.port) &&
      !ns_capable(net->user_ns, CAP_NET_BIND_SERVICE))
   return -EACCES;
 }

 scope = sctp_scope(daddr);
 asoc = sctp_association_new(ep, sk, scope, GFP_KERNEL);
 if (!asoc)
  return -ENOMEM;

 err = sctp_assoc_set_bind_addr_from_ep(asoc, scope, GFP_KERNEL);
 if (err < 0)
  goto free;

 *tp = sctp_assoc_add_peer(asoc, daddr, GFP_KERNEL, SCTP_UNKNOWN);
 if (!*tp) {
  err = -ENOMEM;
  goto free;
 }

 if (!init)
  return 0;

 if (init->sinit_num_ostreams) {
  __u16 outcnt = init->sinit_num_ostreams;

  asoc->c.sinit_num_ostreams = outcnt;
  /* outcnt has been changed, need to re-init stream */
  err = sctp_stream_init(&asoc->stream, outcnt, 0, GFP_KERNEL);
  if (err)
   goto free;
 }

 if (init->sinit_max_instreams)
  asoc->c.sinit_max_instreams = init->sinit_max_instreams;

 if (init->sinit_max_attempts)
  asoc->max_init_attempts = init->sinit_max_attempts;

 if (init->sinit_max_init_timeo)
  asoc->max_init_timeo =
   msecs_to_jiffies(init->sinit_max_init_timeo);

 return 0;
free:
 sctp_association_free(asoc);
 return err;
}

static int sctp_connect_add_peer(struct sctp_association *asoc,
     union sctp_addr *daddr, int addr_len)
{
 struct sctp_endpoint *ep = asoc->ep;
 struct sctp_association *old;
 struct sctp_transport *t;
 int err;

 err = sctp_verify_addr(ep->base.sk, daddr, addr_len);
 if (err)
  return err;

 old = sctp_endpoint_lookup_assoc(ep, daddr, &t);
 if (old && old != asoc)
  return old->state >= SCTP_STATE_ESTABLISHED ? -EISCONN
           : -EALREADY;

 if (sctp_endpoint_is_peeled_off(ep, daddr))
  return -EADDRNOTAVAIL;

 t = sctp_assoc_add_peer(asoc, daddr, GFP_KERNEL, SCTP_UNKNOWN);
 if (!t)
  return -ENOMEM;

 return 0;
}

/* __sctp_connect(struct sock* sk, struct sockaddr *kaddrs, int addrs_size)
 *
 * Common routine for handling connect() and sctp_connectx().
 * Connect will come in with just a single address.
 */

static int __sctp_connect(struct sock *sk, struct sockaddr *kaddrs,
     int addrs_size, int flags, sctp_assoc_t *assoc_id)
{
 struct sctp_sock *sp = sctp_sk(sk);
 struct sctp_endpoint *ep = sp->ep;
 struct sctp_transport *transport;
 struct sctp_association *asoc;
 void *addr_buf = kaddrs;
 union sctp_addr *daddr;
 struct sctp_af *af;
 int walk_size, err;
 long timeo;

 if (sctp_sstate(sk, ESTABLISHED) || sctp_sstate(sk, CLOSING) ||
     (sctp_style(sk, TCP) && sctp_sstate(sk, LISTENING)))
  return -EISCONN;

 daddr = addr_buf;
 af = sctp_get_af_specific(daddr->sa.sa_family);
 if (!af || af->sockaddr_len > addrs_size)
  return -EINVAL;

 err = sctp_verify_addr(sk, daddr, af->sockaddr_len);
 if (err)
  return err;

 asoc = sctp_endpoint_lookup_assoc(ep, daddr, &transport);
 if (asoc)
  return asoc->state >= SCTP_STATE_ESTABLISHED ? -EISCONN
            : -EALREADY;

 err = sctp_connect_new_asoc(ep, daddr, NULL, &transport);
 if (err)
  return err;
 asoc = transport->asoc;

 addr_buf += af->sockaddr_len;
 walk_size = af->sockaddr_len;
 while (walk_size < addrs_size) {
  err = -EINVAL;
  if (walk_size + sizeof(sa_family_t) > addrs_size)
   goto out_free;

  daddr = addr_buf;
  af = sctp_get_af_specific(daddr->sa.sa_family);
  if (!af || af->sockaddr_len + walk_size > addrs_size)
   goto out_free;

  if (asoc->peer.port != ntohs(daddr->v4.sin_port))
   goto out_free;

  err = sctp_connect_add_peer(asoc, daddr, af->sockaddr_len);
  if (err)
   goto out_free;

  addr_buf  += af->sockaddr_len;
  walk_size += af->sockaddr_len;
 }

 /* In case the user of sctp_connectx() wants an association
 * id back, assign one now.
 */

 if (assoc_id) {
  err = sctp_assoc_set_id(asoc, GFP_KERNEL);
  if (err < 0)
   goto out_free;
 }

 err = sctp_primitive_ASSOCIATE(sock_net(sk), asoc, NULL);
 if (err < 0)
  goto out_free;

 /* Initialize sk's dport and daddr for getpeername() */
 inet_sk(sk)->inet_dport = htons(asoc->peer.port);
 sp->pf->to_sk_daddr(daddr, sk);
 sk->sk_err = 0;

 if (assoc_id)
  *assoc_id = asoc->assoc_id;

 timeo = sock_sndtimeo(sk, flags & O_NONBLOCK);
 return sctp_wait_for_connect(asoc, &timeo);

out_free:
 pr_debug("%s: took out_free path with asoc:%p kaddrs:%p err:%d\n",
   __func__, asoc, kaddrs, err);
 sctp_association_free(asoc);
 return err;
}

/* Helper for tunneling sctp_connectx() requests through sctp_setsockopt()
 *
 * API 8.9
 * int sctp_connectx(int sd, struct sockaddr *addrs, int addrcnt,
 *  sctp_assoc_t *asoc);
 *
 * If sd is an IPv4 socket, the addresses passed must be IPv4 addresses.
 * If the sd is an IPv6 socket, the addresses passed can either be IPv4
 * or IPv6 addresses.
 *
 * A single address may be specified as INADDR_ANY or IN6ADDR_ANY, see
 * Section 3.1.2 for this usage.
 *
 * addrs is a pointer to an array of one or more socket addresses. Each
 * address is contained in its appropriate structure (i.e. struct
 * sockaddr_in or struct sockaddr_in6) the family of the address type
 * must be used to distengish the address length (note that this
 * representation is termed a "packed array" of addresses). The caller
 * specifies the number of addresses in the array with addrcnt.
 *
 * On success, sctp_connectx() returns 0. It also sets the assoc_id to
 * the association id of the new association.  On failure, sctp_connectx()
 * returns -1, and sets errno to the appropriate error code.  The assoc_id
 * is not touched by the kernel.
 *
 * For SCTP, the port given in each socket address must be the same, or
 * sctp_connectx() will fail, setting errno to EINVAL.
 *
 * An application can use sctp_connectx to initiate an association with
 * an endpoint that is multi-homed.  Much like sctp_bindx() this call
 * allows a caller to specify multiple addresses at which a peer can be
 * reached.  The way the SCTP stack uses the list of addresses to set up
 * the association is implementation dependent.  This function only
 * specifies that the stack will try to make use of all the addresses in
 * the list when needed.
 *
 * Note that the list of addresses passed in is only used for setting up
 * the association.  It does not necessarily equal the set of addresses
 * the peer uses for the resulting association.  If the caller wants to
 * find out the set of peer addresses, it must use sctp_getpaddrs() to
 * retrieve them after the association has been set up.
 *
 * Basically do nothing but copying the addresses from user to kernel
 * land and invoking either sctp_connectx(). This is used for tunneling
 * the sctp_connectx() request through sctp_setsockopt() from userspace.
 *
 * On exit there is no need to do sockfd_put(), sys_setsockopt() does
 * it.
 *
 * sk        The sk of the socket
 * addrs     The pointer to the addresses
 * addrssize Size of the addrs buffer
 *
 * Returns >=0 if ok, <0 errno code on error.
 */

static int __sctp_setsockopt_connectx(struct sock *sk, struct sockaddr *kaddrs,
          int addrs_size, sctp_assoc_t *assoc_id)
{
 int err = 0, flags = 0;

 pr_debug("%s: sk:%p addrs:%p addrs_size:%d\n",
   __func__, sk, kaddrs, addrs_size);

 /* make sure the 1st addr's sa_family is accessible later */
 if (unlikely(addrs_size < sizeof(sa_family_t)))
  return -EINVAL;

 /* Allow security module to validate connectx addresses. */
 err = security_sctp_bind_connect(sk, SCTP_SOCKOPT_CONNECTX,
      (struct sockaddr *)kaddrs,
       addrs_size);
 if (err)
  return err;

 /* in-kernel sockets don't generally have a file allocated to them
 * if all they do is call sock_create_kern().
 */

 if (sk->sk_socket->file)
  flags = sk->sk_socket->file->f_flags;

 return __sctp_connect(sk, kaddrs, addrs_size, flags, assoc_id);
}

/*
 * This is an older interface.  It's kept for backward compatibility
 * to the option that doesn't provide association id.
 */

static int sctp_setsockopt_connectx_old(struct sock *sk,
     struct sockaddr *kaddrs,
     int addrs_size)
{
 return __sctp_setsockopt_connectx(sk, kaddrs, addrs_size, NULL);
}

/*
 * New interface for the API.  The since the API is done with a socket
 * option, to make it simple we feed back the association id is as a return
 * indication to the call.  Error is always negative and association id is
 * always positive.
 */

static int sctp_setsockopt_connectx(struct sock *sk,
        struct sockaddr *kaddrs,
        int addrs_size)
{
 sctp_assoc_t assoc_id = 0;
 int err = 0;

 err = __sctp_setsockopt_connectx(sk, kaddrs, addrs_size, &assoc_id);

 if (err)
  return err;
 else
  return assoc_id;
}

/*
 * New (hopefully final) interface for the API.
 * We use the sctp_getaddrs_old structure so that use-space library
 * can avoid any unnecessary allocations. The only different part
 * is that we store the actual length of the address buffer into the
 * addrs_num structure member. That way we can re-use the existing
 * code.
 */

#ifdef CONFIG_COMPAT
struct compat_sctp_getaddrs_old {
 sctp_assoc_t assoc_id;
 s32  addr_num;
 compat_uptr_t addrs;  /* struct sockaddr * */
};
#endif

static int sctp_getsockopt_connectx3(struct sock *sk, int len,
         char __user *optval,
         int __user *optlen)
{
 struct sctp_getaddrs_old param;
 sctp_assoc_t assoc_id = 0;
 struct sockaddr *kaddrs;
 int err = 0;

#ifdef CONFIG_COMPAT
 if (in_compat_syscall()) {
  struct compat_sctp_getaddrs_old param32;

  if (len < sizeof(param32))
   return -EINVAL;
  if (copy_from_user(¶m32, optval, sizeof(param32)))
   return -EFAULT;

  param.assoc_id = param32.assoc_id;
  param.addr_num = param32.addr_num;
  param.addrs = compat_ptr(param32.addrs);
 } else
#endif
 {
  if (len < sizeof(param))
   return -EINVAL;
  if (copy_from_user(¶m, optval, sizeof(param)))
   return -EFAULT;
 }

 kaddrs = memdup_user(param.addrs, param.addr_num);
 if (IS_ERR(kaddrs))
  return PTR_ERR(kaddrs);

 err = __sctp_setsockopt_connectx(sk, kaddrs, param.addr_num, &assoc_id);
 kfree(kaddrs);
 if (err == 0 || err == -EINPROGRESS) {
  if (copy_to_user(optval, &assoc_id, sizeof(assoc_id)))
   return -EFAULT;
  if (put_user(sizeof(assoc_id), optlen))
   return -EFAULT;
 }

 return err;
}

/* API 3.1.4 close() - UDP Style Syntax
 * Applications use close() to perform graceful shutdown (as described in
 * Section 10.1 of [SCTP]) on ALL the associations currently represented
 * by a UDP-style socket.
 *
 * The syntax is
 *
 *   ret = close(int sd);
 *
 *   sd      - the socket descriptor of the associations to be closed.
 *
 * To gracefully shutdown a specific association represented by the
 * UDP-style socket, an application should use the sendmsg() call,
 * passing no user data, but including the appropriate flag in the
 * ancillary data (see Section xxxx).
 *
 * If sd in the close() call is a branched-off socket representing only
 * one association, the shutdown is performed on that association only.
 *
 * 4.1.6 close() - TCP Style Syntax
 *
 * Applications use close() to gracefully close down an association.
 *
 * The syntax is:
 *
 *    int close(int sd);
 *
 *      sd      - the socket descriptor of the association to be closed.
 *
 * After an application calls close() on a socket descriptor, no further
 * socket operations will succeed on that descriptor.
 *
 * API 7.1.4 SO_LINGER
 *
 * An application using the TCP-style socket can use this option to
 * perform the SCTP ABORT primitive.  The linger option structure is:
 *
 *  struct  linger {
 *     int     l_onoff;                // option on/off
 *     int     l_linger;               // linger time
 * };
 *
 * To enable the option, set l_onoff to 1.  If the l_linger value is set
 * to 0, calling close() is the same as the ABORT primitive.  If the
 * value is set to a negative value, the setsockopt() call will return
 * an error.  If the value is set to a positive value linger_time, the
 * close() can be blocked for at most linger_time ms.  If the graceful
 * shutdown phase does not finish during this period, close() will
 * return but the graceful shutdown phase continues in the system.
 */

static void sctp_close(struct sock *sk, long timeout)
{
 struct net *net = sock_net(sk);
 struct sctp_endpoint *ep;
 struct sctp_association *asoc;
 struct list_head *pos, *temp;
 unsigned int data_was_unread;

 pr_debug("%s: sk:%p, timeout:%ld\n", __func__, sk, timeout);

 lock_sock_nested(sk, SINGLE_DEPTH_NESTING);
 sk->sk_shutdown = SHUTDOWN_MASK;
 inet_sk_set_state(sk, SCTP_SS_CLOSING);

 ep = sctp_sk(sk)->ep;

 /* Clean up any skbs sitting on the receive queue.  */
 data_was_unread = sctp_queue_purge_ulpevents(&sk->sk_receive_queue);
 data_was_unread += sctp_queue_purge_ulpevents(&sctp_sk(sk)->pd_lobby);

 /* Walk all associations on an endpoint.  */
 list_for_each_safe(pos, temp, &ep->asocs) {
  asoc = list_entry(pos, struct sctp_association, asocs);

  if (sctp_style(sk, TCP)) {
   /* A closed association can still be in the list if
 * it belongs to a TCP-style listening socket that is
 * not yet accepted. If so, free it. If not, send an
 * ABORT or SHUTDOWN based on the linger options.
 */

   if (sctp_state(asoc, CLOSED)) {
    sctp_association_free(asoc);
    continue;
   }
  }

  if (data_was_unread || !skb_queue_empty(&asoc->ulpq.lobby) ||
      !skb_queue_empty(&asoc->ulpq.reasm) ||
      !skb_queue_empty(&asoc->ulpq.reasm_uo) ||
      (sock_flag(sk, SOCK_LINGER) && !sk->sk_lingertime)) {
   struct sctp_chunk *chunk;

   chunk = sctp_make_abort_user(asoc, NULL, 0);
   sctp_primitive_ABORT(net, asoc, chunk);
  } else
   sctp_primitive_SHUTDOWN(net, asoc, NULL);
 }

 /* On a TCP-style socket, block for at most linger_time if set. */
 if (sctp_style(sk, TCP) && timeout)
  sctp_wait_for_close(sk, timeout);

 /* This will run the backlog queue.  */
 release_sock(sk);

 /* Supposedly, no process has access to the socket, but
 * the net layers still may.
 * Also, sctp_destroy_sock() needs to be called with addr_wq_lock
 * held and that should be grabbed before socket lock.
 */

 spin_lock_bh(&net->sctp.addr_wq_lock);
 bh_lock_sock_nested(sk);

 /* Hold the sock, since sk_common_release() will put sock_put()
 * and we have just a little more cleanup.
 */

 sock_hold(sk);
 sk_common_release(sk);

 bh_unlock_sock(sk);
 spin_unlock_bh(&net->sctp.addr_wq_lock);

 sock_put(sk);

 SCTP_DBG_OBJCNT_DEC(sock);
}

/* Handle EPIPE error. */
static int sctp_error(struct sock *sk, int flags, int err)
{
 if (err == -EPIPE)
  err = sock_error(sk) ? : -EPIPE;
 if (err == -EPIPE && !(flags & MSG_NOSIGNAL))
  send_sig(SIGPIPE, current, 0);
 return err;
}

/* API 3.1.3 sendmsg() - UDP Style Syntax
 *
 * An application uses sendmsg() and recvmsg() calls to transmit data to
 * and receive data from its peer.
 *
 *  ssize_t sendmsg(int socket, const struct msghdr *message,
 *                  int flags);
 *
 *  socket  - the socket descriptor of the endpoint.
 *  message - pointer to the msghdr structure which contains a single
 *            user message and possibly some ancillary data.
 *
 *            See Section 5 for complete description of the data
 *            structures.
 *
 *  flags   - flags sent or received with the user message, see Section
 *            5 for complete description of the flags.
 *
 * Note:  This function could use a rewrite especially when explicit
 * connect support comes in.
 */

/* BUG:  We do not implement the equivalent of sk_stream_wait_memory(). */

static int sctp_msghdr_parse(const struct msghdr *msg,
        struct sctp_cmsgs *cmsgs);

static int sctp_sendmsg_parse(struct sock *sk, struct sctp_cmsgs *cmsgs,
         struct sctp_sndrcvinfo *srinfo,
         const struct msghdr *msg, size_t msg_len)
{
 __u16 sflags;
 int err;

 if (sctp_sstate(sk, LISTENING) && sctp_style(sk, TCP))
  return -EPIPE;

 if (msg_len > sk->sk_sndbuf)
  return -EMSGSIZE;

 memset(cmsgs, 0, sizeof(*cmsgs));
 err = sctp_msghdr_parse(msg, cmsgs);
 if (err) {
  pr_debug("%s: msghdr parse err:%x\n", __func__, err);
  return err;
 }

 memset(srinfo, 0, sizeof(*srinfo));
 if (cmsgs->srinfo) {
  srinfo->sinfo_stream = cmsgs->srinfo->sinfo_stream;
  srinfo->sinfo_flags = cmsgs->srinfo->sinfo_flags;
  srinfo->sinfo_ppid = cmsgs->srinfo->sinfo_ppid;
  srinfo->sinfo_context = cmsgs->srinfo->sinfo_context;
  srinfo->sinfo_assoc_id = cmsgs->srinfo->sinfo_assoc_id;
  srinfo->sinfo_timetolive = cmsgs->srinfo->sinfo_timetolive;
 }

 if (cmsgs->sinfo) {
  srinfo->sinfo_stream = cmsgs->sinfo->snd_sid;
  srinfo->sinfo_flags = cmsgs->sinfo->snd_flags;
  srinfo->sinfo_ppid = cmsgs->sinfo->snd_ppid;
  srinfo->sinfo_context = cmsgs->sinfo->snd_context;
  srinfo->sinfo_assoc_id = cmsgs->sinfo->snd_assoc_id;
 }

 if (cmsgs->prinfo) {
  srinfo->sinfo_timetolive = cmsgs->prinfo->pr_value;
  SCTP_PR_SET_POLICY(srinfo->sinfo_flags,
       cmsgs->prinfo->pr_policy);
 }

 sflags = srinfo->sinfo_flags;
 if (!sflags && msg_len)
  return 0;

 if (sctp_style(sk, TCP) && (sflags & (SCTP_EOF | SCTP_ABORT)))
  return -EINVAL;

 if (((sflags & SCTP_EOF) && msg_len > 0) ||
     (!(sflags & (SCTP_EOF | SCTP_ABORT)) && msg_len == 0))
  return -EINVAL;

 if ((sflags & SCTP_ADDR_OVER) && !msg->msg_name)
  return -EINVAL;

 return 0;
}

static int sctp_sendmsg_new_asoc(struct sock *sk, __u16 sflags,
     struct sctp_cmsgs *cmsgs,
     union sctp_addr *daddr,
     struct sctp_transport **tp)
{
 struct sctp_endpoint *ep = sctp_sk(sk)->ep;
 struct sctp_association *asoc;
 struct cmsghdr *cmsg;
 __be32 flowinfo = 0;
 struct sctp_af *af;
 int err;

 *tp = NULL;

 if (sflags & (SCTP_EOF | SCTP_ABORT))
  return -EINVAL;

 if (sctp_style(sk, TCP) && (sctp_sstate(sk, ESTABLISHED) ||
        sctp_sstate(sk, CLOSING)))
  return -EADDRNOTAVAIL;

 /* Label connection socket for first association 1-to-many
 * style for client sequence socket()->sendmsg(). This
 * needs to be done before sctp_assoc_add_peer() as that will
 * set up the initial packet that needs to account for any
 * security ip options (CIPSO/CALIPSO) added to the packet.
 */

 af = sctp_get_af_specific(daddr->sa.sa_family);
 if (!af)
  return -EINVAL;
 err = security_sctp_bind_connect(sk, SCTP_SENDMSG_CONNECT,
      (struct sockaddr *)daddr,
      af->sockaddr_len);
 if (err < 0)
  return err;

 err = sctp_connect_new_asoc(ep, daddr, cmsgs->init, tp);
 if (err)
  return err;
 asoc = (*tp)->asoc;

 if (!cmsgs->addrs_msg)
  return 0;

 if (daddr->sa.sa_family == AF_INET6)
  flowinfo = daddr->v6.sin6_flowinfo;

 /* sendv addr list parse */
 for_each_cmsghdr(cmsg, cmsgs->addrs_msg) {
  union sctp_addr _daddr;
  int dlen;

  if (cmsg->cmsg_level != IPPROTO_SCTP ||
      (cmsg->cmsg_type != SCTP_DSTADDRV4 &&
       cmsg->cmsg_type != SCTP_DSTADDRV6))
   continue;

  daddr = &_daddr;
  memset(daddr, 0, sizeof(*daddr));
  dlen = cmsg->cmsg_len - sizeof(struct cmsghdr);
  if (cmsg->cmsg_type == SCTP_DSTADDRV4) {
   if (dlen < sizeof(struct in_addr)) {
    err = -EINVAL;
    goto free;
   }

   dlen = sizeof(struct in_addr);
   daddr->v4.sin_family = AF_INET;
   daddr->v4.sin_port = htons(asoc->peer.port);
   memcpy(&daddr->v4.sin_addr, CMSG_DATA(cmsg), dlen);
  } else {
   if (dlen < sizeof(struct in6_addr)) {
    err = -EINVAL;
    goto free;
   }

   dlen = sizeof(struct in6_addr);
   daddr->v6.sin6_flowinfo = flowinfo;
   daddr->v6.sin6_family = AF_INET6;
   daddr->v6.sin6_port = htons(asoc->peer.port);
   memcpy(&daddr->v6.sin6_addr, CMSG_DATA(cmsg), dlen);
  }

  err = sctp_connect_add_peer(asoc, daddr, sizeof(*daddr));
  if (err)
   goto free;
 }

 return 0;

free:
 sctp_association_free(asoc);
 return err;
}

static int sctp_sendmsg_check_sflags(struct sctp_association *asoc,
         __u16 sflags, struct msghdr *msg,
         size_t msg_len)
{
 struct sock *sk = asoc->base.sk;
 struct net *net = sock_net(sk);

 if (sctp_state(asoc, CLOSED) && sctp_style(sk, TCP))
  return -EPIPE;

 if ((sflags & SCTP_SENDALL) && sctp_style(sk, UDP) &&
     !sctp_state(asoc, ESTABLISHED))
  return 0;

 if (sflags & SCTP_EOF) {
  pr_debug("%s: shutting down association:%p\n", __func__, asoc);
  sctp_primitive_SHUTDOWN(net, asoc, NULL);

  return 0;
 }

 if (sflags & SCTP_ABORT) {
  struct sctp_chunk *chunk;

  chunk = sctp_make_abort_user(asoc, msg, msg_len);
  if (!chunk)
   return -ENOMEM;

  pr_debug("%s: aborting association:%p\n", __func__, asoc);
  sctp_primitive_ABORT(net, asoc, chunk);
  iov_iter_revert(&msg->msg_iter, msg_len);

  return 0;
 }

 return 1;
}

static int sctp_sendmsg_to_asoc(struct sctp_association *asoc,
    struct msghdr *msg, size_t msg_len,
    struct sctp_transport *transport,
    struct sctp_sndrcvinfo *sinfo)
{
 struct sock *sk = asoc->base.sk;
 struct sctp_sock *sp = sctp_sk(sk);
 struct net *net = sock_net(sk);
 struct sctp_datamsg *datamsg;
 bool wait_connect = false;
 struct sctp_chunk *chunk;
 long timeo;
 int err;

 if (sinfo->sinfo_stream >= asoc->stream.outcnt) {
  err = -EINVAL;
  goto err;
 }

 if (unlikely(!SCTP_SO(&asoc->stream, sinfo->sinfo_stream)->ext)) {
  err = sctp_stream_init_ext(&asoc->stream, sinfo->sinfo_stream);
  if (err)
   goto err;
 }

 if (sp->disable_fragments && msg_len > asoc->frag_point) {
  err = -EMSGSIZE;
  goto err;
 }

 if (asoc->pmtu_pending) {
  if (sp->param_flags & SPP_PMTUD_ENABLE)
   sctp_assoc_sync_pmtu(asoc);
  asoc->pmtu_pending = 0;
 }

 if (sctp_wspace(asoc) < (int)msg_len)
  sctp_prsctp_prune(asoc, sinfo, msg_len - sctp_wspace(asoc));

 if (sctp_wspace(asoc) <= 0 || !sk_wmem_schedule(sk, msg_len)) {
  timeo = sock_sndtimeo(sk, msg->msg_flags & MSG_DONTWAIT);
  err = sctp_wait_for_sndbuf(asoc, transport, &timeo, msg_len);
  if (err)
   goto err;
  if (unlikely(sinfo->sinfo_stream >= asoc->stream.outcnt)) {
   err = -EINVAL;
   goto err;
  }
 }

 if (sctp_state(asoc, CLOSED)) {
  err = sctp_primitive_ASSOCIATE(net, asoc, NULL);
  if (err)
   goto err;

  if (asoc->ep->intl_enable) {
   timeo = sock_sndtimeo(sk, 0);
   err = sctp_wait_for_connect(asoc, &timeo);
   if (err) {
    err = -ESRCH;
    goto err;
   }
  } else {
   wait_connect = true;
  }

  pr_debug("%s: we associated primitively\n", __func__);
 }

 datamsg = sctp_datamsg_from_user(asoc, sinfo, &msg->msg_iter);
 if (IS_ERR(datamsg)) {
  err = PTR_ERR(datamsg);
  goto err;
 }

 asoc->force_delay = !!(msg->msg_flags & MSG_MORE);

 list_for_each_entry(chunk, &datamsg->chunks, frag_list) {
  sctp_chunk_hold(chunk);
  sctp_set_owner_w(chunk);
  chunk->transport = transport;
 }

 err = sctp_primitive_SEND(net, asoc, datamsg);
 if (err) {
  sctp_datamsg_free(datamsg);
  goto err;
 }

 pr_debug("%s: we sent primitively\n", __func__);

 sctp_datamsg_put(datamsg);

 if (unlikely(wait_connect)) {
  timeo = sock_sndtimeo(sk, msg->msg_flags & MSG_DONTWAIT);
  sctp_wait_for_connect(asoc, &timeo);
 }

 err = msg_len;

err:
 return err;
}

static union sctp_addr *sctp_sendmsg_get_daddr(struct sock *sk,
            const struct msghdr *msg,
            struct sctp_cmsgs *cmsgs)
{
 union sctp_addr *daddr = NULL;
 int err;

 if (!sctp_style(sk, UDP_HIGH_BANDWIDTH) && msg->msg_name) {
  int len = msg->msg_namelen;

  if (len > sizeof(*daddr))
   len = sizeof(*daddr);

  daddr = (union sctp_addr *)msg->msg_name;

  err = sctp_verify_addr(sk, daddr, len);
  if (err)
   return ERR_PTR(err);
 }

 return daddr;
}

static void sctp_sendmsg_update_sinfo(struct sctp_association *asoc,
          struct sctp_sndrcvinfo *sinfo,
          struct sctp_cmsgs *cmsgs)
{
 if (!cmsgs->srinfo && !cmsgs->sinfo) {
  sinfo->sinfo_stream = asoc->default_stream;
  sinfo->sinfo_ppid = asoc->default_ppid;
  sinfo->sinfo_context = asoc->default_context;
  sinfo->sinfo_assoc_id = sctp_assoc2id(asoc);

  if (!cmsgs->prinfo)
   sinfo->sinfo_flags = asoc->default_flags;
 }

 if (!cmsgs->srinfo && !cmsgs->prinfo)
  sinfo->sinfo_timetolive = asoc->default_timetolive;

 if (cmsgs->authinfo) {
  /* Reuse sinfo_tsn to indicate that authinfo was set and
 * sinfo_ssn to save the keyid on tx path.
 */

  sinfo->sinfo_tsn = 1;
  sinfo->sinfo_ssn = cmsgs->authinfo->auth_keynumber;
 }
}

static int sctp_sendmsg(struct sock *sk, struct msghdr *msg, size_t msg_len)
{
 struct sctp_endpoint *ep = sctp_sk(sk)->ep;
 struct sctp_transport *transport = NULL;
 struct sctp_sndrcvinfo _sinfo, *sinfo;
 struct sctp_association *asoc, *tmp;
 struct sctp_cmsgs cmsgs;
 union sctp_addr *daddr;
 bool new = false;
 __u16 sflags;
 int err;

 /* Parse and get snd_info */
 err = sctp_sendmsg_parse(sk, &cmsgs, &_sinfo, msg, msg_len);
 if (err)
  goto out;

 sinfo  = &_sinfo;
 sflags = sinfo->sinfo_flags;

 /* Get daddr from msg */
 daddr = sctp_sendmsg_get_daddr(sk, msg, &cmsgs);
 if (IS_ERR(daddr)) {
  err = PTR_ERR(daddr);
  goto out;
 }

 lock_sock(sk);

 /* SCTP_SENDALL process */
 if ((sflags & SCTP_SENDALL) && sctp_style(sk, UDP)) {
  list_for_each_entry_safe(asoc, tmp, &ep->asocs, asocs) {
   err = sctp_sendmsg_check_sflags(asoc, sflags, msg,
       msg_len);
   if (err == 0)
    continue;
   if (err < 0)
    goto out_unlock;

   sctp_sendmsg_update_sinfo(asoc, sinfo, &cmsgs);

   err = sctp_sendmsg_to_asoc(asoc, msg, msg_len,
         NULL, sinfo);
   if (err < 0)
    goto out_unlock;

   iov_iter_revert(&msg->msg_iter, err);
  }

  goto out_unlock;
 }

 /* Get and check or create asoc */
 if (daddr) {
  asoc = sctp_endpoint_lookup_assoc(ep, daddr, &transport);
  if (asoc) {
   err = sctp_sendmsg_check_sflags(asoc, sflags, msg,
       msg_len);
   if (err <= 0)
    goto out_unlock;
  } else {
   err = sctp_sendmsg_new_asoc(sk, sflags, &cmsgs, daddr,
          &transport);
   if (err)
    goto out_unlock;

   asoc = transport->asoc;
   new = true;
  }

  if (!sctp_style(sk, TCP) && !(sflags & SCTP_ADDR_OVER))
   transport = NULL;
 } else {
  asoc = sctp_id2assoc(sk, sinfo->sinfo_assoc_id);
  if (!asoc) {
   err = -EPIPE;
   goto out_unlock;
  }

  err = sctp_sendmsg_check_sflags(asoc, sflags, msg, msg_len);
  if (err <= 0)
   goto out_unlock;
 }

 /* Update snd_info with the asoc */
 sctp_sendmsg_update_sinfo(asoc, sinfo, &cmsgs);

 /* Send msg to the asoc */
 err = sctp_sendmsg_to_asoc(asoc, msg, msg_len, transport, sinfo);
 if (err < 0 && err != -ESRCH && new)
  sctp_association_free(asoc);

out_unlock:
 release_sock(sk);
out:
 return sctp_error(sk, msg->msg_flags, err);
}

/* This is an extended version of skb_pull() that removes the data from the
 * start of a skb even when data is spread across the list of skb's in the
 * frag_list. len specifies the total amount of data that needs to be removed.
 * when 'len' bytes could be removed from the skb, it returns 0.
 * If 'len' exceeds the total skb length,  it returns the no. of bytes that
 * could not be removed.
 */

static int sctp_skb_pull(struct sk_buff *skb, int len)
{
 struct sk_buff *list;
 int skb_len = skb_headlen(skb);
 int rlen;

 if (len <= skb_len) {
  __skb_pull(skb, len);
  return 0;
 }
 len -= skb_len;
 __skb_pull(skb, skb_len);

 skb_walk_frags(skb, list) {
  rlen = sctp_skb_pull(list, len);
  skb->len -= (len-rlen);
  skb->data_len -= (len-rlen);

  if (!rlen)
   return 0;

  len = rlen;
 }

 return len;
}

/* API 3.1.3  recvmsg() - UDP Style Syntax
 *
 *  ssize_t recvmsg(int socket, struct msghdr *message,
 *                    int flags);
 *
 *  socket  - the socket descriptor of the endpoint.
 *  message - pointer to the msghdr structure which contains a single
 *            user message and possibly some ancillary data.
 *
 *            See Section 5 for complete description of the data
 *            structures.
 *
 *  flags   - flags sent or received with the user message, see Section
 *            5 for complete description of the flags.
 */

static int sctp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
   int flags, int *addr_len)
{
 struct sctp_ulpevent *event = NULL;
 struct sctp_sock *sp = sctp_sk(sk);
 struct sk_buff *skb, *head_skb;
 int copied;
 int err = 0;
 int skb_len;

 pr_debug("%s: sk:%p, msghdr:%p, len:%zd, flags:0x%x, addr_len:%p)\n",
   __func__, sk, msg, len, flags, addr_len);

 if (unlikely(flags & MSG_ERRQUEUE))
  return inet_recv_error(sk, msg, len, addr_len);

 if (sk_can_busy_loop(sk) &&
     skb_queue_empty_lockless(&sk->sk_receive_queue))
  sk_busy_loop(sk, flags & MSG_DONTWAIT);

 lock_sock(sk);

 if (sctp_style(sk, TCP) && !sctp_sstate(sk, ESTABLISHED) &&
     !sctp_sstate(sk, CLOSING) && !sctp_sstate(sk, CLOSED)) {
  err = -ENOTCONN;
  goto out;
 }

 skb = sctp_skb_recv_datagram(sk, flags, &err);
 if (!skb)
  goto out;

 /* Get the total length of the skb including any skb's in the
 * frag_list.
 */

 skb_len = skb->len;

 copied = skb_len;
 if (copied > len)
  copied = len;

 err = skb_copy_datagram_msg(skb, 0, msg, copied);

 event = sctp_skb2event(skb);

 if (err)
  goto out_free;

 if (event->chunk && event->chunk->head_skb)
  head_skb = event->chunk->head_skb;
 else
  head_skb = skb;
 sock_recv_cmsgs(msg, sk, head_skb);
 if (sctp_ulpevent_is_notification(event)) {
  msg->msg_flags |= MSG_NOTIFICATION;
  sp->pf->event_msgname(event, msg->msg_name, addr_len);
 } else {
  sp->pf->skb_msgname(head_skb, msg->msg_name, addr_len);
 }

 /* Check if we allow SCTP_NXTINFO. */
 if (sp->recvnxtinfo)
  sctp_ulpevent_read_nxtinfo(event, msg, sk);
 /* Check if we allow SCTP_RCVINFO. */
 if (sp->recvrcvinfo)
  sctp_ulpevent_read_rcvinfo(event, msg);
 /* Check if we allow SCTP_SNDRCVINFO. */
 if (sctp_ulpevent_type_enabled(sp->subscribe, SCTP_DATA_IO_EVENT))
  sctp_ulpevent_read_sndrcvinfo(event, msg);

 err = copied;

 /* If skb's length exceeds the user's buffer, update the skb and
 * push it back to the receive_queue so that the next call to
 * recvmsg() will return the remaining data. Don't set MSG_EOR.
 */

 if (skb_len > copied) {
  msg->msg_flags &= ~MSG_EOR;
  if (flags & MSG_PEEK)
   goto out_free;
  sctp_skb_pull(skb, copied);
  skb_queue_head(&sk->sk_receive_queue, skb);

  /* When only partial message is copied to the user, increase
 * rwnd by that amount. If all the data in the skb is read,
 * rwnd is updated when the event is freed.
 */

  if (!sctp_ulpevent_is_notification(event))
   sctp_assoc_rwnd_increase(event->asoc, copied);
  goto out;
 } else if ((event->msg_flags & MSG_NOTIFICATION) ||
     (event->msg_flags & MSG_EOR))
  msg->msg_flags |= MSG_EOR;
 else
  msg->msg_flags &= ~MSG_EOR;

out_free:
 if (flags & MSG_PEEK) {
  /* Release the skb reference acquired after peeking the skb in
 * sctp_skb_recv_datagram().
 */

  kfree_skb(skb);
 } else {
  /* Free the event which includes releasing the reference to
 * the owner of the skb, freeing the skb and updating the
 * rwnd.
 */

  sctp_ulpevent_free(event);
 }
out:
 release_sock(sk);
 return err;
}

/* 7.1.12 Enable/Disable message fragmentation (SCTP_DISABLE_FRAGMENTS)
 *
 * This option is a on/off flag.  If enabled no SCTP message
 * fragmentation will be performed.  Instead if a message being sent
 * exceeds the current PMTU size, the message will NOT be sent and
 * instead a error will be indicated to the user.
 */

static int sctp_setsockopt_disable_fragments(struct sock *sk, int *val,
          unsigned int optlen)
{
 if (optlen < sizeof(int))
  return -EINVAL;
 sctp_sk(sk)->disable_fragments = (*val == 0) ? 0 : 1;
 return 0;
}

static int sctp_setsockopt_events(struct sock *sk, __u8 *sn_type,
      unsigned int optlen)
{
 struct sctp_sock *sp = sctp_sk(sk);
 struct sctp_association *asoc;
 int i;

 if (optlen > sizeof(struct sctp_event_subscribe))
  return -EINVAL;

 for (i = 0; i < optlen; i++)
  sctp_ulpevent_type_set(&sp->subscribe, SCTP_SN_TYPE_BASE + i,
           sn_type[i]);

 list_for_each_entry(asoc, &sp->ep->asocs, asocs)
  asoc->subscribe = sctp_sk(sk)->subscribe;

 /* At the time when a user app subscribes to SCTP_SENDER_DRY_EVENT,
 * if there is no data to be sent or retransmit, the stack will
 * immediately send up this notification.
 */

 if (sctp_ulpevent_type_enabled(sp->subscribe, SCTP_SENDER_DRY_EVENT)) {
  struct sctp_ulpevent *event;

  asoc = sctp_id2assoc(sk, 0);
  if (asoc && sctp_outq_is_empty(&asoc->outqueue)) {
   event = sctp_ulpevent_make_sender_dry_event(asoc,
     GFP_USER | __GFP_NOWARN);
   if (!event)
    return -ENOMEM;

   asoc->stream.si->enqueue_event(&asoc->ulpq, event);
  }
 }

 return 0;
}

/* 7.1.8 Automatic Close of associations (SCTP_AUTOCLOSE)
 *
 * This socket option is applicable to the UDP-style socket only.  When
 * set it will cause associations that are idle for more than the
 * specified number of seconds to automatically close.  An association
 * being idle is defined an association that has NOT sent or received
 * user data.  The special value of '0' indicates that no automatic
 * close of any associations should be performed.  The option expects an
 * integer defining the number of seconds of idle time before an
 * association is closed.
 */

static int sctp_setsockopt_autoclose(struct sock *sk, u32 *optval,
         unsigned int optlen)
{
 struct sctp_sock *sp = sctp_sk(sk);
 struct net *net = sock_net(sk);

 /* Applicable to UDP-style socket only */
 if (sctp_style(sk, TCP))
  return -EOPNOTSUPP;
 if (optlen != sizeof(int))
  return -EINVAL;

 sp->autoclose = *optval;
 if (sp->autoclose > net->sctp.max_autoclose)
  sp->autoclose = net->sctp.max_autoclose;

 return 0;
}

/* 7.1.13 Peer Address Parameters (SCTP_PEER_ADDR_PARAMS)
 *
 * Applications can enable or disable heartbeats for any peer address of
 * an association, modify an address's heartbeat interval, force a
 * heartbeat to be sent immediately, and adjust the address's maximum
 * number of retransmissions sent before an address is considered
 * unreachable.  The following structure is used to access and modify an
 * address's parameters:
 *
 *  struct sctp_paddrparams {
 *     sctp_assoc_t            spp_assoc_id;
 *     struct sockaddr_storage spp_address;
 *     uint32_t                spp_hbinterval;
 *     uint16_t                spp_pathmaxrxt;
 *     uint32_t                spp_pathmtu;
 *     uint32_t                spp_sackdelay;
 *     uint32_t                spp_flags;
 *     uint32_t                spp_ipv6_flowlabel;
--> --------------------

--> maximum size reached

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

Messung V0.5
C=96 H=88 G=91

¤ Dauer der Verarbeitung: 0.23 Sekunden  (vorverarbeitet)  ¤

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