162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/* SCTP kernel implementation
362306a36Sopenharmony_ci * (C) Copyright IBM Corp. 2001, 2004
462306a36Sopenharmony_ci * Copyright (c) 1999-2000 Cisco, Inc.
562306a36Sopenharmony_ci * Copyright (c) 1999-2001 Motorola, Inc.
662306a36Sopenharmony_ci * Copyright (c) 2001-2003 Intel Corp.
762306a36Sopenharmony_ci * Copyright (c) 2001-2002 Nokia, Inc.
862306a36Sopenharmony_ci * Copyright (c) 2001 La Monte H.P. Yarroll
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci * This file is part of the SCTP kernel implementation
1162306a36Sopenharmony_ci *
1262306a36Sopenharmony_ci * These functions interface with the sockets layer to implement the
1362306a36Sopenharmony_ci * SCTP Extensions for the Sockets API.
1462306a36Sopenharmony_ci *
1562306a36Sopenharmony_ci * Note that the descriptions from the specification are USER level
1662306a36Sopenharmony_ci * functions--this file is the functions which populate the struct proto
1762306a36Sopenharmony_ci * for SCTP which is the BOTTOM of the sockets interface.
1862306a36Sopenharmony_ci *
1962306a36Sopenharmony_ci * Please send any bug reports or fixes you make to the
2062306a36Sopenharmony_ci * email address(es):
2162306a36Sopenharmony_ci *    lksctp developers <linux-sctp@vger.kernel.org>
2262306a36Sopenharmony_ci *
2362306a36Sopenharmony_ci * Written or modified by:
2462306a36Sopenharmony_ci *    La Monte H.P. Yarroll <piggy@acm.org>
2562306a36Sopenharmony_ci *    Narasimha Budihal     <narsi@refcode.org>
2662306a36Sopenharmony_ci *    Karl Knutson          <karl@athena.chicago.il.us>
2762306a36Sopenharmony_ci *    Jon Grimm             <jgrimm@us.ibm.com>
2862306a36Sopenharmony_ci *    Xingang Guo           <xingang.guo@intel.com>
2962306a36Sopenharmony_ci *    Daisy Chang           <daisyc@us.ibm.com>
3062306a36Sopenharmony_ci *    Sridhar Samudrala     <samudrala@us.ibm.com>
3162306a36Sopenharmony_ci *    Inaky Perez-Gonzalez  <inaky.gonzalez@intel.com>
3262306a36Sopenharmony_ci *    Ardelle Fan	    <ardelle.fan@intel.com>
3362306a36Sopenharmony_ci *    Ryan Layer	    <rmlayer@us.ibm.com>
3462306a36Sopenharmony_ci *    Anup Pemmaiah         <pemmaiah@cc.usu.edu>
3562306a36Sopenharmony_ci *    Kevin Gao             <kevin.gao@intel.com>
3662306a36Sopenharmony_ci */
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci#include <crypto/hash.h>
4162306a36Sopenharmony_ci#include <linux/types.h>
4262306a36Sopenharmony_ci#include <linux/kernel.h>
4362306a36Sopenharmony_ci#include <linux/wait.h>
4462306a36Sopenharmony_ci#include <linux/time.h>
4562306a36Sopenharmony_ci#include <linux/sched/signal.h>
4662306a36Sopenharmony_ci#include <linux/ip.h>
4762306a36Sopenharmony_ci#include <linux/capability.h>
4862306a36Sopenharmony_ci#include <linux/fcntl.h>
4962306a36Sopenharmony_ci#include <linux/poll.h>
5062306a36Sopenharmony_ci#include <linux/init.h>
5162306a36Sopenharmony_ci#include <linux/slab.h>
5262306a36Sopenharmony_ci#include <linux/file.h>
5362306a36Sopenharmony_ci#include <linux/compat.h>
5462306a36Sopenharmony_ci#include <linux/rhashtable.h>
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci#include <net/ip.h>
5762306a36Sopenharmony_ci#include <net/icmp.h>
5862306a36Sopenharmony_ci#include <net/route.h>
5962306a36Sopenharmony_ci#include <net/ipv6.h>
6062306a36Sopenharmony_ci#include <net/inet_common.h>
6162306a36Sopenharmony_ci#include <net/busy_poll.h>
6262306a36Sopenharmony_ci#include <trace/events/sock.h>
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci#include <linux/socket.h> /* for sa_family_t */
6562306a36Sopenharmony_ci#include <linux/export.h>
6662306a36Sopenharmony_ci#include <net/sock.h>
6762306a36Sopenharmony_ci#include <net/sctp/sctp.h>
6862306a36Sopenharmony_ci#include <net/sctp/sm.h>
6962306a36Sopenharmony_ci#include <net/sctp/stream_sched.h>
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci/* Forward declarations for internal helper functions. */
7262306a36Sopenharmony_cistatic bool sctp_writeable(const struct sock *sk);
7362306a36Sopenharmony_cistatic void sctp_wfree(struct sk_buff *skb);
7462306a36Sopenharmony_cistatic int sctp_wait_for_sndbuf(struct sctp_association *asoc, long *timeo_p,
7562306a36Sopenharmony_ci				size_t msg_len);
7662306a36Sopenharmony_cistatic int sctp_wait_for_packet(struct sock *sk, int *err, long *timeo_p);
7762306a36Sopenharmony_cistatic int sctp_wait_for_connect(struct sctp_association *, long *timeo_p);
7862306a36Sopenharmony_cistatic int sctp_wait_for_accept(struct sock *sk, long timeo);
7962306a36Sopenharmony_cistatic void sctp_wait_for_close(struct sock *sk, long timeo);
8062306a36Sopenharmony_cistatic void sctp_destruct_sock(struct sock *sk);
8162306a36Sopenharmony_cistatic struct sctp_af *sctp_sockaddr_af(struct sctp_sock *opt,
8262306a36Sopenharmony_ci					union sctp_addr *addr, int len);
8362306a36Sopenharmony_cistatic int sctp_bindx_add(struct sock *, struct sockaddr *, int);
8462306a36Sopenharmony_cistatic int sctp_bindx_rem(struct sock *, struct sockaddr *, int);
8562306a36Sopenharmony_cistatic int sctp_send_asconf_add_ip(struct sock *, struct sockaddr *, int);
8662306a36Sopenharmony_cistatic int sctp_send_asconf_del_ip(struct sock *, struct sockaddr *, int);
8762306a36Sopenharmony_cistatic int sctp_send_asconf(struct sctp_association *asoc,
8862306a36Sopenharmony_ci			    struct sctp_chunk *chunk);
8962306a36Sopenharmony_cistatic int sctp_do_bind(struct sock *, union sctp_addr *, int);
9062306a36Sopenharmony_cistatic int sctp_autobind(struct sock *sk);
9162306a36Sopenharmony_cistatic int sctp_sock_migrate(struct sock *oldsk, struct sock *newsk,
9262306a36Sopenharmony_ci			     struct sctp_association *assoc,
9362306a36Sopenharmony_ci			     enum sctp_socket_type type);
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_cistatic unsigned long sctp_memory_pressure;
9662306a36Sopenharmony_cistatic atomic_long_t sctp_memory_allocated;
9762306a36Sopenharmony_cistatic DEFINE_PER_CPU(int, sctp_memory_per_cpu_fw_alloc);
9862306a36Sopenharmony_cistruct percpu_counter sctp_sockets_allocated;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_cistatic void sctp_enter_memory_pressure(struct sock *sk)
10162306a36Sopenharmony_ci{
10262306a36Sopenharmony_ci	WRITE_ONCE(sctp_memory_pressure, 1);
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci/* Get the sndbuf space available at the time on the association.  */
10762306a36Sopenharmony_cistatic inline int sctp_wspace(struct sctp_association *asoc)
10862306a36Sopenharmony_ci{
10962306a36Sopenharmony_ci	struct sock *sk = asoc->base.sk;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	return asoc->ep->sndbuf_policy ? sk->sk_sndbuf - asoc->sndbuf_used
11262306a36Sopenharmony_ci				       : sk_stream_wspace(sk);
11362306a36Sopenharmony_ci}
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci/* Increment the used sndbuf space count of the corresponding association by
11662306a36Sopenharmony_ci * the size of the outgoing data chunk.
11762306a36Sopenharmony_ci * Also, set the skb destructor for sndbuf accounting later.
11862306a36Sopenharmony_ci *
11962306a36Sopenharmony_ci * Since it is always 1-1 between chunk and skb, and also a new skb is always
12062306a36Sopenharmony_ci * allocated for chunk bundling in sctp_packet_transmit(), we can use the
12162306a36Sopenharmony_ci * destructor in the data chunk skb for the purpose of the sndbuf space
12262306a36Sopenharmony_ci * tracking.
12362306a36Sopenharmony_ci */
12462306a36Sopenharmony_cistatic inline void sctp_set_owner_w(struct sctp_chunk *chunk)
12562306a36Sopenharmony_ci{
12662306a36Sopenharmony_ci	struct sctp_association *asoc = chunk->asoc;
12762306a36Sopenharmony_ci	struct sock *sk = asoc->base.sk;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	/* The sndbuf space is tracked per association.  */
13062306a36Sopenharmony_ci	sctp_association_hold(asoc);
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	if (chunk->shkey)
13362306a36Sopenharmony_ci		sctp_auth_shkey_hold(chunk->shkey);
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	skb_set_owner_w(chunk->skb, sk);
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	chunk->skb->destructor = sctp_wfree;
13862306a36Sopenharmony_ci	/* Save the chunk pointer in skb for sctp_wfree to use later.  */
13962306a36Sopenharmony_ci	skb_shinfo(chunk->skb)->destructor_arg = chunk;
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	refcount_add(sizeof(struct sctp_chunk), &sk->sk_wmem_alloc);
14262306a36Sopenharmony_ci	asoc->sndbuf_used += chunk->skb->truesize + sizeof(struct sctp_chunk);
14362306a36Sopenharmony_ci	sk_wmem_queued_add(sk, chunk->skb->truesize + sizeof(struct sctp_chunk));
14462306a36Sopenharmony_ci	sk_mem_charge(sk, chunk->skb->truesize);
14562306a36Sopenharmony_ci}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_cistatic void sctp_clear_owner_w(struct sctp_chunk *chunk)
14862306a36Sopenharmony_ci{
14962306a36Sopenharmony_ci	skb_orphan(chunk->skb);
15062306a36Sopenharmony_ci}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci#define traverse_and_process()	\
15362306a36Sopenharmony_cido {				\
15462306a36Sopenharmony_ci	msg = chunk->msg;	\
15562306a36Sopenharmony_ci	if (msg == prev_msg)	\
15662306a36Sopenharmony_ci		continue;	\
15762306a36Sopenharmony_ci	list_for_each_entry(c, &msg->chunks, frag_list) {	\
15862306a36Sopenharmony_ci		if ((clear && asoc->base.sk == c->skb->sk) ||	\
15962306a36Sopenharmony_ci		    (!clear && asoc->base.sk != c->skb->sk))	\
16062306a36Sopenharmony_ci			cb(c);	\
16162306a36Sopenharmony_ci	}			\
16262306a36Sopenharmony_ci	prev_msg = msg;		\
16362306a36Sopenharmony_ci} while (0)
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_cistatic void sctp_for_each_tx_datachunk(struct sctp_association *asoc,
16662306a36Sopenharmony_ci				       bool clear,
16762306a36Sopenharmony_ci				       void (*cb)(struct sctp_chunk *))
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci{
17062306a36Sopenharmony_ci	struct sctp_datamsg *msg, *prev_msg = NULL;
17162306a36Sopenharmony_ci	struct sctp_outq *q = &asoc->outqueue;
17262306a36Sopenharmony_ci	struct sctp_chunk *chunk, *c;
17362306a36Sopenharmony_ci	struct sctp_transport *t;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	list_for_each_entry(t, &asoc->peer.transport_addr_list, transports)
17662306a36Sopenharmony_ci		list_for_each_entry(chunk, &t->transmitted, transmitted_list)
17762306a36Sopenharmony_ci			traverse_and_process();
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	list_for_each_entry(chunk, &q->retransmit, transmitted_list)
18062306a36Sopenharmony_ci		traverse_and_process();
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	list_for_each_entry(chunk, &q->sacked, transmitted_list)
18362306a36Sopenharmony_ci		traverse_and_process();
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	list_for_each_entry(chunk, &q->abandoned, transmitted_list)
18662306a36Sopenharmony_ci		traverse_and_process();
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	list_for_each_entry(chunk, &q->out_chunk_list, list)
18962306a36Sopenharmony_ci		traverse_and_process();
19062306a36Sopenharmony_ci}
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_cistatic void sctp_for_each_rx_skb(struct sctp_association *asoc, struct sock *sk,
19362306a36Sopenharmony_ci				 void (*cb)(struct sk_buff *, struct sock *))
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci{
19662306a36Sopenharmony_ci	struct sk_buff *skb, *tmp;
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	sctp_skb_for_each(skb, &asoc->ulpq.lobby, tmp)
19962306a36Sopenharmony_ci		cb(skb, sk);
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	sctp_skb_for_each(skb, &asoc->ulpq.reasm, tmp)
20262306a36Sopenharmony_ci		cb(skb, sk);
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	sctp_skb_for_each(skb, &asoc->ulpq.reasm_uo, tmp)
20562306a36Sopenharmony_ci		cb(skb, sk);
20662306a36Sopenharmony_ci}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci/* Verify that this is a valid address. */
20962306a36Sopenharmony_cistatic inline int sctp_verify_addr(struct sock *sk, union sctp_addr *addr,
21062306a36Sopenharmony_ci				   int len)
21162306a36Sopenharmony_ci{
21262306a36Sopenharmony_ci	struct sctp_af *af;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	/* Verify basic sockaddr. */
21562306a36Sopenharmony_ci	af = sctp_sockaddr_af(sctp_sk(sk), addr, len);
21662306a36Sopenharmony_ci	if (!af)
21762306a36Sopenharmony_ci		return -EINVAL;
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	/* Is this a valid SCTP address?  */
22062306a36Sopenharmony_ci	if (!af->addr_valid(addr, sctp_sk(sk), NULL))
22162306a36Sopenharmony_ci		return -EINVAL;
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	if (!sctp_sk(sk)->pf->send_verify(sctp_sk(sk), (addr)))
22462306a36Sopenharmony_ci		return -EINVAL;
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	return 0;
22762306a36Sopenharmony_ci}
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci/* Look up the association by its id.  If this is not a UDP-style
23062306a36Sopenharmony_ci * socket, the ID field is always ignored.
23162306a36Sopenharmony_ci */
23262306a36Sopenharmony_cistruct sctp_association *sctp_id2assoc(struct sock *sk, sctp_assoc_t id)
23362306a36Sopenharmony_ci{
23462306a36Sopenharmony_ci	struct sctp_association *asoc = NULL;
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	/* If this is not a UDP-style socket, assoc id should be ignored. */
23762306a36Sopenharmony_ci	if (!sctp_style(sk, UDP)) {
23862306a36Sopenharmony_ci		/* Return NULL if the socket state is not ESTABLISHED. It
23962306a36Sopenharmony_ci		 * could be a TCP-style listening socket or a socket which
24062306a36Sopenharmony_ci		 * hasn't yet called connect() to establish an association.
24162306a36Sopenharmony_ci		 */
24262306a36Sopenharmony_ci		if (!sctp_sstate(sk, ESTABLISHED) && !sctp_sstate(sk, CLOSING))
24362306a36Sopenharmony_ci			return NULL;
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci		/* Get the first and the only association from the list. */
24662306a36Sopenharmony_ci		if (!list_empty(&sctp_sk(sk)->ep->asocs))
24762306a36Sopenharmony_ci			asoc = list_entry(sctp_sk(sk)->ep->asocs.next,
24862306a36Sopenharmony_ci					  struct sctp_association, asocs);
24962306a36Sopenharmony_ci		return asoc;
25062306a36Sopenharmony_ci	}
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	/* Otherwise this is a UDP-style socket. */
25362306a36Sopenharmony_ci	if (id <= SCTP_ALL_ASSOC)
25462306a36Sopenharmony_ci		return NULL;
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	spin_lock_bh(&sctp_assocs_id_lock);
25762306a36Sopenharmony_ci	asoc = (struct sctp_association *)idr_find(&sctp_assocs_id, (int)id);
25862306a36Sopenharmony_ci	if (asoc && (asoc->base.sk != sk || asoc->base.dead))
25962306a36Sopenharmony_ci		asoc = NULL;
26062306a36Sopenharmony_ci	spin_unlock_bh(&sctp_assocs_id_lock);
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	return asoc;
26362306a36Sopenharmony_ci}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci/* Look up the transport from an address and an assoc id. If both address and
26662306a36Sopenharmony_ci * id are specified, the associations matching the address and the id should be
26762306a36Sopenharmony_ci * the same.
26862306a36Sopenharmony_ci */
26962306a36Sopenharmony_cistatic struct sctp_transport *sctp_addr_id2transport(struct sock *sk,
27062306a36Sopenharmony_ci					      struct sockaddr_storage *addr,
27162306a36Sopenharmony_ci					      sctp_assoc_t id)
27262306a36Sopenharmony_ci{
27362306a36Sopenharmony_ci	struct sctp_association *addr_asoc = NULL, *id_asoc = NULL;
27462306a36Sopenharmony_ci	struct sctp_af *af = sctp_get_af_specific(addr->ss_family);
27562306a36Sopenharmony_ci	union sctp_addr *laddr = (union sctp_addr *)addr;
27662306a36Sopenharmony_ci	struct sctp_transport *transport;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	if (!af || sctp_verify_addr(sk, laddr, af->sockaddr_len))
27962306a36Sopenharmony_ci		return NULL;
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	addr_asoc = sctp_endpoint_lookup_assoc(sctp_sk(sk)->ep,
28262306a36Sopenharmony_ci					       laddr,
28362306a36Sopenharmony_ci					       &transport);
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	if (!addr_asoc)
28662306a36Sopenharmony_ci		return NULL;
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	id_asoc = sctp_id2assoc(sk, id);
28962306a36Sopenharmony_ci	if (id_asoc && (id_asoc != addr_asoc))
29062306a36Sopenharmony_ci		return NULL;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	sctp_get_pf_specific(sk->sk_family)->addr_to_user(sctp_sk(sk),
29362306a36Sopenharmony_ci						(union sctp_addr *)addr);
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	return transport;
29662306a36Sopenharmony_ci}
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci/* API 3.1.2 bind() - UDP Style Syntax
29962306a36Sopenharmony_ci * The syntax of bind() is,
30062306a36Sopenharmony_ci *
30162306a36Sopenharmony_ci *   ret = bind(int sd, struct sockaddr *addr, int addrlen);
30262306a36Sopenharmony_ci *
30362306a36Sopenharmony_ci *   sd      - the socket descriptor returned by socket().
30462306a36Sopenharmony_ci *   addr    - the address structure (struct sockaddr_in or struct
30562306a36Sopenharmony_ci *             sockaddr_in6 [RFC 2553]),
30662306a36Sopenharmony_ci *   addr_len - the size of the address structure.
30762306a36Sopenharmony_ci */
30862306a36Sopenharmony_cistatic int sctp_bind(struct sock *sk, struct sockaddr *addr, int addr_len)
30962306a36Sopenharmony_ci{
31062306a36Sopenharmony_ci	int retval = 0;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	lock_sock(sk);
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	pr_debug("%s: sk:%p, addr:%p, addr_len:%d\n", __func__, sk,
31562306a36Sopenharmony_ci		 addr, addr_len);
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	/* Disallow binding twice. */
31862306a36Sopenharmony_ci	if (!sctp_sk(sk)->ep->base.bind_addr.port)
31962306a36Sopenharmony_ci		retval = sctp_do_bind(sk, (union sctp_addr *)addr,
32062306a36Sopenharmony_ci				      addr_len);
32162306a36Sopenharmony_ci	else
32262306a36Sopenharmony_ci		retval = -EINVAL;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	release_sock(sk);
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	return retval;
32762306a36Sopenharmony_ci}
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_cistatic int sctp_get_port_local(struct sock *, union sctp_addr *);
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci/* Verify this is a valid sockaddr. */
33262306a36Sopenharmony_cistatic struct sctp_af *sctp_sockaddr_af(struct sctp_sock *opt,
33362306a36Sopenharmony_ci					union sctp_addr *addr, int len)
33462306a36Sopenharmony_ci{
33562306a36Sopenharmony_ci	struct sctp_af *af;
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	/* Check minimum size.  */
33862306a36Sopenharmony_ci	if (len < sizeof (struct sockaddr))
33962306a36Sopenharmony_ci		return NULL;
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	if (!opt->pf->af_supported(addr->sa.sa_family, opt))
34262306a36Sopenharmony_ci		return NULL;
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	if (addr->sa.sa_family == AF_INET6) {
34562306a36Sopenharmony_ci		if (len < SIN6_LEN_RFC2133)
34662306a36Sopenharmony_ci			return NULL;
34762306a36Sopenharmony_ci		/* V4 mapped address are really of AF_INET family */
34862306a36Sopenharmony_ci		if (ipv6_addr_v4mapped(&addr->v6.sin6_addr) &&
34962306a36Sopenharmony_ci		    !opt->pf->af_supported(AF_INET, opt))
35062306a36Sopenharmony_ci			return NULL;
35162306a36Sopenharmony_ci	}
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	/* If we get this far, af is valid. */
35462306a36Sopenharmony_ci	af = sctp_get_af_specific(addr->sa.sa_family);
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	if (len < af->sockaddr_len)
35762306a36Sopenharmony_ci		return NULL;
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	return af;
36062306a36Sopenharmony_ci}
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_cistatic void sctp_auto_asconf_init(struct sctp_sock *sp)
36362306a36Sopenharmony_ci{
36462306a36Sopenharmony_ci	struct net *net = sock_net(&sp->inet.sk);
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	if (net->sctp.default_auto_asconf) {
36762306a36Sopenharmony_ci		spin_lock_bh(&net->sctp.addr_wq_lock);
36862306a36Sopenharmony_ci		list_add_tail(&sp->auto_asconf_list, &net->sctp.auto_asconf_splist);
36962306a36Sopenharmony_ci		spin_unlock_bh(&net->sctp.addr_wq_lock);
37062306a36Sopenharmony_ci		sp->do_auto_asconf = 1;
37162306a36Sopenharmony_ci	}
37262306a36Sopenharmony_ci}
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci/* Bind a local address either to an endpoint or to an association.  */
37562306a36Sopenharmony_cistatic int sctp_do_bind(struct sock *sk, union sctp_addr *addr, int len)
37662306a36Sopenharmony_ci{
37762306a36Sopenharmony_ci	struct net *net = sock_net(sk);
37862306a36Sopenharmony_ci	struct sctp_sock *sp = sctp_sk(sk);
37962306a36Sopenharmony_ci	struct sctp_endpoint *ep = sp->ep;
38062306a36Sopenharmony_ci	struct sctp_bind_addr *bp = &ep->base.bind_addr;
38162306a36Sopenharmony_ci	struct sctp_af *af;
38262306a36Sopenharmony_ci	unsigned short snum;
38362306a36Sopenharmony_ci	int ret = 0;
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	/* Common sockaddr verification. */
38662306a36Sopenharmony_ci	af = sctp_sockaddr_af(sp, addr, len);
38762306a36Sopenharmony_ci	if (!af) {
38862306a36Sopenharmony_ci		pr_debug("%s: sk:%p, newaddr:%p, len:%d EINVAL\n",
38962306a36Sopenharmony_ci			 __func__, sk, addr, len);
39062306a36Sopenharmony_ci		return -EINVAL;
39162306a36Sopenharmony_ci	}
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	snum = ntohs(addr->v4.sin_port);
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	pr_debug("%s: sk:%p, new addr:%pISc, port:%d, new port:%d, len:%d\n",
39662306a36Sopenharmony_ci		 __func__, sk, &addr->sa, bp->port, snum, len);
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	/* PF specific bind() address verification. */
39962306a36Sopenharmony_ci	if (!sp->pf->bind_verify(sp, addr))
40062306a36Sopenharmony_ci		return -EADDRNOTAVAIL;
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	/* We must either be unbound, or bind to the same port.
40362306a36Sopenharmony_ci	 * It's OK to allow 0 ports if we are already bound.
40462306a36Sopenharmony_ci	 * We'll just inhert an already bound port in this case
40562306a36Sopenharmony_ci	 */
40662306a36Sopenharmony_ci	if (bp->port) {
40762306a36Sopenharmony_ci		if (!snum)
40862306a36Sopenharmony_ci			snum = bp->port;
40962306a36Sopenharmony_ci		else if (snum != bp->port) {
41062306a36Sopenharmony_ci			pr_debug("%s: new port %d doesn't match existing port "
41162306a36Sopenharmony_ci				 "%d\n", __func__, snum, bp->port);
41262306a36Sopenharmony_ci			return -EINVAL;
41362306a36Sopenharmony_ci		}
41462306a36Sopenharmony_ci	}
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	if (snum && inet_port_requires_bind_service(net, snum) &&
41762306a36Sopenharmony_ci	    !ns_capable(net->user_ns, CAP_NET_BIND_SERVICE))
41862306a36Sopenharmony_ci		return -EACCES;
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	/* See if the address matches any of the addresses we may have
42162306a36Sopenharmony_ci	 * already bound before checking against other endpoints.
42262306a36Sopenharmony_ci	 */
42362306a36Sopenharmony_ci	if (sctp_bind_addr_match(bp, addr, sp))
42462306a36Sopenharmony_ci		return -EINVAL;
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	/* Make sure we are allowed to bind here.
42762306a36Sopenharmony_ci	 * The function sctp_get_port_local() does duplicate address
42862306a36Sopenharmony_ci	 * detection.
42962306a36Sopenharmony_ci	 */
43062306a36Sopenharmony_ci	addr->v4.sin_port = htons(snum);
43162306a36Sopenharmony_ci	if (sctp_get_port_local(sk, addr))
43262306a36Sopenharmony_ci		return -EADDRINUSE;
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	/* Refresh ephemeral port.  */
43562306a36Sopenharmony_ci	if (!bp->port) {
43662306a36Sopenharmony_ci		bp->port = inet_sk(sk)->inet_num;
43762306a36Sopenharmony_ci		sctp_auto_asconf_init(sp);
43862306a36Sopenharmony_ci	}
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	/* Add the address to the bind address list.
44162306a36Sopenharmony_ci	 * Use GFP_ATOMIC since BHs will be disabled.
44262306a36Sopenharmony_ci	 */
44362306a36Sopenharmony_ci	ret = sctp_add_bind_addr(bp, addr, af->sockaddr_len,
44462306a36Sopenharmony_ci				 SCTP_ADDR_SRC, GFP_ATOMIC);
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	if (ret) {
44762306a36Sopenharmony_ci		sctp_put_port(sk);
44862306a36Sopenharmony_ci		return ret;
44962306a36Sopenharmony_ci	}
45062306a36Sopenharmony_ci	/* Copy back into socket for getsockname() use. */
45162306a36Sopenharmony_ci	inet_sk(sk)->inet_sport = htons(inet_sk(sk)->inet_num);
45262306a36Sopenharmony_ci	sp->pf->to_sk_saddr(addr, sk);
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	return ret;
45562306a36Sopenharmony_ci}
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci /* ADDIP Section 4.1.1 Congestion Control of ASCONF Chunks
45862306a36Sopenharmony_ci *
45962306a36Sopenharmony_ci * R1) One and only one ASCONF Chunk MAY be in transit and unacknowledged
46062306a36Sopenharmony_ci * at any one time.  If a sender, after sending an ASCONF chunk, decides
46162306a36Sopenharmony_ci * it needs to transfer another ASCONF Chunk, it MUST wait until the
46262306a36Sopenharmony_ci * ASCONF-ACK Chunk returns from the previous ASCONF Chunk before sending a
46362306a36Sopenharmony_ci * subsequent ASCONF. Note this restriction binds each side, so at any
46462306a36Sopenharmony_ci * time two ASCONF may be in-transit on any given association (one sent
46562306a36Sopenharmony_ci * from each endpoint).
46662306a36Sopenharmony_ci */
46762306a36Sopenharmony_cistatic int sctp_send_asconf(struct sctp_association *asoc,
46862306a36Sopenharmony_ci			    struct sctp_chunk *chunk)
46962306a36Sopenharmony_ci{
47062306a36Sopenharmony_ci	int retval = 0;
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	/* If there is an outstanding ASCONF chunk, queue it for later
47362306a36Sopenharmony_ci	 * transmission.
47462306a36Sopenharmony_ci	 */
47562306a36Sopenharmony_ci	if (asoc->addip_last_asconf) {
47662306a36Sopenharmony_ci		list_add_tail(&chunk->list, &asoc->addip_chunk_list);
47762306a36Sopenharmony_ci		goto out;
47862306a36Sopenharmony_ci	}
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	/* Hold the chunk until an ASCONF_ACK is received. */
48162306a36Sopenharmony_ci	sctp_chunk_hold(chunk);
48262306a36Sopenharmony_ci	retval = sctp_primitive_ASCONF(asoc->base.net, asoc, chunk);
48362306a36Sopenharmony_ci	if (retval)
48462306a36Sopenharmony_ci		sctp_chunk_free(chunk);
48562306a36Sopenharmony_ci	else
48662306a36Sopenharmony_ci		asoc->addip_last_asconf = chunk;
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ciout:
48962306a36Sopenharmony_ci	return retval;
49062306a36Sopenharmony_ci}
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci/* Add a list of addresses as bind addresses to local endpoint or
49362306a36Sopenharmony_ci * association.
49462306a36Sopenharmony_ci *
49562306a36Sopenharmony_ci * Basically run through each address specified in the addrs/addrcnt
49662306a36Sopenharmony_ci * array/length pair, determine if it is IPv6 or IPv4 and call
49762306a36Sopenharmony_ci * sctp_do_bind() on it.
49862306a36Sopenharmony_ci *
49962306a36Sopenharmony_ci * If any of them fails, then the operation will be reversed and the
50062306a36Sopenharmony_ci * ones that were added will be removed.
50162306a36Sopenharmony_ci *
50262306a36Sopenharmony_ci * Only sctp_setsockopt_bindx() is supposed to call this function.
50362306a36Sopenharmony_ci */
50462306a36Sopenharmony_cistatic int sctp_bindx_add(struct sock *sk, struct sockaddr *addrs, int addrcnt)
50562306a36Sopenharmony_ci{
50662306a36Sopenharmony_ci	int cnt;
50762306a36Sopenharmony_ci	int retval = 0;
50862306a36Sopenharmony_ci	void *addr_buf;
50962306a36Sopenharmony_ci	struct sockaddr *sa_addr;
51062306a36Sopenharmony_ci	struct sctp_af *af;
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	pr_debug("%s: sk:%p, addrs:%p, addrcnt:%d\n", __func__, sk,
51362306a36Sopenharmony_ci		 addrs, addrcnt);
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	addr_buf = addrs;
51662306a36Sopenharmony_ci	for (cnt = 0; cnt < addrcnt; cnt++) {
51762306a36Sopenharmony_ci		/* The list may contain either IPv4 or IPv6 address;
51862306a36Sopenharmony_ci		 * determine the address length for walking thru the list.
51962306a36Sopenharmony_ci		 */
52062306a36Sopenharmony_ci		sa_addr = addr_buf;
52162306a36Sopenharmony_ci		af = sctp_get_af_specific(sa_addr->sa_family);
52262306a36Sopenharmony_ci		if (!af) {
52362306a36Sopenharmony_ci			retval = -EINVAL;
52462306a36Sopenharmony_ci			goto err_bindx_add;
52562306a36Sopenharmony_ci		}
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci		retval = sctp_do_bind(sk, (union sctp_addr *)sa_addr,
52862306a36Sopenharmony_ci				      af->sockaddr_len);
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci		addr_buf += af->sockaddr_len;
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_cierr_bindx_add:
53362306a36Sopenharmony_ci		if (retval < 0) {
53462306a36Sopenharmony_ci			/* Failed. Cleanup the ones that have been added */
53562306a36Sopenharmony_ci			if (cnt > 0)
53662306a36Sopenharmony_ci				sctp_bindx_rem(sk, addrs, cnt);
53762306a36Sopenharmony_ci			return retval;
53862306a36Sopenharmony_ci		}
53962306a36Sopenharmony_ci	}
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	return retval;
54262306a36Sopenharmony_ci}
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci/* Send an ASCONF chunk with Add IP address parameters to all the peers of the
54562306a36Sopenharmony_ci * associations that are part of the endpoint indicating that a list of local
54662306a36Sopenharmony_ci * addresses are added to the endpoint.
54762306a36Sopenharmony_ci *
54862306a36Sopenharmony_ci * If any of the addresses is already in the bind address list of the
54962306a36Sopenharmony_ci * association, we do not send the chunk for that association.  But it will not
55062306a36Sopenharmony_ci * affect other associations.
55162306a36Sopenharmony_ci *
55262306a36Sopenharmony_ci * Only sctp_setsockopt_bindx() is supposed to call this function.
55362306a36Sopenharmony_ci */
55462306a36Sopenharmony_cistatic int sctp_send_asconf_add_ip(struct sock		*sk,
55562306a36Sopenharmony_ci				   struct sockaddr	*addrs,
55662306a36Sopenharmony_ci				   int 			addrcnt)
55762306a36Sopenharmony_ci{
55862306a36Sopenharmony_ci	struct sctp_sock		*sp;
55962306a36Sopenharmony_ci	struct sctp_endpoint		*ep;
56062306a36Sopenharmony_ci	struct sctp_association		*asoc;
56162306a36Sopenharmony_ci	struct sctp_bind_addr		*bp;
56262306a36Sopenharmony_ci	struct sctp_chunk		*chunk;
56362306a36Sopenharmony_ci	struct sctp_sockaddr_entry	*laddr;
56462306a36Sopenharmony_ci	union sctp_addr			*addr;
56562306a36Sopenharmony_ci	union sctp_addr			saveaddr;
56662306a36Sopenharmony_ci	void				*addr_buf;
56762306a36Sopenharmony_ci	struct sctp_af			*af;
56862306a36Sopenharmony_ci	struct list_head		*p;
56962306a36Sopenharmony_ci	int 				i;
57062306a36Sopenharmony_ci	int 				retval = 0;
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	sp = sctp_sk(sk);
57362306a36Sopenharmony_ci	ep = sp->ep;
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	if (!ep->asconf_enable)
57662306a36Sopenharmony_ci		return retval;
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci	pr_debug("%s: sk:%p, addrs:%p, addrcnt:%d\n",
57962306a36Sopenharmony_ci		 __func__, sk, addrs, addrcnt);
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	list_for_each_entry(asoc, &ep->asocs, asocs) {
58262306a36Sopenharmony_ci		if (!asoc->peer.asconf_capable)
58362306a36Sopenharmony_ci			continue;
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci		if (asoc->peer.addip_disabled_mask & SCTP_PARAM_ADD_IP)
58662306a36Sopenharmony_ci			continue;
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci		if (!sctp_state(asoc, ESTABLISHED))
58962306a36Sopenharmony_ci			continue;
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci		/* Check if any address in the packed array of addresses is
59262306a36Sopenharmony_ci		 * in the bind address list of the association. If so,
59362306a36Sopenharmony_ci		 * do not send the asconf chunk to its peer, but continue with
59462306a36Sopenharmony_ci		 * other associations.
59562306a36Sopenharmony_ci		 */
59662306a36Sopenharmony_ci		addr_buf = addrs;
59762306a36Sopenharmony_ci		for (i = 0; i < addrcnt; i++) {
59862306a36Sopenharmony_ci			addr = addr_buf;
59962306a36Sopenharmony_ci			af = sctp_get_af_specific(addr->v4.sin_family);
60062306a36Sopenharmony_ci			if (!af) {
60162306a36Sopenharmony_ci				retval = -EINVAL;
60262306a36Sopenharmony_ci				goto out;
60362306a36Sopenharmony_ci			}
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci			if (sctp_assoc_lookup_laddr(asoc, addr))
60662306a36Sopenharmony_ci				break;
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci			addr_buf += af->sockaddr_len;
60962306a36Sopenharmony_ci		}
61062306a36Sopenharmony_ci		if (i < addrcnt)
61162306a36Sopenharmony_ci			continue;
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci		/* Use the first valid address in bind addr list of
61462306a36Sopenharmony_ci		 * association as Address Parameter of ASCONF CHUNK.
61562306a36Sopenharmony_ci		 */
61662306a36Sopenharmony_ci		bp = &asoc->base.bind_addr;
61762306a36Sopenharmony_ci		p = bp->address_list.next;
61862306a36Sopenharmony_ci		laddr = list_entry(p, struct sctp_sockaddr_entry, list);
61962306a36Sopenharmony_ci		chunk = sctp_make_asconf_update_ip(asoc, &laddr->a, addrs,
62062306a36Sopenharmony_ci						   addrcnt, SCTP_PARAM_ADD_IP);
62162306a36Sopenharmony_ci		if (!chunk) {
62262306a36Sopenharmony_ci			retval = -ENOMEM;
62362306a36Sopenharmony_ci			goto out;
62462306a36Sopenharmony_ci		}
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci		/* Add the new addresses to the bind address list with
62762306a36Sopenharmony_ci		 * use_as_src set to 0.
62862306a36Sopenharmony_ci		 */
62962306a36Sopenharmony_ci		addr_buf = addrs;
63062306a36Sopenharmony_ci		for (i = 0; i < addrcnt; i++) {
63162306a36Sopenharmony_ci			addr = addr_buf;
63262306a36Sopenharmony_ci			af = sctp_get_af_specific(addr->v4.sin_family);
63362306a36Sopenharmony_ci			memcpy(&saveaddr, addr, af->sockaddr_len);
63462306a36Sopenharmony_ci			retval = sctp_add_bind_addr(bp, &saveaddr,
63562306a36Sopenharmony_ci						    sizeof(saveaddr),
63662306a36Sopenharmony_ci						    SCTP_ADDR_NEW, GFP_ATOMIC);
63762306a36Sopenharmony_ci			addr_buf += af->sockaddr_len;
63862306a36Sopenharmony_ci		}
63962306a36Sopenharmony_ci		if (asoc->src_out_of_asoc_ok) {
64062306a36Sopenharmony_ci			struct sctp_transport *trans;
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci			list_for_each_entry(trans,
64362306a36Sopenharmony_ci			    &asoc->peer.transport_addr_list, transports) {
64462306a36Sopenharmony_ci				trans->cwnd = min(4*asoc->pathmtu, max_t(__u32,
64562306a36Sopenharmony_ci				    2*asoc->pathmtu, 4380));
64662306a36Sopenharmony_ci				trans->ssthresh = asoc->peer.i.a_rwnd;
64762306a36Sopenharmony_ci				trans->rto = asoc->rto_initial;
64862306a36Sopenharmony_ci				sctp_max_rto(asoc, trans);
64962306a36Sopenharmony_ci				trans->rtt = trans->srtt = trans->rttvar = 0;
65062306a36Sopenharmony_ci				/* Clear the source and route cache */
65162306a36Sopenharmony_ci				sctp_transport_route(trans, NULL,
65262306a36Sopenharmony_ci						     sctp_sk(asoc->base.sk));
65362306a36Sopenharmony_ci			}
65462306a36Sopenharmony_ci		}
65562306a36Sopenharmony_ci		retval = sctp_send_asconf(asoc, chunk);
65662306a36Sopenharmony_ci	}
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ciout:
65962306a36Sopenharmony_ci	return retval;
66062306a36Sopenharmony_ci}
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci/* Remove a list of addresses from bind addresses list.  Do not remove the
66362306a36Sopenharmony_ci * last address.
66462306a36Sopenharmony_ci *
66562306a36Sopenharmony_ci * Basically run through each address specified in the addrs/addrcnt
66662306a36Sopenharmony_ci * array/length pair, determine if it is IPv6 or IPv4 and call
66762306a36Sopenharmony_ci * sctp_del_bind() on it.
66862306a36Sopenharmony_ci *
66962306a36Sopenharmony_ci * If any of them fails, then the operation will be reversed and the
67062306a36Sopenharmony_ci * ones that were removed will be added back.
67162306a36Sopenharmony_ci *
67262306a36Sopenharmony_ci * At least one address has to be left; if only one address is
67362306a36Sopenharmony_ci * available, the operation will return -EBUSY.
67462306a36Sopenharmony_ci *
67562306a36Sopenharmony_ci * Only sctp_setsockopt_bindx() is supposed to call this function.
67662306a36Sopenharmony_ci */
67762306a36Sopenharmony_cistatic int sctp_bindx_rem(struct sock *sk, struct sockaddr *addrs, int addrcnt)
67862306a36Sopenharmony_ci{
67962306a36Sopenharmony_ci	struct sctp_sock *sp = sctp_sk(sk);
68062306a36Sopenharmony_ci	struct sctp_endpoint *ep = sp->ep;
68162306a36Sopenharmony_ci	int cnt;
68262306a36Sopenharmony_ci	struct sctp_bind_addr *bp = &ep->base.bind_addr;
68362306a36Sopenharmony_ci	int retval = 0;
68462306a36Sopenharmony_ci	void *addr_buf;
68562306a36Sopenharmony_ci	union sctp_addr *sa_addr;
68662306a36Sopenharmony_ci	struct sctp_af *af;
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci	pr_debug("%s: sk:%p, addrs:%p, addrcnt:%d\n",
68962306a36Sopenharmony_ci		 __func__, sk, addrs, addrcnt);
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci	addr_buf = addrs;
69262306a36Sopenharmony_ci	for (cnt = 0; cnt < addrcnt; cnt++) {
69362306a36Sopenharmony_ci		/* If the bind address list is empty or if there is only one
69462306a36Sopenharmony_ci		 * bind address, there is nothing more to be removed (we need
69562306a36Sopenharmony_ci		 * at least one address here).
69662306a36Sopenharmony_ci		 */
69762306a36Sopenharmony_ci		if (list_empty(&bp->address_list) ||
69862306a36Sopenharmony_ci		    (sctp_list_single_entry(&bp->address_list))) {
69962306a36Sopenharmony_ci			retval = -EBUSY;
70062306a36Sopenharmony_ci			goto err_bindx_rem;
70162306a36Sopenharmony_ci		}
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci		sa_addr = addr_buf;
70462306a36Sopenharmony_ci		af = sctp_get_af_specific(sa_addr->sa.sa_family);
70562306a36Sopenharmony_ci		if (!af) {
70662306a36Sopenharmony_ci			retval = -EINVAL;
70762306a36Sopenharmony_ci			goto err_bindx_rem;
70862306a36Sopenharmony_ci		}
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci		if (!af->addr_valid(sa_addr, sp, NULL)) {
71162306a36Sopenharmony_ci			retval = -EADDRNOTAVAIL;
71262306a36Sopenharmony_ci			goto err_bindx_rem;
71362306a36Sopenharmony_ci		}
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci		if (sa_addr->v4.sin_port &&
71662306a36Sopenharmony_ci		    sa_addr->v4.sin_port != htons(bp->port)) {
71762306a36Sopenharmony_ci			retval = -EINVAL;
71862306a36Sopenharmony_ci			goto err_bindx_rem;
71962306a36Sopenharmony_ci		}
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci		if (!sa_addr->v4.sin_port)
72262306a36Sopenharmony_ci			sa_addr->v4.sin_port = htons(bp->port);
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci		/* FIXME - There is probably a need to check if sk->sk_saddr and
72562306a36Sopenharmony_ci		 * sk->sk_rcv_addr are currently set to one of the addresses to
72662306a36Sopenharmony_ci		 * be removed. This is something which needs to be looked into
72762306a36Sopenharmony_ci		 * when we are fixing the outstanding issues with multi-homing
72862306a36Sopenharmony_ci		 * socket routing and failover schemes. Refer to comments in
72962306a36Sopenharmony_ci		 * sctp_do_bind(). -daisy
73062306a36Sopenharmony_ci		 */
73162306a36Sopenharmony_ci		retval = sctp_del_bind_addr(bp, sa_addr);
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci		addr_buf += af->sockaddr_len;
73462306a36Sopenharmony_cierr_bindx_rem:
73562306a36Sopenharmony_ci		if (retval < 0) {
73662306a36Sopenharmony_ci			/* Failed. Add the ones that has been removed back */
73762306a36Sopenharmony_ci			if (cnt > 0)
73862306a36Sopenharmony_ci				sctp_bindx_add(sk, addrs, cnt);
73962306a36Sopenharmony_ci			return retval;
74062306a36Sopenharmony_ci		}
74162306a36Sopenharmony_ci	}
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci	return retval;
74462306a36Sopenharmony_ci}
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci/* Send an ASCONF chunk with Delete IP address parameters to all the peers of
74762306a36Sopenharmony_ci * the associations that are part of the endpoint indicating that a list of
74862306a36Sopenharmony_ci * local addresses are removed from the endpoint.
74962306a36Sopenharmony_ci *
75062306a36Sopenharmony_ci * If any of the addresses is already in the bind address list of the
75162306a36Sopenharmony_ci * association, we do not send the chunk for that association.  But it will not
75262306a36Sopenharmony_ci * affect other associations.
75362306a36Sopenharmony_ci *
75462306a36Sopenharmony_ci * Only sctp_setsockopt_bindx() is supposed to call this function.
75562306a36Sopenharmony_ci */
75662306a36Sopenharmony_cistatic int sctp_send_asconf_del_ip(struct sock		*sk,
75762306a36Sopenharmony_ci				   struct sockaddr	*addrs,
75862306a36Sopenharmony_ci				   int			addrcnt)
75962306a36Sopenharmony_ci{
76062306a36Sopenharmony_ci	struct sctp_sock	*sp;
76162306a36Sopenharmony_ci	struct sctp_endpoint	*ep;
76262306a36Sopenharmony_ci	struct sctp_association	*asoc;
76362306a36Sopenharmony_ci	struct sctp_transport	*transport;
76462306a36Sopenharmony_ci	struct sctp_bind_addr	*bp;
76562306a36Sopenharmony_ci	struct sctp_chunk	*chunk;
76662306a36Sopenharmony_ci	union sctp_addr		*laddr;
76762306a36Sopenharmony_ci	void			*addr_buf;
76862306a36Sopenharmony_ci	struct sctp_af		*af;
76962306a36Sopenharmony_ci	struct sctp_sockaddr_entry *saddr;
77062306a36Sopenharmony_ci	int 			i;
77162306a36Sopenharmony_ci	int 			retval = 0;
77262306a36Sopenharmony_ci	int			stored = 0;
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci	chunk = NULL;
77562306a36Sopenharmony_ci	sp = sctp_sk(sk);
77662306a36Sopenharmony_ci	ep = sp->ep;
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci	if (!ep->asconf_enable)
77962306a36Sopenharmony_ci		return retval;
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci	pr_debug("%s: sk:%p, addrs:%p, addrcnt:%d\n",
78262306a36Sopenharmony_ci		 __func__, sk, addrs, addrcnt);
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci	list_for_each_entry(asoc, &ep->asocs, asocs) {
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_ci		if (!asoc->peer.asconf_capable)
78762306a36Sopenharmony_ci			continue;
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ci		if (asoc->peer.addip_disabled_mask & SCTP_PARAM_DEL_IP)
79062306a36Sopenharmony_ci			continue;
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci		if (!sctp_state(asoc, ESTABLISHED))
79362306a36Sopenharmony_ci			continue;
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_ci		/* Check if any address in the packed array of addresses is
79662306a36Sopenharmony_ci		 * not present in the bind address list of the association.
79762306a36Sopenharmony_ci		 * If so, do not send the asconf chunk to its peer, but
79862306a36Sopenharmony_ci		 * continue with other associations.
79962306a36Sopenharmony_ci		 */
80062306a36Sopenharmony_ci		addr_buf = addrs;
80162306a36Sopenharmony_ci		for (i = 0; i < addrcnt; i++) {
80262306a36Sopenharmony_ci			laddr = addr_buf;
80362306a36Sopenharmony_ci			af = sctp_get_af_specific(laddr->v4.sin_family);
80462306a36Sopenharmony_ci			if (!af) {
80562306a36Sopenharmony_ci				retval = -EINVAL;
80662306a36Sopenharmony_ci				goto out;
80762306a36Sopenharmony_ci			}
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci			if (!sctp_assoc_lookup_laddr(asoc, laddr))
81062306a36Sopenharmony_ci				break;
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci			addr_buf += af->sockaddr_len;
81362306a36Sopenharmony_ci		}
81462306a36Sopenharmony_ci		if (i < addrcnt)
81562306a36Sopenharmony_ci			continue;
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci		/* Find one address in the association's bind address list
81862306a36Sopenharmony_ci		 * that is not in the packed array of addresses. This is to
81962306a36Sopenharmony_ci		 * make sure that we do not delete all the addresses in the
82062306a36Sopenharmony_ci		 * association.
82162306a36Sopenharmony_ci		 */
82262306a36Sopenharmony_ci		bp = &asoc->base.bind_addr;
82362306a36Sopenharmony_ci		laddr = sctp_find_unmatch_addr(bp, (union sctp_addr *)addrs,
82462306a36Sopenharmony_ci					       addrcnt, sp);
82562306a36Sopenharmony_ci		if ((laddr == NULL) && (addrcnt == 1)) {
82662306a36Sopenharmony_ci			if (asoc->asconf_addr_del_pending)
82762306a36Sopenharmony_ci				continue;
82862306a36Sopenharmony_ci			asoc->asconf_addr_del_pending =
82962306a36Sopenharmony_ci			    kzalloc(sizeof(union sctp_addr), GFP_ATOMIC);
83062306a36Sopenharmony_ci			if (asoc->asconf_addr_del_pending == NULL) {
83162306a36Sopenharmony_ci				retval = -ENOMEM;
83262306a36Sopenharmony_ci				goto out;
83362306a36Sopenharmony_ci			}
83462306a36Sopenharmony_ci			asoc->asconf_addr_del_pending->sa.sa_family =
83562306a36Sopenharmony_ci				    addrs->sa_family;
83662306a36Sopenharmony_ci			asoc->asconf_addr_del_pending->v4.sin_port =
83762306a36Sopenharmony_ci				    htons(bp->port);
83862306a36Sopenharmony_ci			if (addrs->sa_family == AF_INET) {
83962306a36Sopenharmony_ci				struct sockaddr_in *sin;
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci				sin = (struct sockaddr_in *)addrs;
84262306a36Sopenharmony_ci				asoc->asconf_addr_del_pending->v4.sin_addr.s_addr = sin->sin_addr.s_addr;
84362306a36Sopenharmony_ci			} else if (addrs->sa_family == AF_INET6) {
84462306a36Sopenharmony_ci				struct sockaddr_in6 *sin6;
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci				sin6 = (struct sockaddr_in6 *)addrs;
84762306a36Sopenharmony_ci				asoc->asconf_addr_del_pending->v6.sin6_addr = sin6->sin6_addr;
84862306a36Sopenharmony_ci			}
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci			pr_debug("%s: keep the last address asoc:%p %pISc at %p\n",
85162306a36Sopenharmony_ci				 __func__, asoc, &asoc->asconf_addr_del_pending->sa,
85262306a36Sopenharmony_ci				 asoc->asconf_addr_del_pending);
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci			asoc->src_out_of_asoc_ok = 1;
85562306a36Sopenharmony_ci			stored = 1;
85662306a36Sopenharmony_ci			goto skip_mkasconf;
85762306a36Sopenharmony_ci		}
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci		if (laddr == NULL)
86062306a36Sopenharmony_ci			return -EINVAL;
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci		/* We do not need RCU protection throughout this loop
86362306a36Sopenharmony_ci		 * because this is done under a socket lock from the
86462306a36Sopenharmony_ci		 * setsockopt call.
86562306a36Sopenharmony_ci		 */
86662306a36Sopenharmony_ci		chunk = sctp_make_asconf_update_ip(asoc, laddr, addrs, addrcnt,
86762306a36Sopenharmony_ci						   SCTP_PARAM_DEL_IP);
86862306a36Sopenharmony_ci		if (!chunk) {
86962306a36Sopenharmony_ci			retval = -ENOMEM;
87062306a36Sopenharmony_ci			goto out;
87162306a36Sopenharmony_ci		}
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_ciskip_mkasconf:
87462306a36Sopenharmony_ci		/* Reset use_as_src flag for the addresses in the bind address
87562306a36Sopenharmony_ci		 * list that are to be deleted.
87662306a36Sopenharmony_ci		 */
87762306a36Sopenharmony_ci		addr_buf = addrs;
87862306a36Sopenharmony_ci		for (i = 0; i < addrcnt; i++) {
87962306a36Sopenharmony_ci			laddr = addr_buf;
88062306a36Sopenharmony_ci			af = sctp_get_af_specific(laddr->v4.sin_family);
88162306a36Sopenharmony_ci			list_for_each_entry(saddr, &bp->address_list, list) {
88262306a36Sopenharmony_ci				if (sctp_cmp_addr_exact(&saddr->a, laddr))
88362306a36Sopenharmony_ci					saddr->state = SCTP_ADDR_DEL;
88462306a36Sopenharmony_ci			}
88562306a36Sopenharmony_ci			addr_buf += af->sockaddr_len;
88662306a36Sopenharmony_ci		}
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci		/* Update the route and saddr entries for all the transports
88962306a36Sopenharmony_ci		 * as some of the addresses in the bind address list are
89062306a36Sopenharmony_ci		 * about to be deleted and cannot be used as source addresses.
89162306a36Sopenharmony_ci		 */
89262306a36Sopenharmony_ci		list_for_each_entry(transport, &asoc->peer.transport_addr_list,
89362306a36Sopenharmony_ci					transports) {
89462306a36Sopenharmony_ci			sctp_transport_route(transport, NULL,
89562306a36Sopenharmony_ci					     sctp_sk(asoc->base.sk));
89662306a36Sopenharmony_ci		}
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci		if (stored)
89962306a36Sopenharmony_ci			/* We don't need to transmit ASCONF */
90062306a36Sopenharmony_ci			continue;
90162306a36Sopenharmony_ci		retval = sctp_send_asconf(asoc, chunk);
90262306a36Sopenharmony_ci	}
90362306a36Sopenharmony_ciout:
90462306a36Sopenharmony_ci	return retval;
90562306a36Sopenharmony_ci}
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci/* set addr events to assocs in the endpoint.  ep and addr_wq must be locked */
90862306a36Sopenharmony_ciint sctp_asconf_mgmt(struct sctp_sock *sp, struct sctp_sockaddr_entry *addrw)
90962306a36Sopenharmony_ci{
91062306a36Sopenharmony_ci	struct sock *sk = sctp_opt2sk(sp);
91162306a36Sopenharmony_ci	union sctp_addr *addr;
91262306a36Sopenharmony_ci	struct sctp_af *af;
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ci	/* It is safe to write port space in caller. */
91562306a36Sopenharmony_ci	addr = &addrw->a;
91662306a36Sopenharmony_ci	addr->v4.sin_port = htons(sp->ep->base.bind_addr.port);
91762306a36Sopenharmony_ci	af = sctp_get_af_specific(addr->sa.sa_family);
91862306a36Sopenharmony_ci	if (!af)
91962306a36Sopenharmony_ci		return -EINVAL;
92062306a36Sopenharmony_ci	if (sctp_verify_addr(sk, addr, af->sockaddr_len))
92162306a36Sopenharmony_ci		return -EINVAL;
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci	if (addrw->state == SCTP_ADDR_NEW)
92462306a36Sopenharmony_ci		return sctp_send_asconf_add_ip(sk, (struct sockaddr *)addr, 1);
92562306a36Sopenharmony_ci	else
92662306a36Sopenharmony_ci		return sctp_send_asconf_del_ip(sk, (struct sockaddr *)addr, 1);
92762306a36Sopenharmony_ci}
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_ci/* Helper for tunneling sctp_bindx() requests through sctp_setsockopt()
93062306a36Sopenharmony_ci *
93162306a36Sopenharmony_ci * API 8.1
93262306a36Sopenharmony_ci * int sctp_bindx(int sd, struct sockaddr *addrs, int addrcnt,
93362306a36Sopenharmony_ci *                int flags);
93462306a36Sopenharmony_ci *
93562306a36Sopenharmony_ci * If sd is an IPv4 socket, the addresses passed must be IPv4 addresses.
93662306a36Sopenharmony_ci * If the sd is an IPv6 socket, the addresses passed can either be IPv4
93762306a36Sopenharmony_ci * or IPv6 addresses.
93862306a36Sopenharmony_ci *
93962306a36Sopenharmony_ci * A single address may be specified as INADDR_ANY or IN6ADDR_ANY, see
94062306a36Sopenharmony_ci * Section 3.1.2 for this usage.
94162306a36Sopenharmony_ci *
94262306a36Sopenharmony_ci * addrs is a pointer to an array of one or more socket addresses. Each
94362306a36Sopenharmony_ci * address is contained in its appropriate structure (i.e. struct
94462306a36Sopenharmony_ci * sockaddr_in or struct sockaddr_in6) the family of the address type
94562306a36Sopenharmony_ci * must be used to distinguish the address length (note that this
94662306a36Sopenharmony_ci * representation is termed a "packed array" of addresses). The caller
94762306a36Sopenharmony_ci * specifies the number of addresses in the array with addrcnt.
94862306a36Sopenharmony_ci *
94962306a36Sopenharmony_ci * On success, sctp_bindx() returns 0. On failure, sctp_bindx() returns
95062306a36Sopenharmony_ci * -1, and sets errno to the appropriate error code.
95162306a36Sopenharmony_ci *
95262306a36Sopenharmony_ci * For SCTP, the port given in each socket address must be the same, or
95362306a36Sopenharmony_ci * sctp_bindx() will fail, setting errno to EINVAL.
95462306a36Sopenharmony_ci *
95562306a36Sopenharmony_ci * The flags parameter is formed from the bitwise OR of zero or more of
95662306a36Sopenharmony_ci * the following currently defined flags:
95762306a36Sopenharmony_ci *
95862306a36Sopenharmony_ci * SCTP_BINDX_ADD_ADDR
95962306a36Sopenharmony_ci *
96062306a36Sopenharmony_ci * SCTP_BINDX_REM_ADDR
96162306a36Sopenharmony_ci *
96262306a36Sopenharmony_ci * SCTP_BINDX_ADD_ADDR directs SCTP to add the given addresses to the
96362306a36Sopenharmony_ci * association, and SCTP_BINDX_REM_ADDR directs SCTP to remove the given
96462306a36Sopenharmony_ci * addresses from the association. The two flags are mutually exclusive;
96562306a36Sopenharmony_ci * if both are given, sctp_bindx() will fail with EINVAL. A caller may
96662306a36Sopenharmony_ci * not remove all addresses from an association; sctp_bindx() will
96762306a36Sopenharmony_ci * reject such an attempt with EINVAL.
96862306a36Sopenharmony_ci *
96962306a36Sopenharmony_ci * An application can use sctp_bindx(SCTP_BINDX_ADD_ADDR) to associate
97062306a36Sopenharmony_ci * additional addresses with an endpoint after calling bind().  Or use
97162306a36Sopenharmony_ci * sctp_bindx(SCTP_BINDX_REM_ADDR) to remove some addresses a listening
97262306a36Sopenharmony_ci * socket is associated with so that no new association accepted will be
97362306a36Sopenharmony_ci * associated with those addresses. If the endpoint supports dynamic
97462306a36Sopenharmony_ci * address a SCTP_BINDX_REM_ADDR or SCTP_BINDX_ADD_ADDR may cause a
97562306a36Sopenharmony_ci * endpoint to send the appropriate message to the peer to change the
97662306a36Sopenharmony_ci * peers address lists.
97762306a36Sopenharmony_ci *
97862306a36Sopenharmony_ci * Adding and removing addresses from a connected association is
97962306a36Sopenharmony_ci * optional functionality. Implementations that do not support this
98062306a36Sopenharmony_ci * functionality should return EOPNOTSUPP.
98162306a36Sopenharmony_ci *
98262306a36Sopenharmony_ci * Basically do nothing but copying the addresses from user to kernel
98362306a36Sopenharmony_ci * land and invoking either sctp_bindx_add() or sctp_bindx_rem() on the sk.
98462306a36Sopenharmony_ci * This is used for tunneling the sctp_bindx() request through sctp_setsockopt()
98562306a36Sopenharmony_ci * from userspace.
98662306a36Sopenharmony_ci *
98762306a36Sopenharmony_ci * On exit there is no need to do sockfd_put(), sys_setsockopt() does
98862306a36Sopenharmony_ci * it.
98962306a36Sopenharmony_ci *
99062306a36Sopenharmony_ci * sk        The sk of the socket
99162306a36Sopenharmony_ci * addrs     The pointer to the addresses
99262306a36Sopenharmony_ci * addrssize Size of the addrs buffer
99362306a36Sopenharmony_ci * op        Operation to perform (add or remove, see the flags of
99462306a36Sopenharmony_ci *           sctp_bindx)
99562306a36Sopenharmony_ci *
99662306a36Sopenharmony_ci * Returns 0 if ok, <0 errno code on error.
99762306a36Sopenharmony_ci */
99862306a36Sopenharmony_cistatic int sctp_setsockopt_bindx(struct sock *sk, struct sockaddr *addrs,
99962306a36Sopenharmony_ci				 int addrs_size, int op)
100062306a36Sopenharmony_ci{
100162306a36Sopenharmony_ci	int err;
100262306a36Sopenharmony_ci	int addrcnt = 0;
100362306a36Sopenharmony_ci	int walk_size = 0;
100462306a36Sopenharmony_ci	struct sockaddr *sa_addr;
100562306a36Sopenharmony_ci	void *addr_buf = addrs;
100662306a36Sopenharmony_ci	struct sctp_af *af;
100762306a36Sopenharmony_ci
100862306a36Sopenharmony_ci	pr_debug("%s: sk:%p addrs:%p addrs_size:%d opt:%d\n",
100962306a36Sopenharmony_ci		 __func__, sk, addr_buf, addrs_size, op);
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_ci	if (unlikely(addrs_size <= 0))
101262306a36Sopenharmony_ci		return -EINVAL;
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_ci	/* Walk through the addrs buffer and count the number of addresses. */
101562306a36Sopenharmony_ci	while (walk_size < addrs_size) {
101662306a36Sopenharmony_ci		if (walk_size + sizeof(sa_family_t) > addrs_size)
101762306a36Sopenharmony_ci			return -EINVAL;
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_ci		sa_addr = addr_buf;
102062306a36Sopenharmony_ci		af = sctp_get_af_specific(sa_addr->sa_family);
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_ci		/* If the address family is not supported or if this address
102362306a36Sopenharmony_ci		 * causes the address buffer to overflow return EINVAL.
102462306a36Sopenharmony_ci		 */
102562306a36Sopenharmony_ci		if (!af || (walk_size + af->sockaddr_len) > addrs_size)
102662306a36Sopenharmony_ci			return -EINVAL;
102762306a36Sopenharmony_ci		addrcnt++;
102862306a36Sopenharmony_ci		addr_buf += af->sockaddr_len;
102962306a36Sopenharmony_ci		walk_size += af->sockaddr_len;
103062306a36Sopenharmony_ci	}
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_ci	/* Do the work. */
103362306a36Sopenharmony_ci	switch (op) {
103462306a36Sopenharmony_ci	case SCTP_BINDX_ADD_ADDR:
103562306a36Sopenharmony_ci		/* Allow security module to validate bindx addresses. */
103662306a36Sopenharmony_ci		err = security_sctp_bind_connect(sk, SCTP_SOCKOPT_BINDX_ADD,
103762306a36Sopenharmony_ci						 addrs, addrs_size);
103862306a36Sopenharmony_ci		if (err)
103962306a36Sopenharmony_ci			return err;
104062306a36Sopenharmony_ci		err = sctp_bindx_add(sk, addrs, addrcnt);
104162306a36Sopenharmony_ci		if (err)
104262306a36Sopenharmony_ci			return err;
104362306a36Sopenharmony_ci		return sctp_send_asconf_add_ip(sk, addrs, addrcnt);
104462306a36Sopenharmony_ci	case SCTP_BINDX_REM_ADDR:
104562306a36Sopenharmony_ci		err = sctp_bindx_rem(sk, addrs, addrcnt);
104662306a36Sopenharmony_ci		if (err)
104762306a36Sopenharmony_ci			return err;
104862306a36Sopenharmony_ci		return sctp_send_asconf_del_ip(sk, addrs, addrcnt);
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_ci	default:
105162306a36Sopenharmony_ci		return -EINVAL;
105262306a36Sopenharmony_ci	}
105362306a36Sopenharmony_ci}
105462306a36Sopenharmony_ci
105562306a36Sopenharmony_cistatic int sctp_bind_add(struct sock *sk, struct sockaddr *addrs,
105662306a36Sopenharmony_ci		int addrlen)
105762306a36Sopenharmony_ci{
105862306a36Sopenharmony_ci	int err;
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_ci	lock_sock(sk);
106162306a36Sopenharmony_ci	err = sctp_setsockopt_bindx(sk, addrs, addrlen, SCTP_BINDX_ADD_ADDR);
106262306a36Sopenharmony_ci	release_sock(sk);
106362306a36Sopenharmony_ci	return err;
106462306a36Sopenharmony_ci}
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_cistatic int sctp_connect_new_asoc(struct sctp_endpoint *ep,
106762306a36Sopenharmony_ci				 const union sctp_addr *daddr,
106862306a36Sopenharmony_ci				 const struct sctp_initmsg *init,
106962306a36Sopenharmony_ci				 struct sctp_transport **tp)
107062306a36Sopenharmony_ci{
107162306a36Sopenharmony_ci	struct sctp_association *asoc;
107262306a36Sopenharmony_ci	struct sock *sk = ep->base.sk;
107362306a36Sopenharmony_ci	struct net *net = sock_net(sk);
107462306a36Sopenharmony_ci	enum sctp_scope scope;
107562306a36Sopenharmony_ci	int err;
107662306a36Sopenharmony_ci
107762306a36Sopenharmony_ci	if (sctp_endpoint_is_peeled_off(ep, daddr))
107862306a36Sopenharmony_ci		return -EADDRNOTAVAIL;
107962306a36Sopenharmony_ci
108062306a36Sopenharmony_ci	if (!ep->base.bind_addr.port) {
108162306a36Sopenharmony_ci		if (sctp_autobind(sk))
108262306a36Sopenharmony_ci			return -EAGAIN;
108362306a36Sopenharmony_ci	} else {
108462306a36Sopenharmony_ci		if (inet_port_requires_bind_service(net, ep->base.bind_addr.port) &&
108562306a36Sopenharmony_ci		    !ns_capable(net->user_ns, CAP_NET_BIND_SERVICE))
108662306a36Sopenharmony_ci			return -EACCES;
108762306a36Sopenharmony_ci	}
108862306a36Sopenharmony_ci
108962306a36Sopenharmony_ci	scope = sctp_scope(daddr);
109062306a36Sopenharmony_ci	asoc = sctp_association_new(ep, sk, scope, GFP_KERNEL);
109162306a36Sopenharmony_ci	if (!asoc)
109262306a36Sopenharmony_ci		return -ENOMEM;
109362306a36Sopenharmony_ci
109462306a36Sopenharmony_ci	err = sctp_assoc_set_bind_addr_from_ep(asoc, scope, GFP_KERNEL);
109562306a36Sopenharmony_ci	if (err < 0)
109662306a36Sopenharmony_ci		goto free;
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_ci	*tp = sctp_assoc_add_peer(asoc, daddr, GFP_KERNEL, SCTP_UNKNOWN);
109962306a36Sopenharmony_ci	if (!*tp) {
110062306a36Sopenharmony_ci		err = -ENOMEM;
110162306a36Sopenharmony_ci		goto free;
110262306a36Sopenharmony_ci	}
110362306a36Sopenharmony_ci
110462306a36Sopenharmony_ci	if (!init)
110562306a36Sopenharmony_ci		return 0;
110662306a36Sopenharmony_ci
110762306a36Sopenharmony_ci	if (init->sinit_num_ostreams) {
110862306a36Sopenharmony_ci		__u16 outcnt = init->sinit_num_ostreams;
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_ci		asoc->c.sinit_num_ostreams = outcnt;
111162306a36Sopenharmony_ci		/* outcnt has been changed, need to re-init stream */
111262306a36Sopenharmony_ci		err = sctp_stream_init(&asoc->stream, outcnt, 0, GFP_KERNEL);
111362306a36Sopenharmony_ci		if (err)
111462306a36Sopenharmony_ci			goto free;
111562306a36Sopenharmony_ci	}
111662306a36Sopenharmony_ci
111762306a36Sopenharmony_ci	if (init->sinit_max_instreams)
111862306a36Sopenharmony_ci		asoc->c.sinit_max_instreams = init->sinit_max_instreams;
111962306a36Sopenharmony_ci
112062306a36Sopenharmony_ci	if (init->sinit_max_attempts)
112162306a36Sopenharmony_ci		asoc->max_init_attempts = init->sinit_max_attempts;
112262306a36Sopenharmony_ci
112362306a36Sopenharmony_ci	if (init->sinit_max_init_timeo)
112462306a36Sopenharmony_ci		asoc->max_init_timeo =
112562306a36Sopenharmony_ci			msecs_to_jiffies(init->sinit_max_init_timeo);
112662306a36Sopenharmony_ci
112762306a36Sopenharmony_ci	return 0;
112862306a36Sopenharmony_cifree:
112962306a36Sopenharmony_ci	sctp_association_free(asoc);
113062306a36Sopenharmony_ci	return err;
113162306a36Sopenharmony_ci}
113262306a36Sopenharmony_ci
113362306a36Sopenharmony_cistatic int sctp_connect_add_peer(struct sctp_association *asoc,
113462306a36Sopenharmony_ci				 union sctp_addr *daddr, int addr_len)
113562306a36Sopenharmony_ci{
113662306a36Sopenharmony_ci	struct sctp_endpoint *ep = asoc->ep;
113762306a36Sopenharmony_ci	struct sctp_association *old;
113862306a36Sopenharmony_ci	struct sctp_transport *t;
113962306a36Sopenharmony_ci	int err;
114062306a36Sopenharmony_ci
114162306a36Sopenharmony_ci	err = sctp_verify_addr(ep->base.sk, daddr, addr_len);
114262306a36Sopenharmony_ci	if (err)
114362306a36Sopenharmony_ci		return err;
114462306a36Sopenharmony_ci
114562306a36Sopenharmony_ci	old = sctp_endpoint_lookup_assoc(ep, daddr, &t);
114662306a36Sopenharmony_ci	if (old && old != asoc)
114762306a36Sopenharmony_ci		return old->state >= SCTP_STATE_ESTABLISHED ? -EISCONN
114862306a36Sopenharmony_ci							    : -EALREADY;
114962306a36Sopenharmony_ci
115062306a36Sopenharmony_ci	if (sctp_endpoint_is_peeled_off(ep, daddr))
115162306a36Sopenharmony_ci		return -EADDRNOTAVAIL;
115262306a36Sopenharmony_ci
115362306a36Sopenharmony_ci	t = sctp_assoc_add_peer(asoc, daddr, GFP_KERNEL, SCTP_UNKNOWN);
115462306a36Sopenharmony_ci	if (!t)
115562306a36Sopenharmony_ci		return -ENOMEM;
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_ci	return 0;
115862306a36Sopenharmony_ci}
115962306a36Sopenharmony_ci
116062306a36Sopenharmony_ci/* __sctp_connect(struct sock* sk, struct sockaddr *kaddrs, int addrs_size)
116162306a36Sopenharmony_ci *
116262306a36Sopenharmony_ci * Common routine for handling connect() and sctp_connectx().
116362306a36Sopenharmony_ci * Connect will come in with just a single address.
116462306a36Sopenharmony_ci */
116562306a36Sopenharmony_cistatic int __sctp_connect(struct sock *sk, struct sockaddr *kaddrs,
116662306a36Sopenharmony_ci			  int addrs_size, int flags, sctp_assoc_t *assoc_id)
116762306a36Sopenharmony_ci{
116862306a36Sopenharmony_ci	struct sctp_sock *sp = sctp_sk(sk);
116962306a36Sopenharmony_ci	struct sctp_endpoint *ep = sp->ep;
117062306a36Sopenharmony_ci	struct sctp_transport *transport;
117162306a36Sopenharmony_ci	struct sctp_association *asoc;
117262306a36Sopenharmony_ci	void *addr_buf = kaddrs;
117362306a36Sopenharmony_ci	union sctp_addr *daddr;
117462306a36Sopenharmony_ci	struct sctp_af *af;
117562306a36Sopenharmony_ci	int walk_size, err;
117662306a36Sopenharmony_ci	long timeo;
117762306a36Sopenharmony_ci
117862306a36Sopenharmony_ci	if (sctp_sstate(sk, ESTABLISHED) || sctp_sstate(sk, CLOSING) ||
117962306a36Sopenharmony_ci	    (sctp_style(sk, TCP) && sctp_sstate(sk, LISTENING)))
118062306a36Sopenharmony_ci		return -EISCONN;
118162306a36Sopenharmony_ci
118262306a36Sopenharmony_ci	daddr = addr_buf;
118362306a36Sopenharmony_ci	af = sctp_get_af_specific(daddr->sa.sa_family);
118462306a36Sopenharmony_ci	if (!af || af->sockaddr_len > addrs_size)
118562306a36Sopenharmony_ci		return -EINVAL;
118662306a36Sopenharmony_ci
118762306a36Sopenharmony_ci	err = sctp_verify_addr(sk, daddr, af->sockaddr_len);
118862306a36Sopenharmony_ci	if (err)
118962306a36Sopenharmony_ci		return err;
119062306a36Sopenharmony_ci
119162306a36Sopenharmony_ci	asoc = sctp_endpoint_lookup_assoc(ep, daddr, &transport);
119262306a36Sopenharmony_ci	if (asoc)
119362306a36Sopenharmony_ci		return asoc->state >= SCTP_STATE_ESTABLISHED ? -EISCONN
119462306a36Sopenharmony_ci							     : -EALREADY;
119562306a36Sopenharmony_ci
119662306a36Sopenharmony_ci	err = sctp_connect_new_asoc(ep, daddr, NULL, &transport);
119762306a36Sopenharmony_ci	if (err)
119862306a36Sopenharmony_ci		return err;
119962306a36Sopenharmony_ci	asoc = transport->asoc;
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_ci	addr_buf += af->sockaddr_len;
120262306a36Sopenharmony_ci	walk_size = af->sockaddr_len;
120362306a36Sopenharmony_ci	while (walk_size < addrs_size) {
120462306a36Sopenharmony_ci		err = -EINVAL;
120562306a36Sopenharmony_ci		if (walk_size + sizeof(sa_family_t) > addrs_size)
120662306a36Sopenharmony_ci			goto out_free;
120762306a36Sopenharmony_ci
120862306a36Sopenharmony_ci		daddr = addr_buf;
120962306a36Sopenharmony_ci		af = sctp_get_af_specific(daddr->sa.sa_family);
121062306a36Sopenharmony_ci		if (!af || af->sockaddr_len + walk_size > addrs_size)
121162306a36Sopenharmony_ci			goto out_free;
121262306a36Sopenharmony_ci
121362306a36Sopenharmony_ci		if (asoc->peer.port != ntohs(daddr->v4.sin_port))
121462306a36Sopenharmony_ci			goto out_free;
121562306a36Sopenharmony_ci
121662306a36Sopenharmony_ci		err = sctp_connect_add_peer(asoc, daddr, af->sockaddr_len);
121762306a36Sopenharmony_ci		if (err)
121862306a36Sopenharmony_ci			goto out_free;
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_ci		addr_buf  += af->sockaddr_len;
122162306a36Sopenharmony_ci		walk_size += af->sockaddr_len;
122262306a36Sopenharmony_ci	}
122362306a36Sopenharmony_ci
122462306a36Sopenharmony_ci	/* In case the user of sctp_connectx() wants an association
122562306a36Sopenharmony_ci	 * id back, assign one now.
122662306a36Sopenharmony_ci	 */
122762306a36Sopenharmony_ci	if (assoc_id) {
122862306a36Sopenharmony_ci		err = sctp_assoc_set_id(asoc, GFP_KERNEL);
122962306a36Sopenharmony_ci		if (err < 0)
123062306a36Sopenharmony_ci			goto out_free;
123162306a36Sopenharmony_ci	}
123262306a36Sopenharmony_ci
123362306a36Sopenharmony_ci	err = sctp_primitive_ASSOCIATE(sock_net(sk), asoc, NULL);
123462306a36Sopenharmony_ci	if (err < 0)
123562306a36Sopenharmony_ci		goto out_free;
123662306a36Sopenharmony_ci
123762306a36Sopenharmony_ci	/* Initialize sk's dport and daddr for getpeername() */
123862306a36Sopenharmony_ci	inet_sk(sk)->inet_dport = htons(asoc->peer.port);
123962306a36Sopenharmony_ci	sp->pf->to_sk_daddr(daddr, sk);
124062306a36Sopenharmony_ci	sk->sk_err = 0;
124162306a36Sopenharmony_ci
124262306a36Sopenharmony_ci	if (assoc_id)
124362306a36Sopenharmony_ci		*assoc_id = asoc->assoc_id;
124462306a36Sopenharmony_ci
124562306a36Sopenharmony_ci	timeo = sock_sndtimeo(sk, flags & O_NONBLOCK);
124662306a36Sopenharmony_ci	return sctp_wait_for_connect(asoc, &timeo);
124762306a36Sopenharmony_ci
124862306a36Sopenharmony_ciout_free:
124962306a36Sopenharmony_ci	pr_debug("%s: took out_free path with asoc:%p kaddrs:%p err:%d\n",
125062306a36Sopenharmony_ci		 __func__, asoc, kaddrs, err);
125162306a36Sopenharmony_ci	sctp_association_free(asoc);
125262306a36Sopenharmony_ci	return err;
125362306a36Sopenharmony_ci}
125462306a36Sopenharmony_ci
125562306a36Sopenharmony_ci/* Helper for tunneling sctp_connectx() requests through sctp_setsockopt()
125662306a36Sopenharmony_ci *
125762306a36Sopenharmony_ci * API 8.9
125862306a36Sopenharmony_ci * int sctp_connectx(int sd, struct sockaddr *addrs, int addrcnt,
125962306a36Sopenharmony_ci * 			sctp_assoc_t *asoc);
126062306a36Sopenharmony_ci *
126162306a36Sopenharmony_ci * If sd is an IPv4 socket, the addresses passed must be IPv4 addresses.
126262306a36Sopenharmony_ci * If the sd is an IPv6 socket, the addresses passed can either be IPv4
126362306a36Sopenharmony_ci * or IPv6 addresses.
126462306a36Sopenharmony_ci *
126562306a36Sopenharmony_ci * A single address may be specified as INADDR_ANY or IN6ADDR_ANY, see
126662306a36Sopenharmony_ci * Section 3.1.2 for this usage.
126762306a36Sopenharmony_ci *
126862306a36Sopenharmony_ci * addrs is a pointer to an array of one or more socket addresses. Each
126962306a36Sopenharmony_ci * address is contained in its appropriate structure (i.e. struct
127062306a36Sopenharmony_ci * sockaddr_in or struct sockaddr_in6) the family of the address type
127162306a36Sopenharmony_ci * must be used to distengish the address length (note that this
127262306a36Sopenharmony_ci * representation is termed a "packed array" of addresses). The caller
127362306a36Sopenharmony_ci * specifies the number of addresses in the array with addrcnt.
127462306a36Sopenharmony_ci *
127562306a36Sopenharmony_ci * On success, sctp_connectx() returns 0. It also sets the assoc_id to
127662306a36Sopenharmony_ci * the association id of the new association.  On failure, sctp_connectx()
127762306a36Sopenharmony_ci * returns -1, and sets errno to the appropriate error code.  The assoc_id
127862306a36Sopenharmony_ci * is not touched by the kernel.
127962306a36Sopenharmony_ci *
128062306a36Sopenharmony_ci * For SCTP, the port given in each socket address must be the same, or
128162306a36Sopenharmony_ci * sctp_connectx() will fail, setting errno to EINVAL.
128262306a36Sopenharmony_ci *
128362306a36Sopenharmony_ci * An application can use sctp_connectx to initiate an association with
128462306a36Sopenharmony_ci * an endpoint that is multi-homed.  Much like sctp_bindx() this call
128562306a36Sopenharmony_ci * allows a caller to specify multiple addresses at which a peer can be
128662306a36Sopenharmony_ci * reached.  The way the SCTP stack uses the list of addresses to set up
128762306a36Sopenharmony_ci * the association is implementation dependent.  This function only
128862306a36Sopenharmony_ci * specifies that the stack will try to make use of all the addresses in
128962306a36Sopenharmony_ci * the list when needed.
129062306a36Sopenharmony_ci *
129162306a36Sopenharmony_ci * Note that the list of addresses passed in is only used for setting up
129262306a36Sopenharmony_ci * the association.  It does not necessarily equal the set of addresses
129362306a36Sopenharmony_ci * the peer uses for the resulting association.  If the caller wants to
129462306a36Sopenharmony_ci * find out the set of peer addresses, it must use sctp_getpaddrs() to
129562306a36Sopenharmony_ci * retrieve them after the association has been set up.
129662306a36Sopenharmony_ci *
129762306a36Sopenharmony_ci * Basically do nothing but copying the addresses from user to kernel
129862306a36Sopenharmony_ci * land and invoking either sctp_connectx(). This is used for tunneling
129962306a36Sopenharmony_ci * the sctp_connectx() request through sctp_setsockopt() from userspace.
130062306a36Sopenharmony_ci *
130162306a36Sopenharmony_ci * On exit there is no need to do sockfd_put(), sys_setsockopt() does
130262306a36Sopenharmony_ci * it.
130362306a36Sopenharmony_ci *
130462306a36Sopenharmony_ci * sk        The sk of the socket
130562306a36Sopenharmony_ci * addrs     The pointer to the addresses
130662306a36Sopenharmony_ci * addrssize Size of the addrs buffer
130762306a36Sopenharmony_ci *
130862306a36Sopenharmony_ci * Returns >=0 if ok, <0 errno code on error.
130962306a36Sopenharmony_ci */
131062306a36Sopenharmony_cistatic int __sctp_setsockopt_connectx(struct sock *sk, struct sockaddr *kaddrs,
131162306a36Sopenharmony_ci				      int addrs_size, sctp_assoc_t *assoc_id)
131262306a36Sopenharmony_ci{
131362306a36Sopenharmony_ci	int err = 0, flags = 0;
131462306a36Sopenharmony_ci
131562306a36Sopenharmony_ci	pr_debug("%s: sk:%p addrs:%p addrs_size:%d\n",
131662306a36Sopenharmony_ci		 __func__, sk, kaddrs, addrs_size);
131762306a36Sopenharmony_ci
131862306a36Sopenharmony_ci	/* make sure the 1st addr's sa_family is accessible later */
131962306a36Sopenharmony_ci	if (unlikely(addrs_size < sizeof(sa_family_t)))
132062306a36Sopenharmony_ci		return -EINVAL;
132162306a36Sopenharmony_ci
132262306a36Sopenharmony_ci	/* Allow security module to validate connectx addresses. */
132362306a36Sopenharmony_ci	err = security_sctp_bind_connect(sk, SCTP_SOCKOPT_CONNECTX,
132462306a36Sopenharmony_ci					 (struct sockaddr *)kaddrs,
132562306a36Sopenharmony_ci					  addrs_size);
132662306a36Sopenharmony_ci	if (err)
132762306a36Sopenharmony_ci		return err;
132862306a36Sopenharmony_ci
132962306a36Sopenharmony_ci	/* in-kernel sockets don't generally have a file allocated to them
133062306a36Sopenharmony_ci	 * if all they do is call sock_create_kern().
133162306a36Sopenharmony_ci	 */
133262306a36Sopenharmony_ci	if (sk->sk_socket->file)
133362306a36Sopenharmony_ci		flags = sk->sk_socket->file->f_flags;
133462306a36Sopenharmony_ci
133562306a36Sopenharmony_ci	return __sctp_connect(sk, kaddrs, addrs_size, flags, assoc_id);
133662306a36Sopenharmony_ci}
133762306a36Sopenharmony_ci
133862306a36Sopenharmony_ci/*
133962306a36Sopenharmony_ci * This is an older interface.  It's kept for backward compatibility
134062306a36Sopenharmony_ci * to the option that doesn't provide association id.
134162306a36Sopenharmony_ci */
134262306a36Sopenharmony_cistatic int sctp_setsockopt_connectx_old(struct sock *sk,
134362306a36Sopenharmony_ci					struct sockaddr *kaddrs,
134462306a36Sopenharmony_ci					int addrs_size)
134562306a36Sopenharmony_ci{
134662306a36Sopenharmony_ci	return __sctp_setsockopt_connectx(sk, kaddrs, addrs_size, NULL);
134762306a36Sopenharmony_ci}
134862306a36Sopenharmony_ci
134962306a36Sopenharmony_ci/*
135062306a36Sopenharmony_ci * New interface for the API.  The since the API is done with a socket
135162306a36Sopenharmony_ci * option, to make it simple we feed back the association id is as a return
135262306a36Sopenharmony_ci * indication to the call.  Error is always negative and association id is
135362306a36Sopenharmony_ci * always positive.
135462306a36Sopenharmony_ci */
135562306a36Sopenharmony_cistatic int sctp_setsockopt_connectx(struct sock *sk,
135662306a36Sopenharmony_ci				    struct sockaddr *kaddrs,
135762306a36Sopenharmony_ci				    int addrs_size)
135862306a36Sopenharmony_ci{
135962306a36Sopenharmony_ci	sctp_assoc_t assoc_id = 0;
136062306a36Sopenharmony_ci	int err = 0;
136162306a36Sopenharmony_ci
136262306a36Sopenharmony_ci	err = __sctp_setsockopt_connectx(sk, kaddrs, addrs_size, &assoc_id);
136362306a36Sopenharmony_ci
136462306a36Sopenharmony_ci	if (err)
136562306a36Sopenharmony_ci		return err;
136662306a36Sopenharmony_ci	else
136762306a36Sopenharmony_ci		return assoc_id;
136862306a36Sopenharmony_ci}
136962306a36Sopenharmony_ci
137062306a36Sopenharmony_ci/*
137162306a36Sopenharmony_ci * New (hopefully final) interface for the API.
137262306a36Sopenharmony_ci * We use the sctp_getaddrs_old structure so that use-space library
137362306a36Sopenharmony_ci * can avoid any unnecessary allocations. The only different part
137462306a36Sopenharmony_ci * is that we store the actual length of the address buffer into the
137562306a36Sopenharmony_ci * addrs_num structure member. That way we can re-use the existing
137662306a36Sopenharmony_ci * code.
137762306a36Sopenharmony_ci */
137862306a36Sopenharmony_ci#ifdef CONFIG_COMPAT
137962306a36Sopenharmony_cistruct compat_sctp_getaddrs_old {
138062306a36Sopenharmony_ci	sctp_assoc_t	assoc_id;
138162306a36Sopenharmony_ci	s32		addr_num;
138262306a36Sopenharmony_ci	compat_uptr_t	addrs;		/* struct sockaddr * */
138362306a36Sopenharmony_ci};
138462306a36Sopenharmony_ci#endif
138562306a36Sopenharmony_ci
138662306a36Sopenharmony_cistatic int sctp_getsockopt_connectx3(struct sock *sk, int len,
138762306a36Sopenharmony_ci				     char __user *optval,
138862306a36Sopenharmony_ci				     int __user *optlen)
138962306a36Sopenharmony_ci{
139062306a36Sopenharmony_ci	struct sctp_getaddrs_old param;
139162306a36Sopenharmony_ci	sctp_assoc_t assoc_id = 0;
139262306a36Sopenharmony_ci	struct sockaddr *kaddrs;
139362306a36Sopenharmony_ci	int err = 0;
139462306a36Sopenharmony_ci
139562306a36Sopenharmony_ci#ifdef CONFIG_COMPAT
139662306a36Sopenharmony_ci	if (in_compat_syscall()) {
139762306a36Sopenharmony_ci		struct compat_sctp_getaddrs_old param32;
139862306a36Sopenharmony_ci
139962306a36Sopenharmony_ci		if (len < sizeof(param32))
140062306a36Sopenharmony_ci			return -EINVAL;
140162306a36Sopenharmony_ci		if (copy_from_user(&param32, optval, sizeof(param32)))
140262306a36Sopenharmony_ci			return -EFAULT;
140362306a36Sopenharmony_ci
140462306a36Sopenharmony_ci		param.assoc_id = param32.assoc_id;
140562306a36Sopenharmony_ci		param.addr_num = param32.addr_num;
140662306a36Sopenharmony_ci		param.addrs = compat_ptr(param32.addrs);
140762306a36Sopenharmony_ci	} else
140862306a36Sopenharmony_ci#endif
140962306a36Sopenharmony_ci	{
141062306a36Sopenharmony_ci		if (len < sizeof(param))
141162306a36Sopenharmony_ci			return -EINVAL;
141262306a36Sopenharmony_ci		if (copy_from_user(&param, optval, sizeof(param)))
141362306a36Sopenharmony_ci			return -EFAULT;
141462306a36Sopenharmony_ci	}
141562306a36Sopenharmony_ci
141662306a36Sopenharmony_ci	kaddrs = memdup_user(param.addrs, param.addr_num);
141762306a36Sopenharmony_ci	if (IS_ERR(kaddrs))
141862306a36Sopenharmony_ci		return PTR_ERR(kaddrs);
141962306a36Sopenharmony_ci
142062306a36Sopenharmony_ci	err = __sctp_setsockopt_connectx(sk, kaddrs, param.addr_num, &assoc_id);
142162306a36Sopenharmony_ci	kfree(kaddrs);
142262306a36Sopenharmony_ci	if (err == 0 || err == -EINPROGRESS) {
142362306a36Sopenharmony_ci		if (copy_to_user(optval, &assoc_id, sizeof(assoc_id)))
142462306a36Sopenharmony_ci			return -EFAULT;
142562306a36Sopenharmony_ci		if (put_user(sizeof(assoc_id), optlen))
142662306a36Sopenharmony_ci			return -EFAULT;
142762306a36Sopenharmony_ci	}
142862306a36Sopenharmony_ci
142962306a36Sopenharmony_ci	return err;
143062306a36Sopenharmony_ci}
143162306a36Sopenharmony_ci
143262306a36Sopenharmony_ci/* API 3.1.4 close() - UDP Style Syntax
143362306a36Sopenharmony_ci * Applications use close() to perform graceful shutdown (as described in
143462306a36Sopenharmony_ci * Section 10.1 of [SCTP]) on ALL the associations currently represented
143562306a36Sopenharmony_ci * by a UDP-style socket.
143662306a36Sopenharmony_ci *
143762306a36Sopenharmony_ci * The syntax is
143862306a36Sopenharmony_ci *
143962306a36Sopenharmony_ci *   ret = close(int sd);
144062306a36Sopenharmony_ci *
144162306a36Sopenharmony_ci *   sd      - the socket descriptor of the associations to be closed.
144262306a36Sopenharmony_ci *
144362306a36Sopenharmony_ci * To gracefully shutdown a specific association represented by the
144462306a36Sopenharmony_ci * UDP-style socket, an application should use the sendmsg() call,
144562306a36Sopenharmony_ci * passing no user data, but including the appropriate flag in the
144662306a36Sopenharmony_ci * ancillary data (see Section xxxx).
144762306a36Sopenharmony_ci *
144862306a36Sopenharmony_ci * If sd in the close() call is a branched-off socket representing only
144962306a36Sopenharmony_ci * one association, the shutdown is performed on that association only.
145062306a36Sopenharmony_ci *
145162306a36Sopenharmony_ci * 4.1.6 close() - TCP Style Syntax
145262306a36Sopenharmony_ci *
145362306a36Sopenharmony_ci * Applications use close() to gracefully close down an association.
145462306a36Sopenharmony_ci *
145562306a36Sopenharmony_ci * The syntax is:
145662306a36Sopenharmony_ci *
145762306a36Sopenharmony_ci *    int close(int sd);
145862306a36Sopenharmony_ci *
145962306a36Sopenharmony_ci *      sd      - the socket descriptor of the association to be closed.
146062306a36Sopenharmony_ci *
146162306a36Sopenharmony_ci * After an application calls close() on a socket descriptor, no further
146262306a36Sopenharmony_ci * socket operations will succeed on that descriptor.
146362306a36Sopenharmony_ci *
146462306a36Sopenharmony_ci * API 7.1.4 SO_LINGER
146562306a36Sopenharmony_ci *
146662306a36Sopenharmony_ci * An application using the TCP-style socket can use this option to
146762306a36Sopenharmony_ci * perform the SCTP ABORT primitive.  The linger option structure is:
146862306a36Sopenharmony_ci *
146962306a36Sopenharmony_ci *  struct  linger {
147062306a36Sopenharmony_ci *     int     l_onoff;                // option on/off
147162306a36Sopenharmony_ci *     int     l_linger;               // linger time
147262306a36Sopenharmony_ci * };
147362306a36Sopenharmony_ci *
147462306a36Sopenharmony_ci * To enable the option, set l_onoff to 1.  If the l_linger value is set
147562306a36Sopenharmony_ci * to 0, calling close() is the same as the ABORT primitive.  If the
147662306a36Sopenharmony_ci * value is set to a negative value, the setsockopt() call will return
147762306a36Sopenharmony_ci * an error.  If the value is set to a positive value linger_time, the
147862306a36Sopenharmony_ci * close() can be blocked for at most linger_time ms.  If the graceful
147962306a36Sopenharmony_ci * shutdown phase does not finish during this period, close() will
148062306a36Sopenharmony_ci * return but the graceful shutdown phase continues in the system.
148162306a36Sopenharmony_ci */
148262306a36Sopenharmony_cistatic void sctp_close(struct sock *sk, long timeout)
148362306a36Sopenharmony_ci{
148462306a36Sopenharmony_ci	struct net *net = sock_net(sk);
148562306a36Sopenharmony_ci	struct sctp_endpoint *ep;
148662306a36Sopenharmony_ci	struct sctp_association *asoc;
148762306a36Sopenharmony_ci	struct list_head *pos, *temp;
148862306a36Sopenharmony_ci	unsigned int data_was_unread;
148962306a36Sopenharmony_ci
149062306a36Sopenharmony_ci	pr_debug("%s: sk:%p, timeout:%ld\n", __func__, sk, timeout);
149162306a36Sopenharmony_ci
149262306a36Sopenharmony_ci	lock_sock_nested(sk, SINGLE_DEPTH_NESTING);
149362306a36Sopenharmony_ci	sk->sk_shutdown = SHUTDOWN_MASK;
149462306a36Sopenharmony_ci	inet_sk_set_state(sk, SCTP_SS_CLOSING);
149562306a36Sopenharmony_ci
149662306a36Sopenharmony_ci	ep = sctp_sk(sk)->ep;
149762306a36Sopenharmony_ci
149862306a36Sopenharmony_ci	/* Clean up any skbs sitting on the receive queue.  */
149962306a36Sopenharmony_ci	data_was_unread = sctp_queue_purge_ulpevents(&sk->sk_receive_queue);
150062306a36Sopenharmony_ci	data_was_unread += sctp_queue_purge_ulpevents(&sctp_sk(sk)->pd_lobby);
150162306a36Sopenharmony_ci
150262306a36Sopenharmony_ci	/* Walk all associations on an endpoint.  */
150362306a36Sopenharmony_ci	list_for_each_safe(pos, temp, &ep->asocs) {
150462306a36Sopenharmony_ci		asoc = list_entry(pos, struct sctp_association, asocs);
150562306a36Sopenharmony_ci
150662306a36Sopenharmony_ci		if (sctp_style(sk, TCP)) {
150762306a36Sopenharmony_ci			/* A closed association can still be in the list if
150862306a36Sopenharmony_ci			 * it belongs to a TCP-style listening socket that is
150962306a36Sopenharmony_ci			 * not yet accepted. If so, free it. If not, send an
151062306a36Sopenharmony_ci			 * ABORT or SHUTDOWN based on the linger options.
151162306a36Sopenharmony_ci			 */
151262306a36Sopenharmony_ci			if (sctp_state(asoc, CLOSED)) {
151362306a36Sopenharmony_ci				sctp_association_free(asoc);
151462306a36Sopenharmony_ci				continue;
151562306a36Sopenharmony_ci			}
151662306a36Sopenharmony_ci		}
151762306a36Sopenharmony_ci
151862306a36Sopenharmony_ci		if (data_was_unread || !skb_queue_empty(&asoc->ulpq.lobby) ||
151962306a36Sopenharmony_ci		    !skb_queue_empty(&asoc->ulpq.reasm) ||
152062306a36Sopenharmony_ci		    !skb_queue_empty(&asoc->ulpq.reasm_uo) ||
152162306a36Sopenharmony_ci		    (sock_flag(sk, SOCK_LINGER) && !sk->sk_lingertime)) {
152262306a36Sopenharmony_ci			struct sctp_chunk *chunk;
152362306a36Sopenharmony_ci
152462306a36Sopenharmony_ci			chunk = sctp_make_abort_user(asoc, NULL, 0);
152562306a36Sopenharmony_ci			sctp_primitive_ABORT(net, asoc, chunk);
152662306a36Sopenharmony_ci		} else
152762306a36Sopenharmony_ci			sctp_primitive_SHUTDOWN(net, asoc, NULL);
152862306a36Sopenharmony_ci	}
152962306a36Sopenharmony_ci
153062306a36Sopenharmony_ci	/* On a TCP-style socket, block for at most linger_time if set. */
153162306a36Sopenharmony_ci	if (sctp_style(sk, TCP) && timeout)
153262306a36Sopenharmony_ci		sctp_wait_for_close(sk, timeout);
153362306a36Sopenharmony_ci
153462306a36Sopenharmony_ci	/* This will run the backlog queue.  */
153562306a36Sopenharmony_ci	release_sock(sk);
153662306a36Sopenharmony_ci
153762306a36Sopenharmony_ci	/* Supposedly, no process has access to the socket, but
153862306a36Sopenharmony_ci	 * the net layers still may.
153962306a36Sopenharmony_ci	 * Also, sctp_destroy_sock() needs to be called with addr_wq_lock
154062306a36Sopenharmony_ci	 * held and that should be grabbed before socket lock.
154162306a36Sopenharmony_ci	 */
154262306a36Sopenharmony_ci	spin_lock_bh(&net->sctp.addr_wq_lock);
154362306a36Sopenharmony_ci	bh_lock_sock_nested(sk);
154462306a36Sopenharmony_ci
154562306a36Sopenharmony_ci	/* Hold the sock, since sk_common_release() will put sock_put()
154662306a36Sopenharmony_ci	 * and we have just a little more cleanup.
154762306a36Sopenharmony_ci	 */
154862306a36Sopenharmony_ci	sock_hold(sk);
154962306a36Sopenharmony_ci	sk_common_release(sk);
155062306a36Sopenharmony_ci
155162306a36Sopenharmony_ci	bh_unlock_sock(sk);
155262306a36Sopenharmony_ci	spin_unlock_bh(&net->sctp.addr_wq_lock);
155362306a36Sopenharmony_ci
155462306a36Sopenharmony_ci	sock_put(sk);
155562306a36Sopenharmony_ci
155662306a36Sopenharmony_ci	SCTP_DBG_OBJCNT_DEC(sock);
155762306a36Sopenharmony_ci}
155862306a36Sopenharmony_ci
155962306a36Sopenharmony_ci/* Handle EPIPE error. */
156062306a36Sopenharmony_cistatic int sctp_error(struct sock *sk, int flags, int err)
156162306a36Sopenharmony_ci{
156262306a36Sopenharmony_ci	if (err == -EPIPE)
156362306a36Sopenharmony_ci		err = sock_error(sk) ? : -EPIPE;
156462306a36Sopenharmony_ci	if (err == -EPIPE && !(flags & MSG_NOSIGNAL))
156562306a36Sopenharmony_ci		send_sig(SIGPIPE, current, 0);
156662306a36Sopenharmony_ci	return err;
156762306a36Sopenharmony_ci}
156862306a36Sopenharmony_ci
156962306a36Sopenharmony_ci/* API 3.1.3 sendmsg() - UDP Style Syntax
157062306a36Sopenharmony_ci *
157162306a36Sopenharmony_ci * An application uses sendmsg() and recvmsg() calls to transmit data to
157262306a36Sopenharmony_ci * and receive data from its peer.
157362306a36Sopenharmony_ci *
157462306a36Sopenharmony_ci *  ssize_t sendmsg(int socket, const struct msghdr *message,
157562306a36Sopenharmony_ci *                  int flags);
157662306a36Sopenharmony_ci *
157762306a36Sopenharmony_ci *  socket  - the socket descriptor of the endpoint.
157862306a36Sopenharmony_ci *  message - pointer to the msghdr structure which contains a single
157962306a36Sopenharmony_ci *            user message and possibly some ancillary data.
158062306a36Sopenharmony_ci *
158162306a36Sopenharmony_ci *            See Section 5 for complete description of the data
158262306a36Sopenharmony_ci *            structures.
158362306a36Sopenharmony_ci *
158462306a36Sopenharmony_ci *  flags   - flags sent or received with the user message, see Section
158562306a36Sopenharmony_ci *            5 for complete description of the flags.
158662306a36Sopenharmony_ci *
158762306a36Sopenharmony_ci * Note:  This function could use a rewrite especially when explicit
158862306a36Sopenharmony_ci * connect support comes in.
158962306a36Sopenharmony_ci */
159062306a36Sopenharmony_ci/* BUG:  We do not implement the equivalent of sk_stream_wait_memory(). */
159162306a36Sopenharmony_ci
159262306a36Sopenharmony_cistatic int sctp_msghdr_parse(const struct msghdr *msg,
159362306a36Sopenharmony_ci			     struct sctp_cmsgs *cmsgs);
159462306a36Sopenharmony_ci
159562306a36Sopenharmony_cistatic int sctp_sendmsg_parse(struct sock *sk, struct sctp_cmsgs *cmsgs,
159662306a36Sopenharmony_ci			      struct sctp_sndrcvinfo *srinfo,
159762306a36Sopenharmony_ci			      const struct msghdr *msg, size_t msg_len)
159862306a36Sopenharmony_ci{
159962306a36Sopenharmony_ci	__u16 sflags;
160062306a36Sopenharmony_ci	int err;
160162306a36Sopenharmony_ci
160262306a36Sopenharmony_ci	if (sctp_sstate(sk, LISTENING) && sctp_style(sk, TCP))
160362306a36Sopenharmony_ci		return -EPIPE;
160462306a36Sopenharmony_ci
160562306a36Sopenharmony_ci	if (msg_len > sk->sk_sndbuf)
160662306a36Sopenharmony_ci		return -EMSGSIZE;
160762306a36Sopenharmony_ci
160862306a36Sopenharmony_ci	memset(cmsgs, 0, sizeof(*cmsgs));
160962306a36Sopenharmony_ci	err = sctp_msghdr_parse(msg, cmsgs);
161062306a36Sopenharmony_ci	if (err) {
161162306a36Sopenharmony_ci		pr_debug("%s: msghdr parse err:%x\n", __func__, err);
161262306a36Sopenharmony_ci		return err;
161362306a36Sopenharmony_ci	}
161462306a36Sopenharmony_ci
161562306a36Sopenharmony_ci	memset(srinfo, 0, sizeof(*srinfo));
161662306a36Sopenharmony_ci	if (cmsgs->srinfo) {
161762306a36Sopenharmony_ci		srinfo->sinfo_stream = cmsgs->srinfo->sinfo_stream;
161862306a36Sopenharmony_ci		srinfo->sinfo_flags = cmsgs->srinfo->sinfo_flags;
161962306a36Sopenharmony_ci		srinfo->sinfo_ppid = cmsgs->srinfo->sinfo_ppid;
162062306a36Sopenharmony_ci		srinfo->sinfo_context = cmsgs->srinfo->sinfo_context;
162162306a36Sopenharmony_ci		srinfo->sinfo_assoc_id = cmsgs->srinfo->sinfo_assoc_id;
162262306a36Sopenharmony_ci		srinfo->sinfo_timetolive = cmsgs->srinfo->sinfo_timetolive;
162362306a36Sopenharmony_ci	}
162462306a36Sopenharmony_ci
162562306a36Sopenharmony_ci	if (cmsgs->sinfo) {
162662306a36Sopenharmony_ci		srinfo->sinfo_stream = cmsgs->sinfo->snd_sid;
162762306a36Sopenharmony_ci		srinfo->sinfo_flags = cmsgs->sinfo->snd_flags;
162862306a36Sopenharmony_ci		srinfo->sinfo_ppid = cmsgs->sinfo->snd_ppid;
162962306a36Sopenharmony_ci		srinfo->sinfo_context = cmsgs->sinfo->snd_context;
163062306a36Sopenharmony_ci		srinfo->sinfo_assoc_id = cmsgs->sinfo->snd_assoc_id;
163162306a36Sopenharmony_ci	}
163262306a36Sopenharmony_ci
163362306a36Sopenharmony_ci	if (cmsgs->prinfo) {
163462306a36Sopenharmony_ci		srinfo->sinfo_timetolive = cmsgs->prinfo->pr_value;
163562306a36Sopenharmony_ci		SCTP_PR_SET_POLICY(srinfo->sinfo_flags,
163662306a36Sopenharmony_ci				   cmsgs->prinfo->pr_policy);
163762306a36Sopenharmony_ci	}
163862306a36Sopenharmony_ci
163962306a36Sopenharmony_ci	sflags = srinfo->sinfo_flags;
164062306a36Sopenharmony_ci	if (!sflags && msg_len)
164162306a36Sopenharmony_ci		return 0;
164262306a36Sopenharmony_ci
164362306a36Sopenharmony_ci	if (sctp_style(sk, TCP) && (sflags & (SCTP_EOF | SCTP_ABORT)))
164462306a36Sopenharmony_ci		return -EINVAL;
164562306a36Sopenharmony_ci
164662306a36Sopenharmony_ci	if (((sflags & SCTP_EOF) && msg_len > 0) ||
164762306a36Sopenharmony_ci	    (!(sflags & (SCTP_EOF | SCTP_ABORT)) && msg_len == 0))
164862306a36Sopenharmony_ci		return -EINVAL;
164962306a36Sopenharmony_ci
165062306a36Sopenharmony_ci	if ((sflags & SCTP_ADDR_OVER) && !msg->msg_name)
165162306a36Sopenharmony_ci		return -EINVAL;
165262306a36Sopenharmony_ci
165362306a36Sopenharmony_ci	return 0;
165462306a36Sopenharmony_ci}
165562306a36Sopenharmony_ci
165662306a36Sopenharmony_cistatic int sctp_sendmsg_new_asoc(struct sock *sk, __u16 sflags,
165762306a36Sopenharmony_ci				 struct sctp_cmsgs *cmsgs,
165862306a36Sopenharmony_ci				 union sctp_addr *daddr,
165962306a36Sopenharmony_ci				 struct sctp_transport **tp)
166062306a36Sopenharmony_ci{
166162306a36Sopenharmony_ci	struct sctp_endpoint *ep = sctp_sk(sk)->ep;
166262306a36Sopenharmony_ci	struct sctp_association *asoc;
166362306a36Sopenharmony_ci	struct cmsghdr *cmsg;
166462306a36Sopenharmony_ci	__be32 flowinfo = 0;
166562306a36Sopenharmony_ci	struct sctp_af *af;
166662306a36Sopenharmony_ci	int err;
166762306a36Sopenharmony_ci
166862306a36Sopenharmony_ci	*tp = NULL;
166962306a36Sopenharmony_ci
167062306a36Sopenharmony_ci	if (sflags & (SCTP_EOF | SCTP_ABORT))
167162306a36Sopenharmony_ci		return -EINVAL;
167262306a36Sopenharmony_ci
167362306a36Sopenharmony_ci	if (sctp_style(sk, TCP) && (sctp_sstate(sk, ESTABLISHED) ||
167462306a36Sopenharmony_ci				    sctp_sstate(sk, CLOSING)))
167562306a36Sopenharmony_ci		return -EADDRNOTAVAIL;
167662306a36Sopenharmony_ci
167762306a36Sopenharmony_ci	/* Label connection socket for first association 1-to-many
167862306a36Sopenharmony_ci	 * style for client sequence socket()->sendmsg(). This
167962306a36Sopenharmony_ci	 * needs to be done before sctp_assoc_add_peer() as that will
168062306a36Sopenharmony_ci	 * set up the initial packet that needs to account for any
168162306a36Sopenharmony_ci	 * security ip options (CIPSO/CALIPSO) added to the packet.
168262306a36Sopenharmony_ci	 */
168362306a36Sopenharmony_ci	af = sctp_get_af_specific(daddr->sa.sa_family);
168462306a36Sopenharmony_ci	if (!af)
168562306a36Sopenharmony_ci		return -EINVAL;
168662306a36Sopenharmony_ci	err = security_sctp_bind_connect(sk, SCTP_SENDMSG_CONNECT,
168762306a36Sopenharmony_ci					 (struct sockaddr *)daddr,
168862306a36Sopenharmony_ci					 af->sockaddr_len);
168962306a36Sopenharmony_ci	if (err < 0)
169062306a36Sopenharmony_ci		return err;
169162306a36Sopenharmony_ci
169262306a36Sopenharmony_ci	err = sctp_connect_new_asoc(ep, daddr, cmsgs->init, tp);
169362306a36Sopenharmony_ci	if (err)
169462306a36Sopenharmony_ci		return err;
169562306a36Sopenharmony_ci	asoc = (*tp)->asoc;
169662306a36Sopenharmony_ci
169762306a36Sopenharmony_ci	if (!cmsgs->addrs_msg)
169862306a36Sopenharmony_ci		return 0;
169962306a36Sopenharmony_ci
170062306a36Sopenharmony_ci	if (daddr->sa.sa_family == AF_INET6)
170162306a36Sopenharmony_ci		flowinfo = daddr->v6.sin6_flowinfo;
170262306a36Sopenharmony_ci
170362306a36Sopenharmony_ci	/* sendv addr list parse */
170462306a36Sopenharmony_ci	for_each_cmsghdr(cmsg, cmsgs->addrs_msg) {
170562306a36Sopenharmony_ci		union sctp_addr _daddr;
170662306a36Sopenharmony_ci		int dlen;
170762306a36Sopenharmony_ci
170862306a36Sopenharmony_ci		if (cmsg->cmsg_level != IPPROTO_SCTP ||
170962306a36Sopenharmony_ci		    (cmsg->cmsg_type != SCTP_DSTADDRV4 &&
171062306a36Sopenharmony_ci		     cmsg->cmsg_type != SCTP_DSTADDRV6))
171162306a36Sopenharmony_ci			continue;
171262306a36Sopenharmony_ci
171362306a36Sopenharmony_ci		daddr = &_daddr;
171462306a36Sopenharmony_ci		memset(daddr, 0, sizeof(*daddr));
171562306a36Sopenharmony_ci		dlen = cmsg->cmsg_len - sizeof(struct cmsghdr);
171662306a36Sopenharmony_ci		if (cmsg->cmsg_type == SCTP_DSTADDRV4) {
171762306a36Sopenharmony_ci			if (dlen < sizeof(struct in_addr)) {
171862306a36Sopenharmony_ci				err = -EINVAL;
171962306a36Sopenharmony_ci				goto free;
172062306a36Sopenharmony_ci			}
172162306a36Sopenharmony_ci
172262306a36Sopenharmony_ci			dlen = sizeof(struct in_addr);
172362306a36Sopenharmony_ci			daddr->v4.sin_family = AF_INET;
172462306a36Sopenharmony_ci			daddr->v4.sin_port = htons(asoc->peer.port);
172562306a36Sopenharmony_ci			memcpy(&daddr->v4.sin_addr, CMSG_DATA(cmsg), dlen);
172662306a36Sopenharmony_ci		} else {
172762306a36Sopenharmony_ci			if (dlen < sizeof(struct in6_addr)) {
172862306a36Sopenharmony_ci				err = -EINVAL;
172962306a36Sopenharmony_ci				goto free;
173062306a36Sopenharmony_ci			}
173162306a36Sopenharmony_ci
173262306a36Sopenharmony_ci			dlen = sizeof(struct in6_addr);
173362306a36Sopenharmony_ci			daddr->v6.sin6_flowinfo = flowinfo;
173462306a36Sopenharmony_ci			daddr->v6.sin6_family = AF_INET6;
173562306a36Sopenharmony_ci			daddr->v6.sin6_port = htons(asoc->peer.port);
173662306a36Sopenharmony_ci			memcpy(&daddr->v6.sin6_addr, CMSG_DATA(cmsg), dlen);
173762306a36Sopenharmony_ci		}
173862306a36Sopenharmony_ci
173962306a36Sopenharmony_ci		err = sctp_connect_add_peer(asoc, daddr, sizeof(*daddr));
174062306a36Sopenharmony_ci		if (err)
174162306a36Sopenharmony_ci			goto free;
174262306a36Sopenharmony_ci	}
174362306a36Sopenharmony_ci
174462306a36Sopenharmony_ci	return 0;
174562306a36Sopenharmony_ci
174662306a36Sopenharmony_cifree:
174762306a36Sopenharmony_ci	sctp_association_free(asoc);
174862306a36Sopenharmony_ci	return err;
174962306a36Sopenharmony_ci}
175062306a36Sopenharmony_ci
175162306a36Sopenharmony_cistatic int sctp_sendmsg_check_sflags(struct sctp_association *asoc,
175262306a36Sopenharmony_ci				     __u16 sflags, struct msghdr *msg,
175362306a36Sopenharmony_ci				     size_t msg_len)
175462306a36Sopenharmony_ci{
175562306a36Sopenharmony_ci	struct sock *sk = asoc->base.sk;
175662306a36Sopenharmony_ci	struct net *net = sock_net(sk);
175762306a36Sopenharmony_ci
175862306a36Sopenharmony_ci	if (sctp_state(asoc, CLOSED) && sctp_style(sk, TCP))
175962306a36Sopenharmony_ci		return -EPIPE;
176062306a36Sopenharmony_ci
176162306a36Sopenharmony_ci	if ((sflags & SCTP_SENDALL) && sctp_style(sk, UDP) &&
176262306a36Sopenharmony_ci	    !sctp_state(asoc, ESTABLISHED))
176362306a36Sopenharmony_ci		return 0;
176462306a36Sopenharmony_ci
176562306a36Sopenharmony_ci	if (sflags & SCTP_EOF) {
176662306a36Sopenharmony_ci		pr_debug("%s: shutting down association:%p\n", __func__, asoc);
176762306a36Sopenharmony_ci		sctp_primitive_SHUTDOWN(net, asoc, NULL);
176862306a36Sopenharmony_ci
176962306a36Sopenharmony_ci		return 0;
177062306a36Sopenharmony_ci	}
177162306a36Sopenharmony_ci
177262306a36Sopenharmony_ci	if (sflags & SCTP_ABORT) {
177362306a36Sopenharmony_ci		struct sctp_chunk *chunk;
177462306a36Sopenharmony_ci
177562306a36Sopenharmony_ci		chunk = sctp_make_abort_user(asoc, msg, msg_len);
177662306a36Sopenharmony_ci		if (!chunk)
177762306a36Sopenharmony_ci			return -ENOMEM;
177862306a36Sopenharmony_ci
177962306a36Sopenharmony_ci		pr_debug("%s: aborting association:%p\n", __func__, asoc);
178062306a36Sopenharmony_ci		sctp_primitive_ABORT(net, asoc, chunk);
178162306a36Sopenharmony_ci		iov_iter_revert(&msg->msg_iter, msg_len);
178262306a36Sopenharmony_ci
178362306a36Sopenharmony_ci		return 0;
178462306a36Sopenharmony_ci	}
178562306a36Sopenharmony_ci
178662306a36Sopenharmony_ci	return 1;
178762306a36Sopenharmony_ci}
178862306a36Sopenharmony_ci
178962306a36Sopenharmony_cistatic int sctp_sendmsg_to_asoc(struct sctp_association *asoc,
179062306a36Sopenharmony_ci				struct msghdr *msg, size_t msg_len,
179162306a36Sopenharmony_ci				struct sctp_transport *transport,
179262306a36Sopenharmony_ci				struct sctp_sndrcvinfo *sinfo)
179362306a36Sopenharmony_ci{
179462306a36Sopenharmony_ci	struct sock *sk = asoc->base.sk;
179562306a36Sopenharmony_ci	struct sctp_sock *sp = sctp_sk(sk);
179662306a36Sopenharmony_ci	struct net *net = sock_net(sk);
179762306a36Sopenharmony_ci	struct sctp_datamsg *datamsg;
179862306a36Sopenharmony_ci	bool wait_connect = false;
179962306a36Sopenharmony_ci	struct sctp_chunk *chunk;
180062306a36Sopenharmony_ci	long timeo;
180162306a36Sopenharmony_ci	int err;
180262306a36Sopenharmony_ci
180362306a36Sopenharmony_ci	if (sinfo->sinfo_stream >= asoc->stream.outcnt) {
180462306a36Sopenharmony_ci		err = -EINVAL;
180562306a36Sopenharmony_ci		goto err;
180662306a36Sopenharmony_ci	}
180762306a36Sopenharmony_ci
180862306a36Sopenharmony_ci	if (unlikely(!SCTP_SO(&asoc->stream, sinfo->sinfo_stream)->ext)) {
180962306a36Sopenharmony_ci		err = sctp_stream_init_ext(&asoc->stream, sinfo->sinfo_stream);
181062306a36Sopenharmony_ci		if (err)
181162306a36Sopenharmony_ci			goto err;
181262306a36Sopenharmony_ci	}
181362306a36Sopenharmony_ci
181462306a36Sopenharmony_ci	if (sp->disable_fragments && msg_len > asoc->frag_point) {
181562306a36Sopenharmony_ci		err = -EMSGSIZE;
181662306a36Sopenharmony_ci		goto err;
181762306a36Sopenharmony_ci	}
181862306a36Sopenharmony_ci
181962306a36Sopenharmony_ci	if (asoc->pmtu_pending) {
182062306a36Sopenharmony_ci		if (sp->param_flags & SPP_PMTUD_ENABLE)
182162306a36Sopenharmony_ci			sctp_assoc_sync_pmtu(asoc);
182262306a36Sopenharmony_ci		asoc->pmtu_pending = 0;
182362306a36Sopenharmony_ci	}
182462306a36Sopenharmony_ci
182562306a36Sopenharmony_ci	if (sctp_wspace(asoc) < (int)msg_len)
182662306a36Sopenharmony_ci		sctp_prsctp_prune(asoc, sinfo, msg_len - sctp_wspace(asoc));
182762306a36Sopenharmony_ci
182862306a36Sopenharmony_ci	if (sctp_wspace(asoc) <= 0 || !sk_wmem_schedule(sk, msg_len)) {
182962306a36Sopenharmony_ci		timeo = sock_sndtimeo(sk, msg->msg_flags & MSG_DONTWAIT);
183062306a36Sopenharmony_ci		err = sctp_wait_for_sndbuf(asoc, &timeo, msg_len);
183162306a36Sopenharmony_ci		if (err)
183262306a36Sopenharmony_ci			goto err;
183362306a36Sopenharmony_ci		if (unlikely(sinfo->sinfo_stream >= asoc->stream.outcnt)) {
183462306a36Sopenharmony_ci			err = -EINVAL;
183562306a36Sopenharmony_ci			goto err;
183662306a36Sopenharmony_ci		}
183762306a36Sopenharmony_ci	}
183862306a36Sopenharmony_ci
183962306a36Sopenharmony_ci	if (sctp_state(asoc, CLOSED)) {
184062306a36Sopenharmony_ci		err = sctp_primitive_ASSOCIATE(net, asoc, NULL);
184162306a36Sopenharmony_ci		if (err)
184262306a36Sopenharmony_ci			goto err;
184362306a36Sopenharmony_ci
184462306a36Sopenharmony_ci		if (asoc->ep->intl_enable) {
184562306a36Sopenharmony_ci			timeo = sock_sndtimeo(sk, 0);
184662306a36Sopenharmony_ci			err = sctp_wait_for_connect(asoc, &timeo);
184762306a36Sopenharmony_ci			if (err) {
184862306a36Sopenharmony_ci				err = -ESRCH;
184962306a36Sopenharmony_ci				goto err;
185062306a36Sopenharmony_ci			}
185162306a36Sopenharmony_ci		} else {
185262306a36Sopenharmony_ci			wait_connect = true;
185362306a36Sopenharmony_ci		}
185462306a36Sopenharmony_ci
185562306a36Sopenharmony_ci		pr_debug("%s: we associated primitively\n", __func__);
185662306a36Sopenharmony_ci	}
185762306a36Sopenharmony_ci
185862306a36Sopenharmony_ci	datamsg = sctp_datamsg_from_user(asoc, sinfo, &msg->msg_iter);
185962306a36Sopenharmony_ci	if (IS_ERR(datamsg)) {
186062306a36Sopenharmony_ci		err = PTR_ERR(datamsg);
186162306a36Sopenharmony_ci		goto err;
186262306a36Sopenharmony_ci	}
186362306a36Sopenharmony_ci
186462306a36Sopenharmony_ci	asoc->force_delay = !!(msg->msg_flags & MSG_MORE);
186562306a36Sopenharmony_ci
186662306a36Sopenharmony_ci	list_for_each_entry(chunk, &datamsg->chunks, frag_list) {
186762306a36Sopenharmony_ci		sctp_chunk_hold(chunk);
186862306a36Sopenharmony_ci		sctp_set_owner_w(chunk);
186962306a36Sopenharmony_ci		chunk->transport = transport;
187062306a36Sopenharmony_ci	}
187162306a36Sopenharmony_ci
187262306a36Sopenharmony_ci	err = sctp_primitive_SEND(net, asoc, datamsg);
187362306a36Sopenharmony_ci	if (err) {
187462306a36Sopenharmony_ci		sctp_datamsg_free(datamsg);
187562306a36Sopenharmony_ci		goto err;
187662306a36Sopenharmony_ci	}
187762306a36Sopenharmony_ci
187862306a36Sopenharmony_ci	pr_debug("%s: we sent primitively\n", __func__);
187962306a36Sopenharmony_ci
188062306a36Sopenharmony_ci	sctp_datamsg_put(datamsg);
188162306a36Sopenharmony_ci
188262306a36Sopenharmony_ci	if (unlikely(wait_connect)) {
188362306a36Sopenharmony_ci		timeo = sock_sndtimeo(sk, msg->msg_flags & MSG_DONTWAIT);
188462306a36Sopenharmony_ci		sctp_wait_for_connect(asoc, &timeo);
188562306a36Sopenharmony_ci	}
188662306a36Sopenharmony_ci
188762306a36Sopenharmony_ci	err = msg_len;
188862306a36Sopenharmony_ci
188962306a36Sopenharmony_cierr:
189062306a36Sopenharmony_ci	return err;
189162306a36Sopenharmony_ci}
189262306a36Sopenharmony_ci
189362306a36Sopenharmony_cistatic union sctp_addr *sctp_sendmsg_get_daddr(struct sock *sk,
189462306a36Sopenharmony_ci					       const struct msghdr *msg,
189562306a36Sopenharmony_ci					       struct sctp_cmsgs *cmsgs)
189662306a36Sopenharmony_ci{
189762306a36Sopenharmony_ci	union sctp_addr *daddr = NULL;
189862306a36Sopenharmony_ci	int err;
189962306a36Sopenharmony_ci
190062306a36Sopenharmony_ci	if (!sctp_style(sk, UDP_HIGH_BANDWIDTH) && msg->msg_name) {
190162306a36Sopenharmony_ci		int len = msg->msg_namelen;
190262306a36Sopenharmony_ci
190362306a36Sopenharmony_ci		if (len > sizeof(*daddr))
190462306a36Sopenharmony_ci			len = sizeof(*daddr);
190562306a36Sopenharmony_ci
190662306a36Sopenharmony_ci		daddr = (union sctp_addr *)msg->msg_name;
190762306a36Sopenharmony_ci
190862306a36Sopenharmony_ci		err = sctp_verify_addr(sk, daddr, len);
190962306a36Sopenharmony_ci		if (err)
191062306a36Sopenharmony_ci			return ERR_PTR(err);
191162306a36Sopenharmony_ci	}
191262306a36Sopenharmony_ci
191362306a36Sopenharmony_ci	return daddr;
191462306a36Sopenharmony_ci}
191562306a36Sopenharmony_ci
191662306a36Sopenharmony_cistatic void sctp_sendmsg_update_sinfo(struct sctp_association *asoc,
191762306a36Sopenharmony_ci				      struct sctp_sndrcvinfo *sinfo,
191862306a36Sopenharmony_ci				      struct sctp_cmsgs *cmsgs)
191962306a36Sopenharmony_ci{
192062306a36Sopenharmony_ci	if (!cmsgs->srinfo && !cmsgs->sinfo) {
192162306a36Sopenharmony_ci		sinfo->sinfo_stream = asoc->default_stream;
192262306a36Sopenharmony_ci		sinfo->sinfo_ppid = asoc->default_ppid;
192362306a36Sopenharmony_ci		sinfo->sinfo_context = asoc->default_context;
192462306a36Sopenharmony_ci		sinfo->sinfo_assoc_id = sctp_assoc2id(asoc);
192562306a36Sopenharmony_ci
192662306a36Sopenharmony_ci		if (!cmsgs->prinfo)
192762306a36Sopenharmony_ci			sinfo->sinfo_flags = asoc->default_flags;
192862306a36Sopenharmony_ci	}
192962306a36Sopenharmony_ci
193062306a36Sopenharmony_ci	if (!cmsgs->srinfo && !cmsgs->prinfo)
193162306a36Sopenharmony_ci		sinfo->sinfo_timetolive = asoc->default_timetolive;
193262306a36Sopenharmony_ci
193362306a36Sopenharmony_ci	if (cmsgs->authinfo) {
193462306a36Sopenharmony_ci		/* Reuse sinfo_tsn to indicate that authinfo was set and
193562306a36Sopenharmony_ci		 * sinfo_ssn to save the keyid on tx path.
193662306a36Sopenharmony_ci		 */
193762306a36Sopenharmony_ci		sinfo->sinfo_tsn = 1;
193862306a36Sopenharmony_ci		sinfo->sinfo_ssn = cmsgs->authinfo->auth_keynumber;
193962306a36Sopenharmony_ci	}
194062306a36Sopenharmony_ci}
194162306a36Sopenharmony_ci
194262306a36Sopenharmony_cistatic int sctp_sendmsg(struct sock *sk, struct msghdr *msg, size_t msg_len)
194362306a36Sopenharmony_ci{
194462306a36Sopenharmony_ci	struct sctp_endpoint *ep = sctp_sk(sk)->ep;
194562306a36Sopenharmony_ci	struct sctp_transport *transport = NULL;
194662306a36Sopenharmony_ci	struct sctp_sndrcvinfo _sinfo, *sinfo;
194762306a36Sopenharmony_ci	struct sctp_association *asoc, *tmp;
194862306a36Sopenharmony_ci	struct sctp_cmsgs cmsgs;
194962306a36Sopenharmony_ci	union sctp_addr *daddr;
195062306a36Sopenharmony_ci	bool new = false;
195162306a36Sopenharmony_ci	__u16 sflags;
195262306a36Sopenharmony_ci	int err;
195362306a36Sopenharmony_ci
195462306a36Sopenharmony_ci	/* Parse and get snd_info */
195562306a36Sopenharmony_ci	err = sctp_sendmsg_parse(sk, &cmsgs, &_sinfo, msg, msg_len);
195662306a36Sopenharmony_ci	if (err)
195762306a36Sopenharmony_ci		goto out;
195862306a36Sopenharmony_ci
195962306a36Sopenharmony_ci	sinfo  = &_sinfo;
196062306a36Sopenharmony_ci	sflags = sinfo->sinfo_flags;
196162306a36Sopenharmony_ci
196262306a36Sopenharmony_ci	/* Get daddr from msg */
196362306a36Sopenharmony_ci	daddr = sctp_sendmsg_get_daddr(sk, msg, &cmsgs);
196462306a36Sopenharmony_ci	if (IS_ERR(daddr)) {
196562306a36Sopenharmony_ci		err = PTR_ERR(daddr);
196662306a36Sopenharmony_ci		goto out;
196762306a36Sopenharmony_ci	}
196862306a36Sopenharmony_ci
196962306a36Sopenharmony_ci	lock_sock(sk);
197062306a36Sopenharmony_ci
197162306a36Sopenharmony_ci	/* SCTP_SENDALL process */
197262306a36Sopenharmony_ci	if ((sflags & SCTP_SENDALL) && sctp_style(sk, UDP)) {
197362306a36Sopenharmony_ci		list_for_each_entry_safe(asoc, tmp, &ep->asocs, asocs) {
197462306a36Sopenharmony_ci			err = sctp_sendmsg_check_sflags(asoc, sflags, msg,
197562306a36Sopenharmony_ci							msg_len);
197662306a36Sopenharmony_ci			if (err == 0)
197762306a36Sopenharmony_ci				continue;
197862306a36Sopenharmony_ci			if (err < 0)
197962306a36Sopenharmony_ci				goto out_unlock;
198062306a36Sopenharmony_ci
198162306a36Sopenharmony_ci			sctp_sendmsg_update_sinfo(asoc, sinfo, &cmsgs);
198262306a36Sopenharmony_ci
198362306a36Sopenharmony_ci			err = sctp_sendmsg_to_asoc(asoc, msg, msg_len,
198462306a36Sopenharmony_ci						   NULL, sinfo);
198562306a36Sopenharmony_ci			if (err < 0)
198662306a36Sopenharmony_ci				goto out_unlock;
198762306a36Sopenharmony_ci
198862306a36Sopenharmony_ci			iov_iter_revert(&msg->msg_iter, err);
198962306a36Sopenharmony_ci		}
199062306a36Sopenharmony_ci
199162306a36Sopenharmony_ci		goto out_unlock;
199262306a36Sopenharmony_ci	}
199362306a36Sopenharmony_ci
199462306a36Sopenharmony_ci	/* Get and check or create asoc */
199562306a36Sopenharmony_ci	if (daddr) {
199662306a36Sopenharmony_ci		asoc = sctp_endpoint_lookup_assoc(ep, daddr, &transport);
199762306a36Sopenharmony_ci		if (asoc) {
199862306a36Sopenharmony_ci			err = sctp_sendmsg_check_sflags(asoc, sflags, msg,
199962306a36Sopenharmony_ci							msg_len);
200062306a36Sopenharmony_ci			if (err <= 0)
200162306a36Sopenharmony_ci				goto out_unlock;
200262306a36Sopenharmony_ci		} else {
200362306a36Sopenharmony_ci			err = sctp_sendmsg_new_asoc(sk, sflags, &cmsgs, daddr,
200462306a36Sopenharmony_ci						    &transport);
200562306a36Sopenharmony_ci			if (err)
200662306a36Sopenharmony_ci				goto out_unlock;
200762306a36Sopenharmony_ci
200862306a36Sopenharmony_ci			asoc = transport->asoc;
200962306a36Sopenharmony_ci			new = true;
201062306a36Sopenharmony_ci		}
201162306a36Sopenharmony_ci
201262306a36Sopenharmony_ci		if (!sctp_style(sk, TCP) && !(sflags & SCTP_ADDR_OVER))
201362306a36Sopenharmony_ci			transport = NULL;
201462306a36Sopenharmony_ci	} else {
201562306a36Sopenharmony_ci		asoc = sctp_id2assoc(sk, sinfo->sinfo_assoc_id);
201662306a36Sopenharmony_ci		if (!asoc) {
201762306a36Sopenharmony_ci			err = -EPIPE;
201862306a36Sopenharmony_ci			goto out_unlock;
201962306a36Sopenharmony_ci		}
202062306a36Sopenharmony_ci
202162306a36Sopenharmony_ci		err = sctp_sendmsg_check_sflags(asoc, sflags, msg, msg_len);
202262306a36Sopenharmony_ci		if (err <= 0)
202362306a36Sopenharmony_ci			goto out_unlock;
202462306a36Sopenharmony_ci	}
202562306a36Sopenharmony_ci
202662306a36Sopenharmony_ci	/* Update snd_info with the asoc */
202762306a36Sopenharmony_ci	sctp_sendmsg_update_sinfo(asoc, sinfo, &cmsgs);
202862306a36Sopenharmony_ci
202962306a36Sopenharmony_ci	/* Send msg to the asoc */
203062306a36Sopenharmony_ci	err = sctp_sendmsg_to_asoc(asoc, msg, msg_len, transport, sinfo);
203162306a36Sopenharmony_ci	if (err < 0 && err != -ESRCH && new)
203262306a36Sopenharmony_ci		sctp_association_free(asoc);
203362306a36Sopenharmony_ci
203462306a36Sopenharmony_ciout_unlock:
203562306a36Sopenharmony_ci	release_sock(sk);
203662306a36Sopenharmony_ciout:
203762306a36Sopenharmony_ci	return sctp_error(sk, msg->msg_flags, err);
203862306a36Sopenharmony_ci}
203962306a36Sopenharmony_ci
204062306a36Sopenharmony_ci/* This is an extended version of skb_pull() that removes the data from the
204162306a36Sopenharmony_ci * start of a skb even when data is spread across the list of skb's in the
204262306a36Sopenharmony_ci * frag_list. len specifies the total amount of data that needs to be removed.
204362306a36Sopenharmony_ci * when 'len' bytes could be removed from the skb, it returns 0.
204462306a36Sopenharmony_ci * If 'len' exceeds the total skb length,  it returns the no. of bytes that
204562306a36Sopenharmony_ci * could not be removed.
204662306a36Sopenharmony_ci */
204762306a36Sopenharmony_cistatic int sctp_skb_pull(struct sk_buff *skb, int len)
204862306a36Sopenharmony_ci{
204962306a36Sopenharmony_ci	struct sk_buff *list;
205062306a36Sopenharmony_ci	int skb_len = skb_headlen(skb);
205162306a36Sopenharmony_ci	int rlen;
205262306a36Sopenharmony_ci
205362306a36Sopenharmony_ci	if (len <= skb_len) {
205462306a36Sopenharmony_ci		__skb_pull(skb, len);
205562306a36Sopenharmony_ci		return 0;
205662306a36Sopenharmony_ci	}
205762306a36Sopenharmony_ci	len -= skb_len;
205862306a36Sopenharmony_ci	__skb_pull(skb, skb_len);
205962306a36Sopenharmony_ci
206062306a36Sopenharmony_ci	skb_walk_frags(skb, list) {
206162306a36Sopenharmony_ci		rlen = sctp_skb_pull(list, len);
206262306a36Sopenharmony_ci		skb->len -= (len-rlen);
206362306a36Sopenharmony_ci		skb->data_len -= (len-rlen);
206462306a36Sopenharmony_ci
206562306a36Sopenharmony_ci		if (!rlen)
206662306a36Sopenharmony_ci			return 0;
206762306a36Sopenharmony_ci
206862306a36Sopenharmony_ci		len = rlen;
206962306a36Sopenharmony_ci	}
207062306a36Sopenharmony_ci
207162306a36Sopenharmony_ci	return len;
207262306a36Sopenharmony_ci}
207362306a36Sopenharmony_ci
207462306a36Sopenharmony_ci/* API 3.1.3  recvmsg() - UDP Style Syntax
207562306a36Sopenharmony_ci *
207662306a36Sopenharmony_ci *  ssize_t recvmsg(int socket, struct msghdr *message,
207762306a36Sopenharmony_ci *                    int flags);
207862306a36Sopenharmony_ci *
207962306a36Sopenharmony_ci *  socket  - the socket descriptor of the endpoint.
208062306a36Sopenharmony_ci *  message - pointer to the msghdr structure which contains a single
208162306a36Sopenharmony_ci *            user message and possibly some ancillary data.
208262306a36Sopenharmony_ci *
208362306a36Sopenharmony_ci *            See Section 5 for complete description of the data
208462306a36Sopenharmony_ci *            structures.
208562306a36Sopenharmony_ci *
208662306a36Sopenharmony_ci *  flags   - flags sent or received with the user message, see Section
208762306a36Sopenharmony_ci *            5 for complete description of the flags.
208862306a36Sopenharmony_ci */
208962306a36Sopenharmony_cistatic int sctp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
209062306a36Sopenharmony_ci			int flags, int *addr_len)
209162306a36Sopenharmony_ci{
209262306a36Sopenharmony_ci	struct sctp_ulpevent *event = NULL;
209362306a36Sopenharmony_ci	struct sctp_sock *sp = sctp_sk(sk);
209462306a36Sopenharmony_ci	struct sk_buff *skb, *head_skb;
209562306a36Sopenharmony_ci	int copied;
209662306a36Sopenharmony_ci	int err = 0;
209762306a36Sopenharmony_ci	int skb_len;
209862306a36Sopenharmony_ci
209962306a36Sopenharmony_ci	pr_debug("%s: sk:%p, msghdr:%p, len:%zd, flags:0x%x, addr_len:%p)\n",
210062306a36Sopenharmony_ci		 __func__, sk, msg, len, flags, addr_len);
210162306a36Sopenharmony_ci
210262306a36Sopenharmony_ci	if (unlikely(flags & MSG_ERRQUEUE))
210362306a36Sopenharmony_ci		return inet_recv_error(sk, msg, len, addr_len);
210462306a36Sopenharmony_ci
210562306a36Sopenharmony_ci	if (sk_can_busy_loop(sk) &&
210662306a36Sopenharmony_ci	    skb_queue_empty_lockless(&sk->sk_receive_queue))
210762306a36Sopenharmony_ci		sk_busy_loop(sk, flags & MSG_DONTWAIT);
210862306a36Sopenharmony_ci
210962306a36Sopenharmony_ci	lock_sock(sk);
211062306a36Sopenharmony_ci
211162306a36Sopenharmony_ci	if (sctp_style(sk, TCP) && !sctp_sstate(sk, ESTABLISHED) &&
211262306a36Sopenharmony_ci	    !sctp_sstate(sk, CLOSING) && !sctp_sstate(sk, CLOSED)) {
211362306a36Sopenharmony_ci		err = -ENOTCONN;
211462306a36Sopenharmony_ci		goto out;
211562306a36Sopenharmony_ci	}
211662306a36Sopenharmony_ci
211762306a36Sopenharmony_ci	skb = sctp_skb_recv_datagram(sk, flags, &err);
211862306a36Sopenharmony_ci	if (!skb)
211962306a36Sopenharmony_ci		goto out;
212062306a36Sopenharmony_ci
212162306a36Sopenharmony_ci	/* Get the total length of the skb including any skb's in the
212262306a36Sopenharmony_ci	 * frag_list.
212362306a36Sopenharmony_ci	 */
212462306a36Sopenharmony_ci	skb_len = skb->len;
212562306a36Sopenharmony_ci
212662306a36Sopenharmony_ci	copied = skb_len;
212762306a36Sopenharmony_ci	if (copied > len)
212862306a36Sopenharmony_ci		copied = len;
212962306a36Sopenharmony_ci
213062306a36Sopenharmony_ci	err = skb_copy_datagram_msg(skb, 0, msg, copied);
213162306a36Sopenharmony_ci
213262306a36Sopenharmony_ci	event = sctp_skb2event(skb);
213362306a36Sopenharmony_ci
213462306a36Sopenharmony_ci	if (err)
213562306a36Sopenharmony_ci		goto out_free;
213662306a36Sopenharmony_ci
213762306a36Sopenharmony_ci	if (event->chunk && event->chunk->head_skb)
213862306a36Sopenharmony_ci		head_skb = event->chunk->head_skb;
213962306a36Sopenharmony_ci	else
214062306a36Sopenharmony_ci		head_skb = skb;
214162306a36Sopenharmony_ci	sock_recv_cmsgs(msg, sk, head_skb);
214262306a36Sopenharmony_ci	if (sctp_ulpevent_is_notification(event)) {
214362306a36Sopenharmony_ci		msg->msg_flags |= MSG_NOTIFICATION;
214462306a36Sopenharmony_ci		sp->pf->event_msgname(event, msg->msg_name, addr_len);
214562306a36Sopenharmony_ci	} else {
214662306a36Sopenharmony_ci		sp->pf->skb_msgname(head_skb, msg->msg_name, addr_len);
214762306a36Sopenharmony_ci	}
214862306a36Sopenharmony_ci
214962306a36Sopenharmony_ci	/* Check if we allow SCTP_NXTINFO. */
215062306a36Sopenharmony_ci	if (sp->recvnxtinfo)
215162306a36Sopenharmony_ci		sctp_ulpevent_read_nxtinfo(event, msg, sk);
215262306a36Sopenharmony_ci	/* Check if we allow SCTP_RCVINFO. */
215362306a36Sopenharmony_ci	if (sp->recvrcvinfo)
215462306a36Sopenharmony_ci		sctp_ulpevent_read_rcvinfo(event, msg);
215562306a36Sopenharmony_ci	/* Check if we allow SCTP_SNDRCVINFO. */
215662306a36Sopenharmony_ci	if (sctp_ulpevent_type_enabled(sp->subscribe, SCTP_DATA_IO_EVENT))
215762306a36Sopenharmony_ci		sctp_ulpevent_read_sndrcvinfo(event, msg);
215862306a36Sopenharmony_ci
215962306a36Sopenharmony_ci	err = copied;
216062306a36Sopenharmony_ci
216162306a36Sopenharmony_ci	/* If skb's length exceeds the user's buffer, update the skb and
216262306a36Sopenharmony_ci	 * push it back to the receive_queue so that the next call to
216362306a36Sopenharmony_ci	 * recvmsg() will return the remaining data. Don't set MSG_EOR.
216462306a36Sopenharmony_ci	 */
216562306a36Sopenharmony_ci	if (skb_len > copied) {
216662306a36Sopenharmony_ci		msg->msg_flags &= ~MSG_EOR;
216762306a36Sopenharmony_ci		if (flags & MSG_PEEK)
216862306a36Sopenharmony_ci			goto out_free;
216962306a36Sopenharmony_ci		sctp_skb_pull(skb, copied);
217062306a36Sopenharmony_ci		skb_queue_head(&sk->sk_receive_queue, skb);
217162306a36Sopenharmony_ci
217262306a36Sopenharmony_ci		/* When only partial message is copied to the user, increase
217362306a36Sopenharmony_ci		 * rwnd by that amount. If all the data in the skb is read,
217462306a36Sopenharmony_ci		 * rwnd is updated when the event is freed.
217562306a36Sopenharmony_ci		 */
217662306a36Sopenharmony_ci		if (!sctp_ulpevent_is_notification(event))
217762306a36Sopenharmony_ci			sctp_assoc_rwnd_increase(event->asoc, copied);
217862306a36Sopenharmony_ci		goto out;
217962306a36Sopenharmony_ci	} else if ((event->msg_flags & MSG_NOTIFICATION) ||
218062306a36Sopenharmony_ci		   (event->msg_flags & MSG_EOR))
218162306a36Sopenharmony_ci		msg->msg_flags |= MSG_EOR;
218262306a36Sopenharmony_ci	else
218362306a36Sopenharmony_ci		msg->msg_flags &= ~MSG_EOR;
218462306a36Sopenharmony_ci
218562306a36Sopenharmony_ciout_free:
218662306a36Sopenharmony_ci	if (flags & MSG_PEEK) {
218762306a36Sopenharmony_ci		/* Release the skb reference acquired after peeking the skb in
218862306a36Sopenharmony_ci		 * sctp_skb_recv_datagram().
218962306a36Sopenharmony_ci		 */
219062306a36Sopenharmony_ci		kfree_skb(skb);
219162306a36Sopenharmony_ci	} else {
219262306a36Sopenharmony_ci		/* Free the event which includes releasing the reference to
219362306a36Sopenharmony_ci		 * the owner of the skb, freeing the skb and updating the
219462306a36Sopenharmony_ci		 * rwnd.
219562306a36Sopenharmony_ci		 */
219662306a36Sopenharmony_ci		sctp_ulpevent_free(event);
219762306a36Sopenharmony_ci	}
219862306a36Sopenharmony_ciout:
219962306a36Sopenharmony_ci	release_sock(sk);
220062306a36Sopenharmony_ci	return err;
220162306a36Sopenharmony_ci}
220262306a36Sopenharmony_ci
220362306a36Sopenharmony_ci/* 7.1.12 Enable/Disable message fragmentation (SCTP_DISABLE_FRAGMENTS)
220462306a36Sopenharmony_ci *
220562306a36Sopenharmony_ci * This option is a on/off flag.  If enabled no SCTP message
220662306a36Sopenharmony_ci * fragmentation will be performed.  Instead if a message being sent
220762306a36Sopenharmony_ci * exceeds the current PMTU size, the message will NOT be sent and
220862306a36Sopenharmony_ci * instead a error will be indicated to the user.
220962306a36Sopenharmony_ci */
221062306a36Sopenharmony_cistatic int sctp_setsockopt_disable_fragments(struct sock *sk, int *val,
221162306a36Sopenharmony_ci					     unsigned int optlen)
221262306a36Sopenharmony_ci{
221362306a36Sopenharmony_ci	if (optlen < sizeof(int))
221462306a36Sopenharmony_ci		return -EINVAL;
221562306a36Sopenharmony_ci	sctp_sk(sk)->disable_fragments = (*val == 0) ? 0 : 1;
221662306a36Sopenharmony_ci	return 0;
221762306a36Sopenharmony_ci}
221862306a36Sopenharmony_ci
221962306a36Sopenharmony_cistatic int sctp_setsockopt_events(struct sock *sk, __u8 *sn_type,
222062306a36Sopenharmony_ci				  unsigned int optlen)
222162306a36Sopenharmony_ci{
222262306a36Sopenharmony_ci	struct sctp_sock *sp = sctp_sk(sk);
222362306a36Sopenharmony_ci	struct sctp_association *asoc;
222462306a36Sopenharmony_ci	int i;
222562306a36Sopenharmony_ci
222662306a36Sopenharmony_ci	if (optlen > sizeof(struct sctp_event_subscribe))
222762306a36Sopenharmony_ci		return -EINVAL;
222862306a36Sopenharmony_ci
222962306a36Sopenharmony_ci	for (i = 0; i < optlen; i++)
223062306a36Sopenharmony_ci		sctp_ulpevent_type_set(&sp->subscribe, SCTP_SN_TYPE_BASE + i,
223162306a36Sopenharmony_ci				       sn_type[i]);
223262306a36Sopenharmony_ci
223362306a36Sopenharmony_ci	list_for_each_entry(asoc, &sp->ep->asocs, asocs)
223462306a36Sopenharmony_ci		asoc->subscribe = sctp_sk(sk)->subscribe;
223562306a36Sopenharmony_ci
223662306a36Sopenharmony_ci	/* At the time when a user app subscribes to SCTP_SENDER_DRY_EVENT,
223762306a36Sopenharmony_ci	 * if there is no data to be sent or retransmit, the stack will
223862306a36Sopenharmony_ci	 * immediately send up this notification.
223962306a36Sopenharmony_ci	 */
224062306a36Sopenharmony_ci	if (sctp_ulpevent_type_enabled(sp->subscribe, SCTP_SENDER_DRY_EVENT)) {
224162306a36Sopenharmony_ci		struct sctp_ulpevent *event;
224262306a36Sopenharmony_ci
224362306a36Sopenharmony_ci		asoc = sctp_id2assoc(sk, 0);
224462306a36Sopenharmony_ci		if (asoc && sctp_outq_is_empty(&asoc->outqueue)) {
224562306a36Sopenharmony_ci			event = sctp_ulpevent_make_sender_dry_event(asoc,
224662306a36Sopenharmony_ci					GFP_USER | __GFP_NOWARN);
224762306a36Sopenharmony_ci			if (!event)
224862306a36Sopenharmony_ci				return -ENOMEM;
224962306a36Sopenharmony_ci
225062306a36Sopenharmony_ci			asoc->stream.si->enqueue_event(&asoc->ulpq, event);
225162306a36Sopenharmony_ci		}
225262306a36Sopenharmony_ci	}
225362306a36Sopenharmony_ci
225462306a36Sopenharmony_ci	return 0;
225562306a36Sopenharmony_ci}
225662306a36Sopenharmony_ci
225762306a36Sopenharmony_ci/* 7.1.8 Automatic Close of associations (SCTP_AUTOCLOSE)
225862306a36Sopenharmony_ci *
225962306a36Sopenharmony_ci * This socket option is applicable to the UDP-style socket only.  When
226062306a36Sopenharmony_ci * set it will cause associations that are idle for more than the
226162306a36Sopenharmony_ci * specified number of seconds to automatically close.  An association
226262306a36Sopenharmony_ci * being idle is defined an association that has NOT sent or received
226362306a36Sopenharmony_ci * user data.  The special value of '0' indicates that no automatic
226462306a36Sopenharmony_ci * close of any associations should be performed.  The option expects an
226562306a36Sopenharmony_ci * integer defining the number of seconds of idle time before an
226662306a36Sopenharmony_ci * association is closed.
226762306a36Sopenharmony_ci */
226862306a36Sopenharmony_cistatic int sctp_setsockopt_autoclose(struct sock *sk, u32 *optval,
226962306a36Sopenharmony_ci				     unsigned int optlen)
227062306a36Sopenharmony_ci{
227162306a36Sopenharmony_ci	struct sctp_sock *sp = sctp_sk(sk);
227262306a36Sopenharmony_ci	struct net *net = sock_net(sk);
227362306a36Sopenharmony_ci
227462306a36Sopenharmony_ci	/* Applicable to UDP-style socket only */
227562306a36Sopenharmony_ci	if (sctp_style(sk, TCP))
227662306a36Sopenharmony_ci		return -EOPNOTSUPP;
227762306a36Sopenharmony_ci	if (optlen != sizeof(int))
227862306a36Sopenharmony_ci		return -EINVAL;
227962306a36Sopenharmony_ci
228062306a36Sopenharmony_ci	sp->autoclose = *optval;
228162306a36Sopenharmony_ci	if (sp->autoclose > net->sctp.max_autoclose)
228262306a36Sopenharmony_ci		sp->autoclose = net->sctp.max_autoclose;
228362306a36Sopenharmony_ci
228462306a36Sopenharmony_ci	return 0;
228562306a36Sopenharmony_ci}
228662306a36Sopenharmony_ci
228762306a36Sopenharmony_ci/* 7.1.13 Peer Address Parameters (SCTP_PEER_ADDR_PARAMS)
228862306a36Sopenharmony_ci *
228962306a36Sopenharmony_ci * Applications can enable or disable heartbeats for any peer address of
229062306a36Sopenharmony_ci * an association, modify an address's heartbeat interval, force a
229162306a36Sopenharmony_ci * heartbeat to be sent immediately, and adjust the address's maximum
229262306a36Sopenharmony_ci * number of retransmissions sent before an address is considered
229362306a36Sopenharmony_ci * unreachable.  The following structure is used to access and modify an
229462306a36Sopenharmony_ci * address's parameters:
229562306a36Sopenharmony_ci *
229662306a36Sopenharmony_ci *  struct sctp_paddrparams {
229762306a36Sopenharmony_ci *     sctp_assoc_t            spp_assoc_id;
229862306a36Sopenharmony_ci *     struct sockaddr_storage spp_address;
229962306a36Sopenharmony_ci *     uint32_t                spp_hbinterval;
230062306a36Sopenharmony_ci *     uint16_t                spp_pathmaxrxt;
230162306a36Sopenharmony_ci *     uint32_t                spp_pathmtu;
230262306a36Sopenharmony_ci *     uint32_t                spp_sackdelay;
230362306a36Sopenharmony_ci *     uint32_t                spp_flags;
230462306a36Sopenharmony_ci *     uint32_t                spp_ipv6_flowlabel;
230562306a36Sopenharmony_ci *     uint8_t                 spp_dscp;
230662306a36Sopenharmony_ci * };
230762306a36Sopenharmony_ci *
230862306a36Sopenharmony_ci *   spp_assoc_id    - (one-to-many style socket) This is filled in the
230962306a36Sopenharmony_ci *                     application, and identifies the association for
231062306a36Sopenharmony_ci *                     this query.
231162306a36Sopenharmony_ci *   spp_address     - This specifies which address is of interest.
231262306a36Sopenharmony_ci *   spp_hbinterval  - This contains the value of the heartbeat interval,
231362306a36Sopenharmony_ci *                     in milliseconds.  If a  value of zero
231462306a36Sopenharmony_ci *                     is present in this field then no changes are to
231562306a36Sopenharmony_ci *                     be made to this parameter.
231662306a36Sopenharmony_ci *   spp_pathmaxrxt  - This contains the maximum number of
231762306a36Sopenharmony_ci *                     retransmissions before this address shall be
231862306a36Sopenharmony_ci *                     considered unreachable. If a  value of zero
231962306a36Sopenharmony_ci *                     is present in this field then no changes are to
232062306a36Sopenharmony_ci *                     be made to this parameter.
232162306a36Sopenharmony_ci *   spp_pathmtu     - When Path MTU discovery is disabled the value
232262306a36Sopenharmony_ci *                     specified here will be the "fixed" path mtu.
232362306a36Sopenharmony_ci *                     Note that if the spp_address field is empty
232462306a36Sopenharmony_ci *                     then all associations on this address will
232562306a36Sopenharmony_ci *                     have this fixed path mtu set upon them.
232662306a36Sopenharmony_ci *
232762306a36Sopenharmony_ci *   spp_sackdelay   - When delayed sack is enabled, this value specifies
232862306a36Sopenharmony_ci *                     the number of milliseconds that sacks will be delayed
232962306a36Sopenharmony_ci *                     for. This value will apply to all addresses of an
233062306a36Sopenharmony_ci *                     association if the spp_address field is empty. Note
233162306a36Sopenharmony_ci *                     also, that if delayed sack is enabled and this
233262306a36Sopenharmony_ci *                     value is set to 0, no change is made to the last
233362306a36Sopenharmony_ci *                     recorded delayed sack timer value.
233462306a36Sopenharmony_ci *
233562306a36Sopenharmony_ci *   spp_flags       - These flags are used to control various features
233662306a36Sopenharmony_ci *                     on an association. The flag field may contain
233762306a36Sopenharmony_ci *                     zero or more of the following options.
233862306a36Sopenharmony_ci *
233962306a36Sopenharmony_ci *                     SPP_HB_ENABLE  - Enable heartbeats on the
234062306a36Sopenharmony_ci *                     specified address. Note that if the address
234162306a36Sopenharmony_ci *                     field is empty all addresses for the association
234262306a36Sopenharmony_ci *                     have heartbeats enabled upon them.
234362306a36Sopenharmony_ci *
234462306a36Sopenharmony_ci *                     SPP_HB_DISABLE - Disable heartbeats on the
234562306a36Sopenharmony_ci *                     speicifed address. Note that if the address
234662306a36Sopenharmony_ci *                     field is empty all addresses for the association
234762306a36Sopenharmony_ci *                     will have their heartbeats disabled. Note also
234862306a36Sopenharmony_ci *                     that SPP_HB_ENABLE and SPP_HB_DISABLE are
234962306a36Sopenharmony_ci *                     mutually exclusive, only one of these two should
235062306a36Sopenharmony_ci *                     be specified. Enabling both fields will have
235162306a36Sopenharmony_ci *                     undetermined results.
235262306a36Sopenharmony_ci *
235362306a36Sopenharmony_ci *                     SPP_HB_DEMAND - Request a user initiated heartbeat
235462306a36Sopenharmony_ci *                     to be made immediately.
235562306a36Sopenharmony_ci *
235662306a36Sopenharmony_ci *                     SPP_HB_TIME_IS_ZERO - Specify's that the time for
235762306a36Sopenharmony_ci *                     heartbeat delayis to be set to the value of 0
235862306a36Sopenharmony_ci *                     milliseconds.
235962306a36Sopenharmony_ci *
236062306a36Sopenharmony_ci *                     SPP_PMTUD_ENABLE - This field will enable PMTU
236162306a36Sopenharmony_ci *                     discovery upon the specified address. Note that
236262306a36Sopenharmony_ci *                     if the address feild is empty then all addresses
236362306a36Sopenharmony_ci *                     on the association are effected.
236462306a36Sopenharmony_ci *
236562306a36Sopenharmony_ci *                     SPP_PMTUD_DISABLE - This field will disable PMTU
236662306a36Sopenharmony_ci *                     discovery upon the specified address. Note that
236762306a36Sopenharmony_ci *                     if the address feild is empty then all addresses
236862306a36Sopenharmony_ci *                     on the association are effected. Not also that
236962306a36Sopenharmony_ci *                     SPP_PMTUD_ENABLE and SPP_PMTUD_DISABLE are mutually
237062306a36Sopenharmony_ci *                     exclusive. Enabling both will have undetermined
237162306a36Sopenharmony_ci *                     results.
237262306a36Sopenharmony_ci *
237362306a36Sopenharmony_ci *                     SPP_SACKDELAY_ENABLE - Setting this flag turns
237462306a36Sopenharmony_ci *                     on delayed sack. The time specified in spp_sackdelay
237562306a36Sopenharmony_ci *                     is used to specify the sack delay for this address. Note
237662306a36Sopenharmony_ci *                     that if spp_address is empty then all addresses will
237762306a36Sopenharmony_ci *                     enable delayed sack and take on the sack delay
237862306a36Sopenharmony_ci *                     value specified in spp_sackdelay.
237962306a36Sopenharmony_ci *                     SPP_SACKDELAY_DISABLE - Setting this flag turns
238062306a36Sopenharmony_ci *                     off delayed sack. If the spp_address field is blank then
238162306a36Sopenharmony_ci *                     delayed sack is disabled for the entire association. Note
238262306a36Sopenharmony_ci *                     also that this field is mutually exclusive to
238362306a36Sopenharmony_ci *                     SPP_SACKDELAY_ENABLE, setting both will have undefined
238462306a36Sopenharmony_ci *                     results.
238562306a36Sopenharmony_ci *
238662306a36Sopenharmony_ci *                     SPP_IPV6_FLOWLABEL:  Setting this flag enables the
238762306a36Sopenharmony_ci *                     setting of the IPV6 flow label value.  The value is
238862306a36Sopenharmony_ci *                     contained in the spp_ipv6_flowlabel field.
238962306a36Sopenharmony_ci *                     Upon retrieval, this flag will be set to indicate that
239062306a36Sopenharmony_ci *                     the spp_ipv6_flowlabel field has a valid value returned.
239162306a36Sopenharmony_ci *                     If a specific destination address is set (in the
239262306a36Sopenharmony_ci *                     spp_address field), then the value returned is that of
239362306a36Sopenharmony_ci *                     the address.  If just an association is specified (and
239462306a36Sopenharmony_ci *                     no address), then the association's default flow label
239562306a36Sopenharmony_ci *                     is returned.  If neither an association nor a destination
239662306a36Sopenharmony_ci *                     is specified, then the socket's default flow label is
239762306a36Sopenharmony_ci *                     returned.  For non-IPv6 sockets, this flag will be left
239862306a36Sopenharmony_ci *                     cleared.
239962306a36Sopenharmony_ci *
240062306a36Sopenharmony_ci *                     SPP_DSCP:  Setting this flag enables the setting of the
240162306a36Sopenharmony_ci *                     Differentiated Services Code Point (DSCP) value
240262306a36Sopenharmony_ci *                     associated with either the association or a specific
240362306a36Sopenharmony_ci *                     address.  The value is obtained in the spp_dscp field.
240462306a36Sopenharmony_ci *                     Upon retrieval, this flag will be set to indicate that
240562306a36Sopenharmony_ci *                     the spp_dscp field has a valid value returned.  If a
240662306a36Sopenharmony_ci *                     specific destination address is set when called (in the
240762306a36Sopenharmony_ci *                     spp_address field), then that specific destination
240862306a36Sopenharmony_ci *                     address's DSCP value is returned.  If just an association
240962306a36Sopenharmony_ci *                     is specified, then the association's default DSCP is
241062306a36Sopenharmony_ci *                     returned.  If neither an association nor a destination is
241162306a36Sopenharmony_ci *                     specified, then the socket's default DSCP is returned.
241262306a36Sopenharmony_ci *
241362306a36Sopenharmony_ci *   spp_ipv6_flowlabel
241462306a36Sopenharmony_ci *                   - This field is used in conjunction with the
241562306a36Sopenharmony_ci *                     SPP_IPV6_FLOWLABEL flag and contains the IPv6 flow label.
241662306a36Sopenharmony_ci *                     The 20 least significant bits are used for the flow
241762306a36Sopenharmony_ci *                     label.  This setting has precedence over any IPv6-layer
241862306a36Sopenharmony_ci *                     setting.
241962306a36Sopenharmony_ci *
242062306a36Sopenharmony_ci *   spp_dscp        - This field is used in conjunction with the SPP_DSCP flag
242162306a36Sopenharmony_ci *                     and contains the DSCP.  The 6 most significant bits are
242262306a36Sopenharmony_ci *                     used for the DSCP.  This setting has precedence over any
242362306a36Sopenharmony_ci *                     IPv4- or IPv6- layer setting.
242462306a36Sopenharmony_ci */
242562306a36Sopenharmony_cistatic int sctp_apply_peer_addr_params(struct sctp_paddrparams *params,
242662306a36Sopenharmony_ci				       struct sctp_transport   *trans,
242762306a36Sopenharmony_ci				       struct sctp_association *asoc,
242862306a36Sopenharmony_ci				       struct sctp_sock        *sp,
242962306a36Sopenharmony_ci				       int                      hb_change,
243062306a36Sopenharmony_ci				       int                      pmtud_change,
243162306a36Sopenharmony_ci				       int                      sackdelay_change)
243262306a36Sopenharmony_ci{
243362306a36Sopenharmony_ci	int error;
243462306a36Sopenharmony_ci
243562306a36Sopenharmony_ci	if (params->spp_flags & SPP_HB_DEMAND && trans) {
243662306a36Sopenharmony_ci		error = sctp_primitive_REQUESTHEARTBEAT(trans->asoc->base.net,
243762306a36Sopenharmony_ci							trans->asoc, trans);
243862306a36Sopenharmony_ci		if (error)
243962306a36Sopenharmony_ci			return error;
244062306a36Sopenharmony_ci	}
244162306a36Sopenharmony_ci
244262306a36Sopenharmony_ci	/* Note that unless the spp_flag is set to SPP_HB_ENABLE the value of
244362306a36Sopenharmony_ci	 * this field is ignored.  Note also that a value of zero indicates
244462306a36Sopenharmony_ci	 * the current setting should be left unchanged.
244562306a36Sopenharmony_ci	 */
244662306a36Sopenharmony_ci	if (params->spp_flags & SPP_HB_ENABLE) {
244762306a36Sopenharmony_ci
244862306a36Sopenharmony_ci		/* Re-zero the interval if the SPP_HB_TIME_IS_ZERO is
244962306a36Sopenharmony_ci		 * set.  This lets us use 0 value when this flag
245062306a36Sopenharmony_ci		 * is set.
245162306a36Sopenharmony_ci		 */
245262306a36Sopenharmony_ci		if (params->spp_flags & SPP_HB_TIME_IS_ZERO)
245362306a36Sopenharmony_ci			params->spp_hbinterval = 0;
245462306a36Sopenharmony_ci
245562306a36Sopenharmony_ci		if (params->spp_hbinterval ||
245662306a36Sopenharmony_ci		    (params->spp_flags & SPP_HB_TIME_IS_ZERO)) {
245762306a36Sopenharmony_ci			if (trans) {
245862306a36Sopenharmony_ci				trans->hbinterval =
245962306a36Sopenharmony_ci				    msecs_to_jiffies(params->spp_hbinterval);
246062306a36Sopenharmony_ci				sctp_transport_reset_hb_timer(trans);
246162306a36Sopenharmony_ci			} else if (asoc) {
246262306a36Sopenharmony_ci				asoc->hbinterval =
246362306a36Sopenharmony_ci				    msecs_to_jiffies(params->spp_hbinterval);
246462306a36Sopenharmony_ci			} else {
246562306a36Sopenharmony_ci				sp->hbinterval = params->spp_hbinterval;
246662306a36Sopenharmony_ci			}
246762306a36Sopenharmony_ci		}
246862306a36Sopenharmony_ci	}
246962306a36Sopenharmony_ci
247062306a36Sopenharmony_ci	if (hb_change) {
247162306a36Sopenharmony_ci		if (trans) {
247262306a36Sopenharmony_ci			trans->param_flags =
247362306a36Sopenharmony_ci				(trans->param_flags & ~SPP_HB) | hb_change;
247462306a36Sopenharmony_ci		} else if (asoc) {
247562306a36Sopenharmony_ci			asoc->param_flags =
247662306a36Sopenharmony_ci				(asoc->param_flags & ~SPP_HB) | hb_change;
247762306a36Sopenharmony_ci		} else {
247862306a36Sopenharmony_ci			sp->param_flags =
247962306a36Sopenharmony_ci				(sp->param_flags & ~SPP_HB) | hb_change;
248062306a36Sopenharmony_ci		}
248162306a36Sopenharmony_ci	}
248262306a36Sopenharmony_ci
248362306a36Sopenharmony_ci	/* When Path MTU discovery is disabled the value specified here will
248462306a36Sopenharmony_ci	 * be the "fixed" path mtu (i.e. the value of the spp_flags field must
248562306a36Sopenharmony_ci	 * include the flag SPP_PMTUD_DISABLE for this field to have any
248662306a36Sopenharmony_ci	 * effect).
248762306a36Sopenharmony_ci	 */
248862306a36Sopenharmony_ci	if ((params->spp_flags & SPP_PMTUD_DISABLE) && params->spp_pathmtu) {
248962306a36Sopenharmony_ci		if (trans) {
249062306a36Sopenharmony_ci			trans->pathmtu = params->spp_pathmtu;
249162306a36Sopenharmony_ci			sctp_assoc_sync_pmtu(asoc);
249262306a36Sopenharmony_ci		} else if (asoc) {
249362306a36Sopenharmony_ci			sctp_assoc_set_pmtu(asoc, params->spp_pathmtu);
249462306a36Sopenharmony_ci		} else {
249562306a36Sopenharmony_ci			sp->pathmtu = params->spp_pathmtu;
249662306a36Sopenharmony_ci		}
249762306a36Sopenharmony_ci	}
249862306a36Sopenharmony_ci
249962306a36Sopenharmony_ci	if (pmtud_change) {
250062306a36Sopenharmony_ci		if (trans) {
250162306a36Sopenharmony_ci			int update = (trans->param_flags & SPP_PMTUD_DISABLE) &&
250262306a36Sopenharmony_ci				(params->spp_flags & SPP_PMTUD_ENABLE);
250362306a36Sopenharmony_ci			trans->param_flags =
250462306a36Sopenharmony_ci				(trans->param_flags & ~SPP_PMTUD) | pmtud_change;
250562306a36Sopenharmony_ci			if (update) {
250662306a36Sopenharmony_ci				sctp_transport_pmtu(trans, sctp_opt2sk(sp));
250762306a36Sopenharmony_ci				sctp_assoc_sync_pmtu(asoc);
250862306a36Sopenharmony_ci			}
250962306a36Sopenharmony_ci			sctp_transport_pl_reset(trans);
251062306a36Sopenharmony_ci		} else if (asoc) {
251162306a36Sopenharmony_ci			asoc->param_flags =
251262306a36Sopenharmony_ci				(asoc->param_flags & ~SPP_PMTUD) | pmtud_change;
251362306a36Sopenharmony_ci		} else {
251462306a36Sopenharmony_ci			sp->param_flags =
251562306a36Sopenharmony_ci				(sp->param_flags & ~SPP_PMTUD) | pmtud_change;
251662306a36Sopenharmony_ci		}
251762306a36Sopenharmony_ci	}
251862306a36Sopenharmony_ci
251962306a36Sopenharmony_ci	/* Note that unless the spp_flag is set to SPP_SACKDELAY_ENABLE the
252062306a36Sopenharmony_ci	 * value of this field is ignored.  Note also that a value of zero
252162306a36Sopenharmony_ci	 * indicates the current setting should be left unchanged.
252262306a36Sopenharmony_ci	 */
252362306a36Sopenharmony_ci	if ((params->spp_flags & SPP_SACKDELAY_ENABLE) && params->spp_sackdelay) {
252462306a36Sopenharmony_ci		if (trans) {
252562306a36Sopenharmony_ci			trans->sackdelay =
252662306a36Sopenharmony_ci				msecs_to_jiffies(params->spp_sackdelay);
252762306a36Sopenharmony_ci		} else if (asoc) {
252862306a36Sopenharmony_ci			asoc->sackdelay =
252962306a36Sopenharmony_ci				msecs_to_jiffies(params->spp_sackdelay);
253062306a36Sopenharmony_ci		} else {
253162306a36Sopenharmony_ci			sp->sackdelay = params->spp_sackdelay;
253262306a36Sopenharmony_ci		}
253362306a36Sopenharmony_ci	}
253462306a36Sopenharmony_ci
253562306a36Sopenharmony_ci	if (sackdelay_change) {
253662306a36Sopenharmony_ci		if (trans) {
253762306a36Sopenharmony_ci			trans->param_flags =
253862306a36Sopenharmony_ci				(trans->param_flags & ~SPP_SACKDELAY) |
253962306a36Sopenharmony_ci				sackdelay_change;
254062306a36Sopenharmony_ci		} else if (asoc) {
254162306a36Sopenharmony_ci			asoc->param_flags =
254262306a36Sopenharmony_ci				(asoc->param_flags & ~SPP_SACKDELAY) |
254362306a36Sopenharmony_ci				sackdelay_change;
254462306a36Sopenharmony_ci		} else {
254562306a36Sopenharmony_ci			sp->param_flags =
254662306a36Sopenharmony_ci				(sp->param_flags & ~SPP_SACKDELAY) |
254762306a36Sopenharmony_ci				sackdelay_change;
254862306a36Sopenharmony_ci		}
254962306a36Sopenharmony_ci	}
255062306a36Sopenharmony_ci
255162306a36Sopenharmony_ci	/* Note that a value of zero indicates the current setting should be
255262306a36Sopenharmony_ci	   left unchanged.
255362306a36Sopenharmony_ci	 */
255462306a36Sopenharmony_ci	if (params->spp_pathmaxrxt) {
255562306a36Sopenharmony_ci		if (trans) {
255662306a36Sopenharmony_ci			trans->pathmaxrxt = params->spp_pathmaxrxt;
255762306a36Sopenharmony_ci		} else if (asoc) {
255862306a36Sopenharmony_ci			asoc->pathmaxrxt = params->spp_pathmaxrxt;
255962306a36Sopenharmony_ci		} else {
256062306a36Sopenharmony_ci			sp->pathmaxrxt = params->spp_pathmaxrxt;
256162306a36Sopenharmony_ci		}
256262306a36Sopenharmony_ci	}
256362306a36Sopenharmony_ci
256462306a36Sopenharmony_ci	if (params->spp_flags & SPP_IPV6_FLOWLABEL) {
256562306a36Sopenharmony_ci		if (trans) {
256662306a36Sopenharmony_ci			if (trans->ipaddr.sa.sa_family == AF_INET6) {
256762306a36Sopenharmony_ci				trans->flowlabel = params->spp_ipv6_flowlabel &
256862306a36Sopenharmony_ci						   SCTP_FLOWLABEL_VAL_MASK;
256962306a36Sopenharmony_ci				trans->flowlabel |= SCTP_FLOWLABEL_SET_MASK;
257062306a36Sopenharmony_ci			}
257162306a36Sopenharmony_ci		} else if (asoc) {
257262306a36Sopenharmony_ci			struct sctp_transport *t;
257362306a36Sopenharmony_ci
257462306a36Sopenharmony_ci			list_for_each_entry(t, &asoc->peer.transport_addr_list,
257562306a36Sopenharmony_ci					    transports) {
257662306a36Sopenharmony_ci				if (t->ipaddr.sa.sa_family != AF_INET6)
257762306a36Sopenharmony_ci					continue;
257862306a36Sopenharmony_ci				t->flowlabel = params->spp_ipv6_flowlabel &
257962306a36Sopenharmony_ci					       SCTP_FLOWLABEL_VAL_MASK;
258062306a36Sopenharmony_ci				t->flowlabel |= SCTP_FLOWLABEL_SET_MASK;
258162306a36Sopenharmony_ci			}
258262306a36Sopenharmony_ci			asoc->flowlabel = params->spp_ipv6_flowlabel &
258362306a36Sopenharmony_ci					  SCTP_FLOWLABEL_VAL_MASK;
258462306a36Sopenharmony_ci			asoc->flowlabel |= SCTP_FLOWLABEL_SET_MASK;
258562306a36Sopenharmony_ci		} else if (sctp_opt2sk(sp)->sk_family == AF_INET6) {
258662306a36Sopenharmony_ci			sp->flowlabel = params->spp_ipv6_flowlabel &
258762306a36Sopenharmony_ci					SCTP_FLOWLABEL_VAL_MASK;
258862306a36Sopenharmony_ci			sp->flowlabel |= SCTP_FLOWLABEL_SET_MASK;
258962306a36Sopenharmony_ci		}
259062306a36Sopenharmony_ci	}
259162306a36Sopenharmony_ci
259262306a36Sopenharmony_ci	if (params->spp_flags & SPP_DSCP) {
259362306a36Sopenharmony_ci		if (trans) {
259462306a36Sopenharmony_ci			trans->dscp = params->spp_dscp & SCTP_DSCP_VAL_MASK;
259562306a36Sopenharmony_ci			trans->dscp |= SCTP_DSCP_SET_MASK;
259662306a36Sopenharmony_ci		} else if (asoc) {
259762306a36Sopenharmony_ci			struct sctp_transport *t;
259862306a36Sopenharmony_ci
259962306a36Sopenharmony_ci			list_for_each_entry(t, &asoc->peer.transport_addr_list,
260062306a36Sopenharmony_ci					    transports) {
260162306a36Sopenharmony_ci				t->dscp = params->spp_dscp &
260262306a36Sopenharmony_ci					  SCTP_DSCP_VAL_MASK;
260362306a36Sopenharmony_ci				t->dscp |= SCTP_DSCP_SET_MASK;
260462306a36Sopenharmony_ci			}
260562306a36Sopenharmony_ci			asoc->dscp = params->spp_dscp & SCTP_DSCP_VAL_MASK;
260662306a36Sopenharmony_ci			asoc->dscp |= SCTP_DSCP_SET_MASK;
260762306a36Sopenharmony_ci		} else {
260862306a36Sopenharmony_ci			sp->dscp = params->spp_dscp & SCTP_DSCP_VAL_MASK;
260962306a36Sopenharmony_ci			sp->dscp |= SCTP_DSCP_SET_MASK;
261062306a36Sopenharmony_ci		}
261162306a36Sopenharmony_ci	}
261262306a36Sopenharmony_ci
261362306a36Sopenharmony_ci	return 0;
261462306a36Sopenharmony_ci}
261562306a36Sopenharmony_ci
261662306a36Sopenharmony_cistatic int sctp_setsockopt_peer_addr_params(struct sock *sk,
261762306a36Sopenharmony_ci					    struct sctp_paddrparams *params,
261862306a36Sopenharmony_ci					    unsigned int optlen)
261962306a36Sopenharmony_ci{
262062306a36Sopenharmony_ci	struct sctp_transport   *trans = NULL;
262162306a36Sopenharmony_ci	struct sctp_association *asoc = NULL;
262262306a36Sopenharmony_ci	struct sctp_sock        *sp = sctp_sk(sk);
262362306a36Sopenharmony_ci	int error;
262462306a36Sopenharmony_ci	int hb_change, pmtud_change, sackdelay_change;
262562306a36Sopenharmony_ci
262662306a36Sopenharmony_ci	if (optlen == ALIGN(offsetof(struct sctp_paddrparams,
262762306a36Sopenharmony_ci					    spp_ipv6_flowlabel), 4)) {
262862306a36Sopenharmony_ci		if (params->spp_flags & (SPP_DSCP | SPP_IPV6_FLOWLABEL))
262962306a36Sopenharmony_ci			return -EINVAL;
263062306a36Sopenharmony_ci	} else if (optlen != sizeof(*params)) {
263162306a36Sopenharmony_ci		return -EINVAL;
263262306a36Sopenharmony_ci	}
263362306a36Sopenharmony_ci
263462306a36Sopenharmony_ci	/* Validate flags and value parameters. */
263562306a36Sopenharmony_ci	hb_change        = params->spp_flags & SPP_HB;
263662306a36Sopenharmony_ci	pmtud_change     = params->spp_flags & SPP_PMTUD;
263762306a36Sopenharmony_ci	sackdelay_change = params->spp_flags & SPP_SACKDELAY;
263862306a36Sopenharmony_ci
263962306a36Sopenharmony_ci	if (hb_change        == SPP_HB ||
264062306a36Sopenharmony_ci	    pmtud_change     == SPP_PMTUD ||
264162306a36Sopenharmony_ci	    sackdelay_change == SPP_SACKDELAY ||
264262306a36Sopenharmony_ci	    params->spp_sackdelay > 500 ||
264362306a36Sopenharmony_ci	    (params->spp_pathmtu &&
264462306a36Sopenharmony_ci	     params->spp_pathmtu < SCTP_DEFAULT_MINSEGMENT))
264562306a36Sopenharmony_ci		return -EINVAL;
264662306a36Sopenharmony_ci
264762306a36Sopenharmony_ci	/* If an address other than INADDR_ANY is specified, and
264862306a36Sopenharmony_ci	 * no transport is found, then the request is invalid.
264962306a36Sopenharmony_ci	 */
265062306a36Sopenharmony_ci	if (!sctp_is_any(sk, (union sctp_addr *)&params->spp_address)) {
265162306a36Sopenharmony_ci		trans = sctp_addr_id2transport(sk, &params->spp_address,
265262306a36Sopenharmony_ci					       params->spp_assoc_id);
265362306a36Sopenharmony_ci		if (!trans)
265462306a36Sopenharmony_ci			return -EINVAL;
265562306a36Sopenharmony_ci	}
265662306a36Sopenharmony_ci
265762306a36Sopenharmony_ci	/* Get association, if assoc_id != SCTP_FUTURE_ASSOC and the
265862306a36Sopenharmony_ci	 * socket is a one to many style socket, and an association
265962306a36Sopenharmony_ci	 * was not found, then the id was invalid.
266062306a36Sopenharmony_ci	 */
266162306a36Sopenharmony_ci	asoc = sctp_id2assoc(sk, params->spp_assoc_id);
266262306a36Sopenharmony_ci	if (!asoc && params->spp_assoc_id != SCTP_FUTURE_ASSOC &&
266362306a36Sopenharmony_ci	    sctp_style(sk, UDP))
266462306a36Sopenharmony_ci		return -EINVAL;
266562306a36Sopenharmony_ci
266662306a36Sopenharmony_ci	/* Heartbeat demand can only be sent on a transport or
266762306a36Sopenharmony_ci	 * association, but not a socket.
266862306a36Sopenharmony_ci	 */
266962306a36Sopenharmony_ci	if (params->spp_flags & SPP_HB_DEMAND && !trans && !asoc)
267062306a36Sopenharmony_ci		return -EINVAL;
267162306a36Sopenharmony_ci
267262306a36Sopenharmony_ci	/* Process parameters. */
267362306a36Sopenharmony_ci	error = sctp_apply_peer_addr_params(params, trans, asoc, sp,
267462306a36Sopenharmony_ci					    hb_change, pmtud_change,
267562306a36Sopenharmony_ci					    sackdelay_change);
267662306a36Sopenharmony_ci
267762306a36Sopenharmony_ci	if (error)
267862306a36Sopenharmony_ci		return error;
267962306a36Sopenharmony_ci
268062306a36Sopenharmony_ci	/* If changes are for association, also apply parameters to each
268162306a36Sopenharmony_ci	 * transport.
268262306a36Sopenharmony_ci	 */
268362306a36Sopenharmony_ci	if (!trans && asoc) {
268462306a36Sopenharmony_ci		list_for_each_entry(trans, &asoc->peer.transport_addr_list,
268562306a36Sopenharmony_ci				transports) {
268662306a36Sopenharmony_ci			sctp_apply_peer_addr_params(params, trans, asoc, sp,
268762306a36Sopenharmony_ci						    hb_change, pmtud_change,
268862306a36Sopenharmony_ci						    sackdelay_change);
268962306a36Sopenharmony_ci		}
269062306a36Sopenharmony_ci	}
269162306a36Sopenharmony_ci
269262306a36Sopenharmony_ci	return 0;
269362306a36Sopenharmony_ci}
269462306a36Sopenharmony_ci
269562306a36Sopenharmony_cistatic inline __u32 sctp_spp_sackdelay_enable(__u32 param_flags)
269662306a36Sopenharmony_ci{
269762306a36Sopenharmony_ci	return (param_flags & ~SPP_SACKDELAY) | SPP_SACKDELAY_ENABLE;
269862306a36Sopenharmony_ci}
269962306a36Sopenharmony_ci
270062306a36Sopenharmony_cistatic inline __u32 sctp_spp_sackdelay_disable(__u32 param_flags)
270162306a36Sopenharmony_ci{
270262306a36Sopenharmony_ci	return (param_flags & ~SPP_SACKDELAY) | SPP_SACKDELAY_DISABLE;
270362306a36Sopenharmony_ci}
270462306a36Sopenharmony_ci
270562306a36Sopenharmony_cistatic void sctp_apply_asoc_delayed_ack(struct sctp_sack_info *params,
270662306a36Sopenharmony_ci					struct sctp_association *asoc)
270762306a36Sopenharmony_ci{
270862306a36Sopenharmony_ci	struct sctp_transport *trans;
270962306a36Sopenharmony_ci
271062306a36Sopenharmony_ci	if (params->sack_delay) {
271162306a36Sopenharmony_ci		asoc->sackdelay = msecs_to_jiffies(params->sack_delay);
271262306a36Sopenharmony_ci		asoc->param_flags =
271362306a36Sopenharmony_ci			sctp_spp_sackdelay_enable(asoc->param_flags);
271462306a36Sopenharmony_ci	}
271562306a36Sopenharmony_ci	if (params->sack_freq == 1) {
271662306a36Sopenharmony_ci		asoc->param_flags =
271762306a36Sopenharmony_ci			sctp_spp_sackdelay_disable(asoc->param_flags);
271862306a36Sopenharmony_ci	} else if (params->sack_freq > 1) {
271962306a36Sopenharmony_ci		asoc->sackfreq = params->sack_freq;
272062306a36Sopenharmony_ci		asoc->param_flags =
272162306a36Sopenharmony_ci			sctp_spp_sackdelay_enable(asoc->param_flags);
272262306a36Sopenharmony_ci	}
272362306a36Sopenharmony_ci
272462306a36Sopenharmony_ci	list_for_each_entry(trans, &asoc->peer.transport_addr_list,
272562306a36Sopenharmony_ci			    transports) {
272662306a36Sopenharmony_ci		if (params->sack_delay) {
272762306a36Sopenharmony_ci			trans->sackdelay = msecs_to_jiffies(params->sack_delay);
272862306a36Sopenharmony_ci			trans->param_flags =
272962306a36Sopenharmony_ci				sctp_spp_sackdelay_enable(trans->param_flags);
273062306a36Sopenharmony_ci		}
273162306a36Sopenharmony_ci		if (params->sack_freq == 1) {
273262306a36Sopenharmony_ci			trans->param_flags =
273362306a36Sopenharmony_ci				sctp_spp_sackdelay_disable(trans->param_flags);
273462306a36Sopenharmony_ci		} else if (params->sack_freq > 1) {
273562306a36Sopenharmony_ci			trans->sackfreq = params->sack_freq;
273662306a36Sopenharmony_ci			trans->param_flags =
273762306a36Sopenharmony_ci				sctp_spp_sackdelay_enable(trans->param_flags);
273862306a36Sopenharmony_ci		}
273962306a36Sopenharmony_ci	}
274062306a36Sopenharmony_ci}
274162306a36Sopenharmony_ci
274262306a36Sopenharmony_ci/*
274362306a36Sopenharmony_ci * 7.1.23.  Get or set delayed ack timer (SCTP_DELAYED_SACK)
274462306a36Sopenharmony_ci *
274562306a36Sopenharmony_ci * This option will effect the way delayed acks are performed.  This
274662306a36Sopenharmony_ci * option allows you to get or set the delayed ack time, in
274762306a36Sopenharmony_ci * milliseconds.  It also allows changing the delayed ack frequency.
274862306a36Sopenharmony_ci * Changing the frequency to 1 disables the delayed sack algorithm.  If
274962306a36Sopenharmony_ci * the assoc_id is 0, then this sets or gets the endpoints default
275062306a36Sopenharmony_ci * values.  If the assoc_id field is non-zero, then the set or get
275162306a36Sopenharmony_ci * effects the specified association for the one to many model (the
275262306a36Sopenharmony_ci * assoc_id field is ignored by the one to one model).  Note that if
275362306a36Sopenharmony_ci * sack_delay or sack_freq are 0 when setting this option, then the
275462306a36Sopenharmony_ci * current values will remain unchanged.
275562306a36Sopenharmony_ci *
275662306a36Sopenharmony_ci * struct sctp_sack_info {
275762306a36Sopenharmony_ci *     sctp_assoc_t            sack_assoc_id;
275862306a36Sopenharmony_ci *     uint32_t                sack_delay;
275962306a36Sopenharmony_ci *     uint32_t                sack_freq;
276062306a36Sopenharmony_ci * };
276162306a36Sopenharmony_ci *
276262306a36Sopenharmony_ci * sack_assoc_id -  This parameter, indicates which association the user
276362306a36Sopenharmony_ci *    is performing an action upon.  Note that if this field's value is
276462306a36Sopenharmony_ci *    zero then the endpoints default value is changed (effecting future
276562306a36Sopenharmony_ci *    associations only).
276662306a36Sopenharmony_ci *
276762306a36Sopenharmony_ci * sack_delay -  This parameter contains the number of milliseconds that
276862306a36Sopenharmony_ci *    the user is requesting the delayed ACK timer be set to.  Note that
276962306a36Sopenharmony_ci *    this value is defined in the standard to be between 200 and 500
277062306a36Sopenharmony_ci *    milliseconds.
277162306a36Sopenharmony_ci *
277262306a36Sopenharmony_ci * sack_freq -  This parameter contains the number of packets that must
277362306a36Sopenharmony_ci *    be received before a sack is sent without waiting for the delay
277462306a36Sopenharmony_ci *    timer to expire.  The default value for this is 2, setting this
277562306a36Sopenharmony_ci *    value to 1 will disable the delayed sack algorithm.
277662306a36Sopenharmony_ci */
277762306a36Sopenharmony_cistatic int __sctp_setsockopt_delayed_ack(struct sock *sk,
277862306a36Sopenharmony_ci					 struct sctp_sack_info *params)
277962306a36Sopenharmony_ci{
278062306a36Sopenharmony_ci	struct sctp_sock *sp = sctp_sk(sk);
278162306a36Sopenharmony_ci	struct sctp_association *asoc;
278262306a36Sopenharmony_ci
278362306a36Sopenharmony_ci	/* Validate value parameter. */
278462306a36Sopenharmony_ci	if (params->sack_delay > 500)
278562306a36Sopenharmony_ci		return -EINVAL;
278662306a36Sopenharmony_ci
278762306a36Sopenharmony_ci	/* Get association, if sack_assoc_id != SCTP_FUTURE_ASSOC and the
278862306a36Sopenharmony_ci	 * socket is a one to many style socket, and an association
278962306a36Sopenharmony_ci	 * was not found, then the id was invalid.
279062306a36Sopenharmony_ci	 */
279162306a36Sopenharmony_ci	asoc = sctp_id2assoc(sk, params->sack_assoc_id);
279262306a36Sopenharmony_ci	if (!asoc && params->sack_assoc_id > SCTP_ALL_ASSOC &&
279362306a36Sopenharmony_ci	    sctp_style(sk, UDP))
279462306a36Sopenharmony_ci		return -EINVAL;
279562306a36Sopenharmony_ci
279662306a36Sopenharmony_ci	if (asoc) {
279762306a36Sopenharmony_ci		sctp_apply_asoc_delayed_ack(params, asoc);
279862306a36Sopenharmony_ci
279962306a36Sopenharmony_ci		return 0;
280062306a36Sopenharmony_ci	}
280162306a36Sopenharmony_ci
280262306a36Sopenharmony_ci	if (sctp_style(sk, TCP))
280362306a36Sopenharmony_ci		params->sack_assoc_id = SCTP_FUTURE_ASSOC;
280462306a36Sopenharmony_ci
280562306a36Sopenharmony_ci	if (params->sack_assoc_id == SCTP_FUTURE_ASSOC ||
280662306a36Sopenharmony_ci	    params->sack_assoc_id == SCTP_ALL_ASSOC) {
280762306a36Sopenharmony_ci		if (params->sack_delay) {
280862306a36Sopenharmony_ci			sp->sackdelay = params->sack_delay;
280962306a36Sopenharmony_ci			sp->param_flags =
281062306a36Sopenharmony_ci				sctp_spp_sackdelay_enable(sp->param_flags);
281162306a36Sopenharmony_ci		}
281262306a36Sopenharmony_ci		if (params->sack_freq == 1) {
281362306a36Sopenharmony_ci			sp->param_flags =
281462306a36Sopenharmony_ci				sctp_spp_sackdelay_disable(sp->param_flags);
281562306a36Sopenharmony_ci		} else if (params->sack_freq > 1) {
281662306a36Sopenharmony_ci			sp->sackfreq = params->sack_freq;
281762306a36Sopenharmony_ci			sp->param_flags =
281862306a36Sopenharmony_ci				sctp_spp_sackdelay_enable(sp->param_flags);
281962306a36Sopenharmony_ci		}
282062306a36Sopenharmony_ci	}
282162306a36Sopenharmony_ci
282262306a36Sopenharmony_ci	if (params->sack_assoc_id == SCTP_CURRENT_ASSOC ||
282362306a36Sopenharmony_ci	    params->sack_assoc_id == SCTP_ALL_ASSOC)
282462306a36Sopenharmony_ci		list_for_each_entry(asoc, &sp->ep->asocs, asocs)
282562306a36Sopenharmony_ci			sctp_apply_asoc_delayed_ack(params, asoc);
282662306a36Sopenharmony_ci
282762306a36Sopenharmony_ci	return 0;
282862306a36Sopenharmony_ci}
282962306a36Sopenharmony_ci
283062306a36Sopenharmony_cistatic int sctp_setsockopt_delayed_ack(struct sock *sk,
283162306a36Sopenharmony_ci				       struct sctp_sack_info *params,
283262306a36Sopenharmony_ci				       unsigned int optlen)
283362306a36Sopenharmony_ci{
283462306a36Sopenharmony_ci	if (optlen == sizeof(struct sctp_assoc_value)) {
283562306a36Sopenharmony_ci		struct sctp_assoc_value *v = (struct sctp_assoc_value *)params;
283662306a36Sopenharmony_ci		struct sctp_sack_info p;
283762306a36Sopenharmony_ci
283862306a36Sopenharmony_ci		pr_warn_ratelimited(DEPRECATED
283962306a36Sopenharmony_ci				    "%s (pid %d) "
284062306a36Sopenharmony_ci				    "Use of struct sctp_assoc_value in delayed_ack socket option.\n"
284162306a36Sopenharmony_ci				    "Use struct sctp_sack_info instead\n",
284262306a36Sopenharmony_ci				    current->comm, task_pid_nr(current));
284362306a36Sopenharmony_ci
284462306a36Sopenharmony_ci		p.sack_assoc_id = v->assoc_id;
284562306a36Sopenharmony_ci		p.sack_delay = v->assoc_value;
284662306a36Sopenharmony_ci		p.sack_freq = v->assoc_value ? 0 : 1;
284762306a36Sopenharmony_ci		return __sctp_setsockopt_delayed_ack(sk, &p);
284862306a36Sopenharmony_ci	}
284962306a36Sopenharmony_ci
285062306a36Sopenharmony_ci	if (optlen != sizeof(struct sctp_sack_info))
285162306a36Sopenharmony_ci		return -EINVAL;
285262306a36Sopenharmony_ci	if (params->sack_delay == 0 && params->sack_freq == 0)
285362306a36Sopenharmony_ci		return 0;
285462306a36Sopenharmony_ci	return __sctp_setsockopt_delayed_ack(sk, params);
285562306a36Sopenharmony_ci}
285662306a36Sopenharmony_ci
285762306a36Sopenharmony_ci/* 7.1.3 Initialization Parameters (SCTP_INITMSG)
285862306a36Sopenharmony_ci *
285962306a36Sopenharmony_ci * Applications can specify protocol parameters for the default association
286062306a36Sopenharmony_ci * initialization.  The option name argument to setsockopt() and getsockopt()
286162306a36Sopenharmony_ci * is SCTP_INITMSG.
286262306a36Sopenharmony_ci *
286362306a36Sopenharmony_ci * Setting initialization parameters is effective only on an unconnected
286462306a36Sopenharmony_ci * socket (for UDP-style sockets only future associations are effected
286562306a36Sopenharmony_ci * by the change).  With TCP-style sockets, this option is inherited by
286662306a36Sopenharmony_ci * sockets derived from a listener socket.
286762306a36Sopenharmony_ci */
286862306a36Sopenharmony_cistatic int sctp_setsockopt_initmsg(struct sock *sk, struct sctp_initmsg *sinit,
286962306a36Sopenharmony_ci				   unsigned int optlen)
287062306a36Sopenharmony_ci{
287162306a36Sopenharmony_ci	struct sctp_sock *sp = sctp_sk(sk);
287262306a36Sopenharmony_ci
287362306a36Sopenharmony_ci	if (optlen != sizeof(struct sctp_initmsg))
287462306a36Sopenharmony_ci		return -EINVAL;
287562306a36Sopenharmony_ci
287662306a36Sopenharmony_ci	if (sinit->sinit_num_ostreams)
287762306a36Sopenharmony_ci		sp->initmsg.sinit_num_ostreams = sinit->sinit_num_ostreams;
287862306a36Sopenharmony_ci	if (sinit->sinit_max_instreams)
287962306a36Sopenharmony_ci		sp->initmsg.sinit_max_instreams = sinit->sinit_max_instreams;
288062306a36Sopenharmony_ci	if (sinit->sinit_max_attempts)
288162306a36Sopenharmony_ci		sp->initmsg.sinit_max_attempts = sinit->sinit_max_attempts;
288262306a36Sopenharmony_ci	if (sinit->sinit_max_init_timeo)
288362306a36Sopenharmony_ci		sp->initmsg.sinit_max_init_timeo = sinit->sinit_max_init_timeo;
288462306a36Sopenharmony_ci
288562306a36Sopenharmony_ci	return 0;
288662306a36Sopenharmony_ci}
288762306a36Sopenharmony_ci
288862306a36Sopenharmony_ci/*
288962306a36Sopenharmony_ci * 7.1.14 Set default send parameters (SCTP_DEFAULT_SEND_PARAM)
289062306a36Sopenharmony_ci *
289162306a36Sopenharmony_ci *   Applications that wish to use the sendto() system call may wish to
289262306a36Sopenharmony_ci *   specify a default set of parameters that would normally be supplied
289362306a36Sopenharmony_ci *   through the inclusion of ancillary data.  This socket option allows
289462306a36Sopenharmony_ci *   such an application to set the default sctp_sndrcvinfo structure.
289562306a36Sopenharmony_ci *   The application that wishes to use this socket option simply passes
289662306a36Sopenharmony_ci *   in to this call the sctp_sndrcvinfo structure defined in Section
289762306a36Sopenharmony_ci *   5.2.2) The input parameters accepted by this call include
289862306a36Sopenharmony_ci *   sinfo_stream, sinfo_flags, sinfo_ppid, sinfo_context,
289962306a36Sopenharmony_ci *   sinfo_timetolive.  The user must provide the sinfo_assoc_id field in
290062306a36Sopenharmony_ci *   to this call if the caller is using the UDP model.
290162306a36Sopenharmony_ci */
290262306a36Sopenharmony_cistatic int sctp_setsockopt_default_send_param(struct sock *sk,
290362306a36Sopenharmony_ci					      struct sctp_sndrcvinfo *info,
290462306a36Sopenharmony_ci					      unsigned int optlen)
290562306a36Sopenharmony_ci{
290662306a36Sopenharmony_ci	struct sctp_sock *sp = sctp_sk(sk);
290762306a36Sopenharmony_ci	struct sctp_association *asoc;
290862306a36Sopenharmony_ci
290962306a36Sopenharmony_ci	if (optlen != sizeof(*info))
291062306a36Sopenharmony_ci		return -EINVAL;
291162306a36Sopenharmony_ci	if (info->sinfo_flags &
291262306a36Sopenharmony_ci	    ~(SCTP_UNORDERED | SCTP_ADDR_OVER |
291362306a36Sopenharmony_ci	      SCTP_ABORT | SCTP_EOF))
291462306a36Sopenharmony_ci		return -EINVAL;
291562306a36Sopenharmony_ci
291662306a36Sopenharmony_ci	asoc = sctp_id2assoc(sk, info->sinfo_assoc_id);
291762306a36Sopenharmony_ci	if (!asoc && info->sinfo_assoc_id > SCTP_ALL_ASSOC &&
291862306a36Sopenharmony_ci	    sctp_style(sk, UDP))
291962306a36Sopenharmony_ci		return -EINVAL;
292062306a36Sopenharmony_ci
292162306a36Sopenharmony_ci	if (asoc) {
292262306a36Sopenharmony_ci		asoc->default_stream = info->sinfo_stream;
292362306a36Sopenharmony_ci		asoc->default_flags = info->sinfo_flags;
292462306a36Sopenharmony_ci		asoc->default_ppid = info->sinfo_ppid;
292562306a36Sopenharmony_ci		asoc->default_context = info->sinfo_context;
292662306a36Sopenharmony_ci		asoc->default_timetolive = info->sinfo_timetolive;
292762306a36Sopenharmony_ci
292862306a36Sopenharmony_ci		return 0;
292962306a36Sopenharmony_ci	}
293062306a36Sopenharmony_ci
293162306a36Sopenharmony_ci	if (sctp_style(sk, TCP))
293262306a36Sopenharmony_ci		info->sinfo_assoc_id = SCTP_FUTURE_ASSOC;
293362306a36Sopenharmony_ci
293462306a36Sopenharmony_ci	if (info->sinfo_assoc_id == SCTP_FUTURE_ASSOC ||
293562306a36Sopenharmony_ci	    info->sinfo_assoc_id == SCTP_ALL_ASSOC) {
293662306a36Sopenharmony_ci		sp->default_stream = info->sinfo_stream;
293762306a36Sopenharmony_ci		sp->default_flags = info->sinfo_flags;
293862306a36Sopenharmony_ci		sp->default_ppid = info->sinfo_ppid;
293962306a36Sopenharmony_ci		sp->default_context = info->sinfo_context;
294062306a36Sopenharmony_ci		sp->default_timetolive = info->sinfo_timetolive;
294162306a36Sopenharmony_ci	}
294262306a36Sopenharmony_ci
294362306a36Sopenharmony_ci	if (info->sinfo_assoc_id == SCTP_CURRENT_ASSOC ||
294462306a36Sopenharmony_ci	    info->sinfo_assoc_id == SCTP_ALL_ASSOC) {
294562306a36Sopenharmony_ci		list_for_each_entry(asoc, &sp->ep->asocs, asocs) {
294662306a36Sopenharmony_ci			asoc->default_stream = info->sinfo_stream;
294762306a36Sopenharmony_ci			asoc->default_flags = info->sinfo_flags;
294862306a36Sopenharmony_ci			asoc->default_ppid = info->sinfo_ppid;
294962306a36Sopenharmony_ci			asoc->default_context = info->sinfo_context;
295062306a36Sopenharmony_ci			asoc->default_timetolive = info->sinfo_timetolive;
295162306a36Sopenharmony_ci		}
295262306a36Sopenharmony_ci	}
295362306a36Sopenharmony_ci
295462306a36Sopenharmony_ci	return 0;
295562306a36Sopenharmony_ci}
295662306a36Sopenharmony_ci
295762306a36Sopenharmony_ci/* RFC6458, Section 8.1.31. Set/get Default Send Parameters
295862306a36Sopenharmony_ci * (SCTP_DEFAULT_SNDINFO)
295962306a36Sopenharmony_ci */
296062306a36Sopenharmony_cistatic int sctp_setsockopt_default_sndinfo(struct sock *sk,
296162306a36Sopenharmony_ci					   struct sctp_sndinfo *info,
296262306a36Sopenharmony_ci					   unsigned int optlen)
296362306a36Sopenharmony_ci{
296462306a36Sopenharmony_ci	struct sctp_sock *sp = sctp_sk(sk);
296562306a36Sopenharmony_ci	struct sctp_association *asoc;
296662306a36Sopenharmony_ci
296762306a36Sopenharmony_ci	if (optlen != sizeof(*info))
296862306a36Sopenharmony_ci		return -EINVAL;
296962306a36Sopenharmony_ci	if (info->snd_flags &
297062306a36Sopenharmony_ci	    ~(SCTP_UNORDERED | SCTP_ADDR_OVER |
297162306a36Sopenharmony_ci	      SCTP_ABORT | SCTP_EOF))
297262306a36Sopenharmony_ci		return -EINVAL;
297362306a36Sopenharmony_ci
297462306a36Sopenharmony_ci	asoc = sctp_id2assoc(sk, info->snd_assoc_id);
297562306a36Sopenharmony_ci	if (!asoc && info->snd_assoc_id > SCTP_ALL_ASSOC &&
297662306a36Sopenharmony_ci	    sctp_style(sk, UDP))
297762306a36Sopenharmony_ci		return -EINVAL;
297862306a36Sopenharmony_ci
297962306a36Sopenharmony_ci	if (asoc) {
298062306a36Sopenharmony_ci		asoc->default_stream = info->snd_sid;
298162306a36Sopenharmony_ci		asoc->default_flags = info->snd_flags;
298262306a36Sopenharmony_ci		asoc->default_ppid = info->snd_ppid;
298362306a36Sopenharmony_ci		asoc->default_context = info->snd_context;
298462306a36Sopenharmony_ci
298562306a36Sopenharmony_ci		return 0;
298662306a36Sopenharmony_ci	}
298762306a36Sopenharmony_ci
298862306a36Sopenharmony_ci	if (sctp_style(sk, TCP))
298962306a36Sopenharmony_ci		info->snd_assoc_id = SCTP_FUTURE_ASSOC;
299062306a36Sopenharmony_ci
299162306a36Sopenharmony_ci	if (info->snd_assoc_id == SCTP_FUTURE_ASSOC ||
299262306a36Sopenharmony_ci	    info->snd_assoc_id == SCTP_ALL_ASSOC) {
299362306a36Sopenharmony_ci		sp->default_stream = info->snd_sid;
299462306a36Sopenharmony_ci		sp->default_flags = info->snd_flags;
299562306a36Sopenharmony_ci		sp->default_ppid = info->snd_ppid;
299662306a36Sopenharmony_ci		sp->default_context = info->snd_context;
299762306a36Sopenharmony_ci	}
299862306a36Sopenharmony_ci
299962306a36Sopenharmony_ci	if (info->snd_assoc_id == SCTP_CURRENT_ASSOC ||
300062306a36Sopenharmony_ci	    info->snd_assoc_id == SCTP_ALL_ASSOC) {
300162306a36Sopenharmony_ci		list_for_each_entry(asoc, &sp->ep->asocs, asocs) {
300262306a36Sopenharmony_ci			asoc->default_stream = info->snd_sid;
300362306a36Sopenharmony_ci			asoc->default_flags = info->snd_flags;
300462306a36Sopenharmony_ci			asoc->default_ppid = info->snd_ppid;
300562306a36Sopenharmony_ci			asoc->default_context = info->snd_context;
300662306a36Sopenharmony_ci		}
300762306a36Sopenharmony_ci	}
300862306a36Sopenharmony_ci
300962306a36Sopenharmony_ci	return 0;
301062306a36Sopenharmony_ci}
301162306a36Sopenharmony_ci
301262306a36Sopenharmony_ci/* 7.1.10 Set Primary Address (SCTP_PRIMARY_ADDR)
301362306a36Sopenharmony_ci *
301462306a36Sopenharmony_ci * Requests that the local SCTP stack use the enclosed peer address as
301562306a36Sopenharmony_ci * the association primary.  The enclosed address must be one of the
301662306a36Sopenharmony_ci * association peer's addresses.
301762306a36Sopenharmony_ci */
301862306a36Sopenharmony_cistatic int sctp_setsockopt_primary_addr(struct sock *sk, struct sctp_prim *prim,
301962306a36Sopenharmony_ci					unsigned int optlen)
302062306a36Sopenharmony_ci{
302162306a36Sopenharmony_ci	struct sctp_transport *trans;
302262306a36Sopenharmony_ci	struct sctp_af *af;
302362306a36Sopenharmony_ci	int err;
302462306a36Sopenharmony_ci
302562306a36Sopenharmony_ci	if (optlen != sizeof(struct sctp_prim))
302662306a36Sopenharmony_ci		return -EINVAL;
302762306a36Sopenharmony_ci
302862306a36Sopenharmony_ci	/* Allow security module to validate address but need address len. */
302962306a36Sopenharmony_ci	af = sctp_get_af_specific(prim->ssp_addr.ss_family);
303062306a36Sopenharmony_ci	if (!af)
303162306a36Sopenharmony_ci		return -EINVAL;
303262306a36Sopenharmony_ci
303362306a36Sopenharmony_ci	err = security_sctp_bind_connect(sk, SCTP_PRIMARY_ADDR,
303462306a36Sopenharmony_ci					 (struct sockaddr *)&prim->ssp_addr,
303562306a36Sopenharmony_ci					 af->sockaddr_len);
303662306a36Sopenharmony_ci	if (err)
303762306a36Sopenharmony_ci		return err;
303862306a36Sopenharmony_ci
303962306a36Sopenharmony_ci	trans = sctp_addr_id2transport(sk, &prim->ssp_addr, prim->ssp_assoc_id);
304062306a36Sopenharmony_ci	if (!trans)
304162306a36Sopenharmony_ci		return -EINVAL;
304262306a36Sopenharmony_ci
304362306a36Sopenharmony_ci	sctp_assoc_set_primary(trans->asoc, trans);
304462306a36Sopenharmony_ci
304562306a36Sopenharmony_ci	return 0;
304662306a36Sopenharmony_ci}
304762306a36Sopenharmony_ci
304862306a36Sopenharmony_ci/*
304962306a36Sopenharmony_ci * 7.1.5 SCTP_NODELAY
305062306a36Sopenharmony_ci *
305162306a36Sopenharmony_ci * Turn on/off any Nagle-like algorithm.  This means that packets are
305262306a36Sopenharmony_ci * generally sent as soon as possible and no unnecessary delays are
305362306a36Sopenharmony_ci * introduced, at the cost of more packets in the network.  Expects an
305462306a36Sopenharmony_ci *  integer boolean flag.
305562306a36Sopenharmony_ci */
305662306a36Sopenharmony_cistatic int sctp_setsockopt_nodelay(struct sock *sk, int *val,
305762306a36Sopenharmony_ci				   unsigned int optlen)
305862306a36Sopenharmony_ci{
305962306a36Sopenharmony_ci	if (optlen < sizeof(int))
306062306a36Sopenharmony_ci		return -EINVAL;
306162306a36Sopenharmony_ci	sctp_sk(sk)->nodelay = (*val == 0) ? 0 : 1;
306262306a36Sopenharmony_ci	return 0;
306362306a36Sopenharmony_ci}
306462306a36Sopenharmony_ci
306562306a36Sopenharmony_ci/*
306662306a36Sopenharmony_ci *
306762306a36Sopenharmony_ci * 7.1.1 SCTP_RTOINFO
306862306a36Sopenharmony_ci *
306962306a36Sopenharmony_ci * The protocol parameters used to initialize and bound retransmission
307062306a36Sopenharmony_ci * timeout (RTO) are tunable. sctp_rtoinfo structure is used to access
307162306a36Sopenharmony_ci * and modify these parameters.
307262306a36Sopenharmony_ci * All parameters are time values, in milliseconds.  A value of 0, when
307362306a36Sopenharmony_ci * modifying the parameters, indicates that the current value should not
307462306a36Sopenharmony_ci * be changed.
307562306a36Sopenharmony_ci *
307662306a36Sopenharmony_ci */
307762306a36Sopenharmony_cistatic int sctp_setsockopt_rtoinfo(struct sock *sk,
307862306a36Sopenharmony_ci				   struct sctp_rtoinfo *rtoinfo,
307962306a36Sopenharmony_ci				   unsigned int optlen)
308062306a36Sopenharmony_ci{
308162306a36Sopenharmony_ci	struct sctp_association *asoc;
308262306a36Sopenharmony_ci	unsigned long rto_min, rto_max;
308362306a36Sopenharmony_ci	struct sctp_sock *sp = sctp_sk(sk);
308462306a36Sopenharmony_ci
308562306a36Sopenharmony_ci	if (optlen != sizeof (struct sctp_rtoinfo))
308662306a36Sopenharmony_ci		return -EINVAL;
308762306a36Sopenharmony_ci
308862306a36Sopenharmony_ci	asoc = sctp_id2assoc(sk, rtoinfo->srto_assoc_id);
308962306a36Sopenharmony_ci
309062306a36Sopenharmony_ci	/* Set the values to the specific association */
309162306a36Sopenharmony_ci	if (!asoc && rtoinfo->srto_assoc_id != SCTP_FUTURE_ASSOC &&
309262306a36Sopenharmony_ci	    sctp_style(sk, UDP))
309362306a36Sopenharmony_ci		return -EINVAL;
309462306a36Sopenharmony_ci
309562306a36Sopenharmony_ci	rto_max = rtoinfo->srto_max;
309662306a36Sopenharmony_ci	rto_min = rtoinfo->srto_min;
309762306a36Sopenharmony_ci
309862306a36Sopenharmony_ci	if (rto_max)
309962306a36Sopenharmony_ci		rto_max = asoc ? msecs_to_jiffies(rto_max) : rto_max;
310062306a36Sopenharmony_ci	else
310162306a36Sopenharmony_ci		rto_max = asoc ? asoc->rto_max : sp->rtoinfo.srto_max;
310262306a36Sopenharmony_ci
310362306a36Sopenharmony_ci	if (rto_min)
310462306a36Sopenharmony_ci		rto_min = asoc ? msecs_to_jiffies(rto_min) : rto_min;
310562306a36Sopenharmony_ci	else
310662306a36Sopenharmony_ci		rto_min = asoc ? asoc->rto_min : sp->rtoinfo.srto_min;
310762306a36Sopenharmony_ci
310862306a36Sopenharmony_ci	if (rto_min > rto_max)
310962306a36Sopenharmony_ci		return -EINVAL;
311062306a36Sopenharmony_ci
311162306a36Sopenharmony_ci	if (asoc) {
311262306a36Sopenharmony_ci		if (rtoinfo->srto_initial != 0)
311362306a36Sopenharmony_ci			asoc->rto_initial =
311462306a36Sopenharmony_ci				msecs_to_jiffies(rtoinfo->srto_initial);
311562306a36Sopenharmony_ci		asoc->rto_max = rto_max;
311662306a36Sopenharmony_ci		asoc->rto_min = rto_min;
311762306a36Sopenharmony_ci	} else {
311862306a36Sopenharmony_ci		/* If there is no association or the association-id = 0
311962306a36Sopenharmony_ci		 * set the values to the endpoint.
312062306a36Sopenharmony_ci		 */
312162306a36Sopenharmony_ci		if (rtoinfo->srto_initial != 0)
312262306a36Sopenharmony_ci			sp->rtoinfo.srto_initial = rtoinfo->srto_initial;
312362306a36Sopenharmony_ci		sp->rtoinfo.srto_max = rto_max;
312462306a36Sopenharmony_ci		sp->rtoinfo.srto_min = rto_min;
312562306a36Sopenharmony_ci	}
312662306a36Sopenharmony_ci
312762306a36Sopenharmony_ci	return 0;
312862306a36Sopenharmony_ci}
312962306a36Sopenharmony_ci
313062306a36Sopenharmony_ci/*
313162306a36Sopenharmony_ci *
313262306a36Sopenharmony_ci * 7.1.2 SCTP_ASSOCINFO
313362306a36Sopenharmony_ci *
313462306a36Sopenharmony_ci * This option is used to tune the maximum retransmission attempts
313562306a36Sopenharmony_ci * of the association.
313662306a36Sopenharmony_ci * Returns an error if the new association retransmission value is
313762306a36Sopenharmony_ci * greater than the sum of the retransmission value  of the peer.
313862306a36Sopenharmony_ci * See [SCTP] for more information.
313962306a36Sopenharmony_ci *
314062306a36Sopenharmony_ci */
314162306a36Sopenharmony_cistatic int sctp_setsockopt_associnfo(struct sock *sk,
314262306a36Sopenharmony_ci				     struct sctp_assocparams *assocparams,
314362306a36Sopenharmony_ci				     unsigned int optlen)
314462306a36Sopenharmony_ci{
314562306a36Sopenharmony_ci
314662306a36Sopenharmony_ci	struct sctp_association *asoc;
314762306a36Sopenharmony_ci
314862306a36Sopenharmony_ci	if (optlen != sizeof(struct sctp_assocparams))
314962306a36Sopenharmony_ci		return -EINVAL;
315062306a36Sopenharmony_ci
315162306a36Sopenharmony_ci	asoc = sctp_id2assoc(sk, assocparams->sasoc_assoc_id);
315262306a36Sopenharmony_ci
315362306a36Sopenharmony_ci	if (!asoc && assocparams->sasoc_assoc_id != SCTP_FUTURE_ASSOC &&
315462306a36Sopenharmony_ci	    sctp_style(sk, UDP))
315562306a36Sopenharmony_ci		return -EINVAL;
315662306a36Sopenharmony_ci
315762306a36Sopenharmony_ci	/* Set the values to the specific association */
315862306a36Sopenharmony_ci	if (asoc) {
315962306a36Sopenharmony_ci		if (assocparams->sasoc_asocmaxrxt != 0) {
316062306a36Sopenharmony_ci			__u32 path_sum = 0;
316162306a36Sopenharmony_ci			int   paths = 0;
316262306a36Sopenharmony_ci			struct sctp_transport *peer_addr;
316362306a36Sopenharmony_ci
316462306a36Sopenharmony_ci			list_for_each_entry(peer_addr, &asoc->peer.transport_addr_list,
316562306a36Sopenharmony_ci					transports) {
316662306a36Sopenharmony_ci				path_sum += peer_addr->pathmaxrxt;
316762306a36Sopenharmony_ci				paths++;
316862306a36Sopenharmony_ci			}
316962306a36Sopenharmony_ci
317062306a36Sopenharmony_ci			/* Only validate asocmaxrxt if we have more than
317162306a36Sopenharmony_ci			 * one path/transport.  We do this because path
317262306a36Sopenharmony_ci			 * retransmissions are only counted when we have more
317362306a36Sopenharmony_ci			 * then one path.
317462306a36Sopenharmony_ci			 */
317562306a36Sopenharmony_ci			if (paths > 1 &&
317662306a36Sopenharmony_ci			    assocparams->sasoc_asocmaxrxt > path_sum)
317762306a36Sopenharmony_ci				return -EINVAL;
317862306a36Sopenharmony_ci
317962306a36Sopenharmony_ci			asoc->max_retrans = assocparams->sasoc_asocmaxrxt;
318062306a36Sopenharmony_ci		}
318162306a36Sopenharmony_ci
318262306a36Sopenharmony_ci		if (assocparams->sasoc_cookie_life != 0)
318362306a36Sopenharmony_ci			asoc->cookie_life =
318462306a36Sopenharmony_ci				ms_to_ktime(assocparams->sasoc_cookie_life);
318562306a36Sopenharmony_ci	} else {
318662306a36Sopenharmony_ci		/* Set the values to the endpoint */
318762306a36Sopenharmony_ci		struct sctp_sock *sp = sctp_sk(sk);
318862306a36Sopenharmony_ci
318962306a36Sopenharmony_ci		if (assocparams->sasoc_asocmaxrxt != 0)
319062306a36Sopenharmony_ci			sp->assocparams.sasoc_asocmaxrxt =
319162306a36Sopenharmony_ci						assocparams->sasoc_asocmaxrxt;
319262306a36Sopenharmony_ci		if (assocparams->sasoc_cookie_life != 0)
319362306a36Sopenharmony_ci			sp->assocparams.sasoc_cookie_life =
319462306a36Sopenharmony_ci						assocparams->sasoc_cookie_life;
319562306a36Sopenharmony_ci	}
319662306a36Sopenharmony_ci	return 0;
319762306a36Sopenharmony_ci}
319862306a36Sopenharmony_ci
319962306a36Sopenharmony_ci/*
320062306a36Sopenharmony_ci * 7.1.16 Set/clear IPv4 mapped addresses (SCTP_I_WANT_MAPPED_V4_ADDR)
320162306a36Sopenharmony_ci *
320262306a36Sopenharmony_ci * This socket option is a boolean flag which turns on or off mapped V4
320362306a36Sopenharmony_ci * addresses.  If this option is turned on and the socket is type
320462306a36Sopenharmony_ci * PF_INET6, then IPv4 addresses will be mapped to V6 representation.
320562306a36Sopenharmony_ci * If this option is turned off, then no mapping will be done of V4
320662306a36Sopenharmony_ci * addresses and a user will receive both PF_INET6 and PF_INET type
320762306a36Sopenharmony_ci * addresses on the socket.
320862306a36Sopenharmony_ci */
320962306a36Sopenharmony_cistatic int sctp_setsockopt_mappedv4(struct sock *sk, int *val,
321062306a36Sopenharmony_ci				    unsigned int optlen)
321162306a36Sopenharmony_ci{
321262306a36Sopenharmony_ci	struct sctp_sock *sp = sctp_sk(sk);
321362306a36Sopenharmony_ci
321462306a36Sopenharmony_ci	if (optlen < sizeof(int))
321562306a36Sopenharmony_ci		return -EINVAL;
321662306a36Sopenharmony_ci	if (*val)
321762306a36Sopenharmony_ci		sp->v4mapped = 1;
321862306a36Sopenharmony_ci	else
321962306a36Sopenharmony_ci		sp->v4mapped = 0;
322062306a36Sopenharmony_ci
322162306a36Sopenharmony_ci	return 0;
322262306a36Sopenharmony_ci}
322362306a36Sopenharmony_ci
322462306a36Sopenharmony_ci/*
322562306a36Sopenharmony_ci * 8.1.16.  Get or Set the Maximum Fragmentation Size (SCTP_MAXSEG)
322662306a36Sopenharmony_ci * This option will get or set the maximum size to put in any outgoing
322762306a36Sopenharmony_ci * SCTP DATA chunk.  If a message is larger than this size it will be
322862306a36Sopenharmony_ci * fragmented by SCTP into the specified size.  Note that the underlying
322962306a36Sopenharmony_ci * SCTP implementation may fragment into smaller sized chunks when the
323062306a36Sopenharmony_ci * PMTU of the underlying association is smaller than the value set by
323162306a36Sopenharmony_ci * the user.  The default value for this option is '0' which indicates
323262306a36Sopenharmony_ci * the user is NOT limiting fragmentation and only the PMTU will effect
323362306a36Sopenharmony_ci * SCTP's choice of DATA chunk size.  Note also that values set larger
323462306a36Sopenharmony_ci * than the maximum size of an IP datagram will effectively let SCTP
323562306a36Sopenharmony_ci * control fragmentation (i.e. the same as setting this option to 0).
323662306a36Sopenharmony_ci *
323762306a36Sopenharmony_ci * The following structure is used to access and modify this parameter:
323862306a36Sopenharmony_ci *
323962306a36Sopenharmony_ci * struct sctp_assoc_value {
324062306a36Sopenharmony_ci *   sctp_assoc_t assoc_id;
324162306a36Sopenharmony_ci *   uint32_t assoc_value;
324262306a36Sopenharmony_ci * };
324362306a36Sopenharmony_ci *
324462306a36Sopenharmony_ci * assoc_id:  This parameter is ignored for one-to-one style sockets.
324562306a36Sopenharmony_ci *    For one-to-many style sockets this parameter indicates which
324662306a36Sopenharmony_ci *    association the user is performing an action upon.  Note that if
324762306a36Sopenharmony_ci *    this field's value is zero then the endpoints default value is
324862306a36Sopenharmony_ci *    changed (effecting future associations only).
324962306a36Sopenharmony_ci * assoc_value:  This parameter specifies the maximum size in bytes.
325062306a36Sopenharmony_ci */
325162306a36Sopenharmony_cistatic int sctp_setsockopt_maxseg(struct sock *sk,
325262306a36Sopenharmony_ci				  struct sctp_assoc_value *params,
325362306a36Sopenharmony_ci				  unsigned int optlen)
325462306a36Sopenharmony_ci{
325562306a36Sopenharmony_ci	struct sctp_sock *sp = sctp_sk(sk);
325662306a36Sopenharmony_ci	struct sctp_association *asoc;
325762306a36Sopenharmony_ci	sctp_assoc_t assoc_id;
325862306a36Sopenharmony_ci	int val;
325962306a36Sopenharmony_ci
326062306a36Sopenharmony_ci	if (optlen == sizeof(int)) {
326162306a36Sopenharmony_ci		pr_warn_ratelimited(DEPRECATED
326262306a36Sopenharmony_ci				    "%s (pid %d) "
326362306a36Sopenharmony_ci				    "Use of int in maxseg socket option.\n"
326462306a36Sopenharmony_ci				    "Use struct sctp_assoc_value instead\n",
326562306a36Sopenharmony_ci				    current->comm, task_pid_nr(current));
326662306a36Sopenharmony_ci		assoc_id = SCTP_FUTURE_ASSOC;
326762306a36Sopenharmony_ci		val = *(int *)params;
326862306a36Sopenharmony_ci	} else if (optlen == sizeof(struct sctp_assoc_value)) {
326962306a36Sopenharmony_ci		assoc_id = params->assoc_id;
327062306a36Sopenharmony_ci		val = params->assoc_value;
327162306a36Sopenharmony_ci	} else {
327262306a36Sopenharmony_ci		return -EINVAL;
327362306a36Sopenharmony_ci	}
327462306a36Sopenharmony_ci
327562306a36Sopenharmony_ci	asoc = sctp_id2assoc(sk, assoc_id);
327662306a36Sopenharmony_ci	if (!asoc && assoc_id != SCTP_FUTURE_ASSOC &&
327762306a36Sopenharmony_ci	    sctp_style(sk, UDP))
327862306a36Sopenharmony_ci		return -EINVAL;
327962306a36Sopenharmony_ci
328062306a36Sopenharmony_ci	if (val) {
328162306a36Sopenharmony_ci		int min_len, max_len;
328262306a36Sopenharmony_ci		__u16 datasize = asoc ? sctp_datachk_len(&asoc->stream) :
328362306a36Sopenharmony_ci				 sizeof(struct sctp_data_chunk);
328462306a36Sopenharmony_ci
328562306a36Sopenharmony_ci		min_len = sctp_min_frag_point(sp, datasize);
328662306a36Sopenharmony_ci		max_len = SCTP_MAX_CHUNK_LEN - datasize;
328762306a36Sopenharmony_ci
328862306a36Sopenharmony_ci		if (val < min_len || val > max_len)
328962306a36Sopenharmony_ci			return -EINVAL;
329062306a36Sopenharmony_ci	}
329162306a36Sopenharmony_ci
329262306a36Sopenharmony_ci	if (asoc) {
329362306a36Sopenharmony_ci		asoc->user_frag = val;
329462306a36Sopenharmony_ci		sctp_assoc_update_frag_point(asoc);
329562306a36Sopenharmony_ci	} else {
329662306a36Sopenharmony_ci		sp->user_frag = val;
329762306a36Sopenharmony_ci	}
329862306a36Sopenharmony_ci
329962306a36Sopenharmony_ci	return 0;
330062306a36Sopenharmony_ci}
330162306a36Sopenharmony_ci
330262306a36Sopenharmony_ci
330362306a36Sopenharmony_ci/*
330462306a36Sopenharmony_ci *  7.1.9 Set Peer Primary Address (SCTP_SET_PEER_PRIMARY_ADDR)
330562306a36Sopenharmony_ci *
330662306a36Sopenharmony_ci *   Requests that the peer mark the enclosed address as the association
330762306a36Sopenharmony_ci *   primary. The enclosed address must be one of the association's
330862306a36Sopenharmony_ci *   locally bound addresses. The following structure is used to make a
330962306a36Sopenharmony_ci *   set primary request:
331062306a36Sopenharmony_ci */
331162306a36Sopenharmony_cistatic int sctp_setsockopt_peer_primary_addr(struct sock *sk,
331262306a36Sopenharmony_ci					     struct sctp_setpeerprim *prim,
331362306a36Sopenharmony_ci					     unsigned int optlen)
331462306a36Sopenharmony_ci{
331562306a36Sopenharmony_ci	struct sctp_sock	*sp;
331662306a36Sopenharmony_ci	struct sctp_association	*asoc = NULL;
331762306a36Sopenharmony_ci	struct sctp_chunk	*chunk;
331862306a36Sopenharmony_ci	struct sctp_af		*af;
331962306a36Sopenharmony_ci	int 			err;
332062306a36Sopenharmony_ci
332162306a36Sopenharmony_ci	sp = sctp_sk(sk);
332262306a36Sopenharmony_ci
332362306a36Sopenharmony_ci	if (!sp->ep->asconf_enable)
332462306a36Sopenharmony_ci		return -EPERM;
332562306a36Sopenharmony_ci
332662306a36Sopenharmony_ci	if (optlen != sizeof(struct sctp_setpeerprim))
332762306a36Sopenharmony_ci		return -EINVAL;
332862306a36Sopenharmony_ci
332962306a36Sopenharmony_ci	asoc = sctp_id2assoc(sk, prim->sspp_assoc_id);
333062306a36Sopenharmony_ci	if (!asoc)
333162306a36Sopenharmony_ci		return -EINVAL;
333262306a36Sopenharmony_ci
333362306a36Sopenharmony_ci	if (!asoc->peer.asconf_capable)
333462306a36Sopenharmony_ci		return -EPERM;
333562306a36Sopenharmony_ci
333662306a36Sopenharmony_ci	if (asoc->peer.addip_disabled_mask & SCTP_PARAM_SET_PRIMARY)
333762306a36Sopenharmony_ci		return -EPERM;
333862306a36Sopenharmony_ci
333962306a36Sopenharmony_ci	if (!sctp_state(asoc, ESTABLISHED))
334062306a36Sopenharmony_ci		return -ENOTCONN;
334162306a36Sopenharmony_ci
334262306a36Sopenharmony_ci	af = sctp_get_af_specific(prim->sspp_addr.ss_family);
334362306a36Sopenharmony_ci	if (!af)
334462306a36Sopenharmony_ci		return -EINVAL;
334562306a36Sopenharmony_ci
334662306a36Sopenharmony_ci	if (!af->addr_valid((union sctp_addr *)&prim->sspp_addr, sp, NULL))
334762306a36Sopenharmony_ci		return -EADDRNOTAVAIL;
334862306a36Sopenharmony_ci
334962306a36Sopenharmony_ci	if (!sctp_assoc_lookup_laddr(asoc, (union sctp_addr *)&prim->sspp_addr))
335062306a36Sopenharmony_ci		return -EADDRNOTAVAIL;
335162306a36Sopenharmony_ci
335262306a36Sopenharmony_ci	/* Allow security module to validate address. */
335362306a36Sopenharmony_ci	err = security_sctp_bind_connect(sk, SCTP_SET_PEER_PRIMARY_ADDR,
335462306a36Sopenharmony_ci					 (struct sockaddr *)&prim->sspp_addr,
335562306a36Sopenharmony_ci					 af->sockaddr_len);
335662306a36Sopenharmony_ci	if (err)
335762306a36Sopenharmony_ci		return err;
335862306a36Sopenharmony_ci
335962306a36Sopenharmony_ci	/* Create an ASCONF chunk with SET_PRIMARY parameter	*/
336062306a36Sopenharmony_ci	chunk = sctp_make_asconf_set_prim(asoc,
336162306a36Sopenharmony_ci					  (union sctp_addr *)&prim->sspp_addr);
336262306a36Sopenharmony_ci	if (!chunk)
336362306a36Sopenharmony_ci		return -ENOMEM;
336462306a36Sopenharmony_ci
336562306a36Sopenharmony_ci	err = sctp_send_asconf(asoc, chunk);
336662306a36Sopenharmony_ci
336762306a36Sopenharmony_ci	pr_debug("%s: we set peer primary addr primitively\n", __func__);
336862306a36Sopenharmony_ci
336962306a36Sopenharmony_ci	return err;
337062306a36Sopenharmony_ci}
337162306a36Sopenharmony_ci
337262306a36Sopenharmony_cistatic int sctp_setsockopt_adaptation_layer(struct sock *sk,
337362306a36Sopenharmony_ci					    struct sctp_setadaptation *adapt,
337462306a36Sopenharmony_ci					    unsigned int optlen)
337562306a36Sopenharmony_ci{
337662306a36Sopenharmony_ci	if (optlen != sizeof(struct sctp_setadaptation))
337762306a36Sopenharmony_ci		return -EINVAL;
337862306a36Sopenharmony_ci
337962306a36Sopenharmony_ci	sctp_sk(sk)->adaptation_ind = adapt->ssb_adaptation_ind;
338062306a36Sopenharmony_ci
338162306a36Sopenharmony_ci	return 0;
338262306a36Sopenharmony_ci}
338362306a36Sopenharmony_ci
338462306a36Sopenharmony_ci/*
338562306a36Sopenharmony_ci * 7.1.29.  Set or Get the default context (SCTP_CONTEXT)
338662306a36Sopenharmony_ci *
338762306a36Sopenharmony_ci * The context field in the sctp_sndrcvinfo structure is normally only
338862306a36Sopenharmony_ci * used when a failed message is retrieved holding the value that was
338962306a36Sopenharmony_ci * sent down on the actual send call.  This option allows the setting of
339062306a36Sopenharmony_ci * a default context on an association basis that will be received on
339162306a36Sopenharmony_ci * reading messages from the peer.  This is especially helpful in the
339262306a36Sopenharmony_ci * one-2-many model for an application to keep some reference to an
339362306a36Sopenharmony_ci * internal state machine that is processing messages on the
339462306a36Sopenharmony_ci * association.  Note that the setting of this value only effects
339562306a36Sopenharmony_ci * received messages from the peer and does not effect the value that is
339662306a36Sopenharmony_ci * saved with outbound messages.
339762306a36Sopenharmony_ci */
339862306a36Sopenharmony_cistatic int sctp_setsockopt_context(struct sock *sk,
339962306a36Sopenharmony_ci				   struct sctp_assoc_value *params,
340062306a36Sopenharmony_ci				   unsigned int optlen)
340162306a36Sopenharmony_ci{
340262306a36Sopenharmony_ci	struct sctp_sock *sp = sctp_sk(sk);
340362306a36Sopenharmony_ci	struct sctp_association *asoc;
340462306a36Sopenharmony_ci
340562306a36Sopenharmony_ci	if (optlen != sizeof(struct sctp_assoc_value))
340662306a36Sopenharmony_ci		return -EINVAL;
340762306a36Sopenharmony_ci
340862306a36Sopenharmony_ci	asoc = sctp_id2assoc(sk, params->assoc_id);
340962306a36Sopenharmony_ci	if (!asoc && params->assoc_id > SCTP_ALL_ASSOC &&
341062306a36Sopenharmony_ci	    sctp_style(sk, UDP))
341162306a36Sopenharmony_ci		return -EINVAL;
341262306a36Sopenharmony_ci
341362306a36Sopenharmony_ci	if (asoc) {
341462306a36Sopenharmony_ci		asoc->default_rcv_context = params->assoc_value;
341562306a36Sopenharmony_ci
341662306a36Sopenharmony_ci		return 0;
341762306a36Sopenharmony_ci	}
341862306a36Sopenharmony_ci
341962306a36Sopenharmony_ci	if (sctp_style(sk, TCP))
342062306a36Sopenharmony_ci		params->assoc_id = SCTP_FUTURE_ASSOC;
342162306a36Sopenharmony_ci
342262306a36Sopenharmony_ci	if (params->assoc_id == SCTP_FUTURE_ASSOC ||
342362306a36Sopenharmony_ci	    params->assoc_id == SCTP_ALL_ASSOC)
342462306a36Sopenharmony_ci		sp->default_rcv_context = params->assoc_value;
342562306a36Sopenharmony_ci
342662306a36Sopenharmony_ci	if (params->assoc_id == SCTP_CURRENT_ASSOC ||
342762306a36Sopenharmony_ci	    params->assoc_id == SCTP_ALL_ASSOC)
342862306a36Sopenharmony_ci		list_for_each_entry(asoc, &sp->ep->asocs, asocs)
342962306a36Sopenharmony_ci			asoc->default_rcv_context = params->assoc_value;
343062306a36Sopenharmony_ci
343162306a36Sopenharmony_ci	return 0;
343262306a36Sopenharmony_ci}
343362306a36Sopenharmony_ci
343462306a36Sopenharmony_ci/*
343562306a36Sopenharmony_ci * 7.1.24.  Get or set fragmented interleave (SCTP_FRAGMENT_INTERLEAVE)
343662306a36Sopenharmony_ci *
343762306a36Sopenharmony_ci * This options will at a minimum specify if the implementation is doing
343862306a36Sopenharmony_ci * fragmented interleave.  Fragmented interleave, for a one to many
343962306a36Sopenharmony_ci * socket, is when subsequent calls to receive a message may return
344062306a36Sopenharmony_ci * parts of messages from different associations.  Some implementations
344162306a36Sopenharmony_ci * may allow you to turn this value on or off.  If so, when turned off,
344262306a36Sopenharmony_ci * no fragment interleave will occur (which will cause a head of line
344362306a36Sopenharmony_ci * blocking amongst multiple associations sharing the same one to many
344462306a36Sopenharmony_ci * socket).  When this option is turned on, then each receive call may
344562306a36Sopenharmony_ci * come from a different association (thus the user must receive data
344662306a36Sopenharmony_ci * with the extended calls (e.g. sctp_recvmsg) to keep track of which
344762306a36Sopenharmony_ci * association each receive belongs to.
344862306a36Sopenharmony_ci *
344962306a36Sopenharmony_ci * This option takes a boolean value.  A non-zero value indicates that
345062306a36Sopenharmony_ci * fragmented interleave is on.  A value of zero indicates that
345162306a36Sopenharmony_ci * fragmented interleave is off.
345262306a36Sopenharmony_ci *
345362306a36Sopenharmony_ci * Note that it is important that an implementation that allows this
345462306a36Sopenharmony_ci * option to be turned on, have it off by default.  Otherwise an unaware
345562306a36Sopenharmony_ci * application using the one to many model may become confused and act
345662306a36Sopenharmony_ci * incorrectly.
345762306a36Sopenharmony_ci */
345862306a36Sopenharmony_cistatic int sctp_setsockopt_fragment_interleave(struct sock *sk, int *val,
345962306a36Sopenharmony_ci					       unsigned int optlen)
346062306a36Sopenharmony_ci{
346162306a36Sopenharmony_ci	if (optlen != sizeof(int))
346262306a36Sopenharmony_ci		return -EINVAL;
346362306a36Sopenharmony_ci
346462306a36Sopenharmony_ci	sctp_sk(sk)->frag_interleave = !!*val;
346562306a36Sopenharmony_ci
346662306a36Sopenharmony_ci	if (!sctp_sk(sk)->frag_interleave)
346762306a36Sopenharmony_ci		sctp_sk(sk)->ep->intl_enable = 0;
346862306a36Sopenharmony_ci
346962306a36Sopenharmony_ci	return 0;
347062306a36Sopenharmony_ci}
347162306a36Sopenharmony_ci
347262306a36Sopenharmony_ci/*
347362306a36Sopenharmony_ci * 8.1.21.  Set or Get the SCTP Partial Delivery Point
347462306a36Sopenharmony_ci *       (SCTP_PARTIAL_DELIVERY_POINT)
347562306a36Sopenharmony_ci *
347662306a36Sopenharmony_ci * This option will set or get the SCTP partial delivery point.  This
347762306a36Sopenharmony_ci * point is the size of a message where the partial delivery API will be
347862306a36Sopenharmony_ci * invoked to help free up rwnd space for the peer.  Setting this to a
347962306a36Sopenharmony_ci * lower value will cause partial deliveries to happen more often.  The
348062306a36Sopenharmony_ci * calls argument is an integer that sets or gets the partial delivery
348162306a36Sopenharmony_ci * point.  Note also that the call will fail if the user attempts to set
348262306a36Sopenharmony_ci * this value larger than the socket receive buffer size.
348362306a36Sopenharmony_ci *
348462306a36Sopenharmony_ci * Note that any single message having a length smaller than or equal to
348562306a36Sopenharmony_ci * the SCTP partial delivery point will be delivered in one single read
348662306a36Sopenharmony_ci * call as long as the user provided buffer is large enough to hold the
348762306a36Sopenharmony_ci * message.
348862306a36Sopenharmony_ci */
348962306a36Sopenharmony_cistatic int sctp_setsockopt_partial_delivery_point(struct sock *sk, u32 *val,
349062306a36Sopenharmony_ci						  unsigned int optlen)
349162306a36Sopenharmony_ci{
349262306a36Sopenharmony_ci	if (optlen != sizeof(u32))
349362306a36Sopenharmony_ci		return -EINVAL;
349462306a36Sopenharmony_ci
349562306a36Sopenharmony_ci	/* Note: We double the receive buffer from what the user sets
349662306a36Sopenharmony_ci	 * it to be, also initial rwnd is based on rcvbuf/2.
349762306a36Sopenharmony_ci	 */
349862306a36Sopenharmony_ci	if (*val > (sk->sk_rcvbuf >> 1))
349962306a36Sopenharmony_ci		return -EINVAL;
350062306a36Sopenharmony_ci
350162306a36Sopenharmony_ci	sctp_sk(sk)->pd_point = *val;
350262306a36Sopenharmony_ci
350362306a36Sopenharmony_ci	return 0; /* is this the right error code? */
350462306a36Sopenharmony_ci}
350562306a36Sopenharmony_ci
350662306a36Sopenharmony_ci/*
350762306a36Sopenharmony_ci * 7.1.28.  Set or Get the maximum burst (SCTP_MAX_BURST)
350862306a36Sopenharmony_ci *
350962306a36Sopenharmony_ci * This option will allow a user to change the maximum burst of packets
351062306a36Sopenharmony_ci * that can be emitted by this association.  Note that the default value
351162306a36Sopenharmony_ci * is 4, and some implementations may restrict this setting so that it
351262306a36Sopenharmony_ci * can only be lowered.
351362306a36Sopenharmony_ci *
351462306a36Sopenharmony_ci * NOTE: This text doesn't seem right.  Do this on a socket basis with
351562306a36Sopenharmony_ci * future associations inheriting the socket value.
351662306a36Sopenharmony_ci */
351762306a36Sopenharmony_cistatic int sctp_setsockopt_maxburst(struct sock *sk,
351862306a36Sopenharmony_ci				    struct sctp_assoc_value *params,
351962306a36Sopenharmony_ci				    unsigned int optlen)
352062306a36Sopenharmony_ci{
352162306a36Sopenharmony_ci	struct sctp_sock *sp = sctp_sk(sk);
352262306a36Sopenharmony_ci	struct sctp_association *asoc;
352362306a36Sopenharmony_ci	sctp_assoc_t assoc_id;
352462306a36Sopenharmony_ci	u32 assoc_value;
352562306a36Sopenharmony_ci
352662306a36Sopenharmony_ci	if (optlen == sizeof(int)) {
352762306a36Sopenharmony_ci		pr_warn_ratelimited(DEPRECATED
352862306a36Sopenharmony_ci				    "%s (pid %d) "
352962306a36Sopenharmony_ci				    "Use of int in max_burst socket option deprecated.\n"
353062306a36Sopenharmony_ci				    "Use struct sctp_assoc_value instead\n",
353162306a36Sopenharmony_ci				    current->comm, task_pid_nr(current));
353262306a36Sopenharmony_ci		assoc_id = SCTP_FUTURE_ASSOC;
353362306a36Sopenharmony_ci		assoc_value = *((int *)params);
353462306a36Sopenharmony_ci	} else if (optlen == sizeof(struct sctp_assoc_value)) {
353562306a36Sopenharmony_ci		assoc_id = params->assoc_id;
353662306a36Sopenharmony_ci		assoc_value = params->assoc_value;
353762306a36Sopenharmony_ci	} else
353862306a36Sopenharmony_ci		return -EINVAL;
353962306a36Sopenharmony_ci
354062306a36Sopenharmony_ci	asoc = sctp_id2assoc(sk, assoc_id);
354162306a36Sopenharmony_ci	if (!asoc && assoc_id > SCTP_ALL_ASSOC && sctp_style(sk, UDP))
354262306a36Sopenharmony_ci		return -EINVAL;
354362306a36Sopenharmony_ci
354462306a36Sopenharmony_ci	if (asoc) {
354562306a36Sopenharmony_ci		asoc->max_burst = assoc_value;
354662306a36Sopenharmony_ci
354762306a36Sopenharmony_ci		return 0;
354862306a36Sopenharmony_ci	}
354962306a36Sopenharmony_ci
355062306a36Sopenharmony_ci	if (sctp_style(sk, TCP))
355162306a36Sopenharmony_ci		assoc_id = SCTP_FUTURE_ASSOC;
355262306a36Sopenharmony_ci
355362306a36Sopenharmony_ci	if (assoc_id == SCTP_FUTURE_ASSOC || assoc_id == SCTP_ALL_ASSOC)
355462306a36Sopenharmony_ci		sp->max_burst = assoc_value;
355562306a36Sopenharmony_ci
355662306a36Sopenharmony_ci	if (assoc_id == SCTP_CURRENT_ASSOC || assoc_id == SCTP_ALL_ASSOC)
355762306a36Sopenharmony_ci		list_for_each_entry(asoc, &sp->ep->asocs, asocs)
355862306a36Sopenharmony_ci			asoc->max_burst = assoc_value;
355962306a36Sopenharmony_ci
356062306a36Sopenharmony_ci	return 0;
356162306a36Sopenharmony_ci}
356262306a36Sopenharmony_ci
356362306a36Sopenharmony_ci/*
356462306a36Sopenharmony_ci * 7.1.18.  Add a chunk that must be authenticated (SCTP_AUTH_CHUNK)
356562306a36Sopenharmony_ci *
356662306a36Sopenharmony_ci * This set option adds a chunk type that the user is requesting to be
356762306a36Sopenharmony_ci * received only in an authenticated way.  Changes to the list of chunks
356862306a36Sopenharmony_ci * will only effect future associations on the socket.
356962306a36Sopenharmony_ci */
357062306a36Sopenharmony_cistatic int sctp_setsockopt_auth_chunk(struct sock *sk,
357162306a36Sopenharmony_ci				      struct sctp_authchunk *val,
357262306a36Sopenharmony_ci				      unsigned int optlen)
357362306a36Sopenharmony_ci{
357462306a36Sopenharmony_ci	struct sctp_endpoint *ep = sctp_sk(sk)->ep;
357562306a36Sopenharmony_ci
357662306a36Sopenharmony_ci	if (!ep->auth_enable)
357762306a36Sopenharmony_ci		return -EACCES;
357862306a36Sopenharmony_ci
357962306a36Sopenharmony_ci	if (optlen != sizeof(struct sctp_authchunk))
358062306a36Sopenharmony_ci		return -EINVAL;
358162306a36Sopenharmony_ci
358262306a36Sopenharmony_ci	switch (val->sauth_chunk) {
358362306a36Sopenharmony_ci	case SCTP_CID_INIT:
358462306a36Sopenharmony_ci	case SCTP_CID_INIT_ACK:
358562306a36Sopenharmony_ci	case SCTP_CID_SHUTDOWN_COMPLETE:
358662306a36Sopenharmony_ci	case SCTP_CID_AUTH:
358762306a36Sopenharmony_ci		return -EINVAL;
358862306a36Sopenharmony_ci	}
358962306a36Sopenharmony_ci
359062306a36Sopenharmony_ci	/* add this chunk id to the endpoint */
359162306a36Sopenharmony_ci	return sctp_auth_ep_add_chunkid(ep, val->sauth_chunk);
359262306a36Sopenharmony_ci}
359362306a36Sopenharmony_ci
359462306a36Sopenharmony_ci/*
359562306a36Sopenharmony_ci * 7.1.19.  Get or set the list of supported HMAC Identifiers (SCTP_HMAC_IDENT)
359662306a36Sopenharmony_ci *
359762306a36Sopenharmony_ci * This option gets or sets the list of HMAC algorithms that the local
359862306a36Sopenharmony_ci * endpoint requires the peer to use.
359962306a36Sopenharmony_ci */
360062306a36Sopenharmony_cistatic int sctp_setsockopt_hmac_ident(struct sock *sk,
360162306a36Sopenharmony_ci				      struct sctp_hmacalgo *hmacs,
360262306a36Sopenharmony_ci				      unsigned int optlen)
360362306a36Sopenharmony_ci{
360462306a36Sopenharmony_ci	struct sctp_endpoint *ep = sctp_sk(sk)->ep;
360562306a36Sopenharmony_ci	u32 idents;
360662306a36Sopenharmony_ci
360762306a36Sopenharmony_ci	if (!ep->auth_enable)
360862306a36Sopenharmony_ci		return -EACCES;
360962306a36Sopenharmony_ci
361062306a36Sopenharmony_ci	if (optlen < sizeof(struct sctp_hmacalgo))
361162306a36Sopenharmony_ci		return -EINVAL;
361262306a36Sopenharmony_ci	optlen = min_t(unsigned int, optlen, sizeof(struct sctp_hmacalgo) +
361362306a36Sopenharmony_ci					     SCTP_AUTH_NUM_HMACS * sizeof(u16));
361462306a36Sopenharmony_ci
361562306a36Sopenharmony_ci	idents = hmacs->shmac_num_idents;
361662306a36Sopenharmony_ci	if (idents == 0 || idents > SCTP_AUTH_NUM_HMACS ||
361762306a36Sopenharmony_ci	    (idents * sizeof(u16)) > (optlen - sizeof(struct sctp_hmacalgo)))
361862306a36Sopenharmony_ci		return -EINVAL;
361962306a36Sopenharmony_ci
362062306a36Sopenharmony_ci	return sctp_auth_ep_set_hmacs(ep, hmacs);
362162306a36Sopenharmony_ci}
362262306a36Sopenharmony_ci
362362306a36Sopenharmony_ci/*
362462306a36Sopenharmony_ci * 7.1.20.  Set a shared key (SCTP_AUTH_KEY)
362562306a36Sopenharmony_ci *
362662306a36Sopenharmony_ci * This option will set a shared secret key which is used to build an
362762306a36Sopenharmony_ci * association shared key.
362862306a36Sopenharmony_ci */
362962306a36Sopenharmony_cistatic int sctp_setsockopt_auth_key(struct sock *sk,
363062306a36Sopenharmony_ci				    struct sctp_authkey *authkey,
363162306a36Sopenharmony_ci				    unsigned int optlen)
363262306a36Sopenharmony_ci{
363362306a36Sopenharmony_ci	struct sctp_endpoint *ep = sctp_sk(sk)->ep;
363462306a36Sopenharmony_ci	struct sctp_association *asoc;
363562306a36Sopenharmony_ci	int ret = -EINVAL;
363662306a36Sopenharmony_ci
363762306a36Sopenharmony_ci	if (optlen <= sizeof(struct sctp_authkey))
363862306a36Sopenharmony_ci		return -EINVAL;
363962306a36Sopenharmony_ci	/* authkey->sca_keylength is u16, so optlen can't be bigger than
364062306a36Sopenharmony_ci	 * this.
364162306a36Sopenharmony_ci	 */
364262306a36Sopenharmony_ci	optlen = min_t(unsigned int, optlen, USHRT_MAX + sizeof(*authkey));
364362306a36Sopenharmony_ci
364462306a36Sopenharmony_ci	if (authkey->sca_keylength > optlen - sizeof(*authkey))
364562306a36Sopenharmony_ci		goto out;
364662306a36Sopenharmony_ci
364762306a36Sopenharmony_ci	asoc = sctp_id2assoc(sk, authkey->sca_assoc_id);
364862306a36Sopenharmony_ci	if (!asoc && authkey->sca_assoc_id > SCTP_ALL_ASSOC &&
364962306a36Sopenharmony_ci	    sctp_style(sk, UDP))
365062306a36Sopenharmony_ci		goto out;
365162306a36Sopenharmony_ci
365262306a36Sopenharmony_ci	if (asoc) {
365362306a36Sopenharmony_ci		ret = sctp_auth_set_key(ep, asoc, authkey);
365462306a36Sopenharmony_ci		goto out;
365562306a36Sopenharmony_ci	}
365662306a36Sopenharmony_ci
365762306a36Sopenharmony_ci	if (sctp_style(sk, TCP))
365862306a36Sopenharmony_ci		authkey->sca_assoc_id = SCTP_FUTURE_ASSOC;
365962306a36Sopenharmony_ci
366062306a36Sopenharmony_ci	if (authkey->sca_assoc_id == SCTP_FUTURE_ASSOC ||
366162306a36Sopenharmony_ci	    authkey->sca_assoc_id == SCTP_ALL_ASSOC) {
366262306a36Sopenharmony_ci		ret = sctp_auth_set_key(ep, asoc, authkey);
366362306a36Sopenharmony_ci		if (ret)
366462306a36Sopenharmony_ci			goto out;
366562306a36Sopenharmony_ci	}
366662306a36Sopenharmony_ci
366762306a36Sopenharmony_ci	ret = 0;
366862306a36Sopenharmony_ci
366962306a36Sopenharmony_ci	if (authkey->sca_assoc_id == SCTP_CURRENT_ASSOC ||
367062306a36Sopenharmony_ci	    authkey->sca_assoc_id == SCTP_ALL_ASSOC) {
367162306a36Sopenharmony_ci		list_for_each_entry(asoc, &ep->asocs, asocs) {
367262306a36Sopenharmony_ci			int res = sctp_auth_set_key(ep, asoc, authkey);
367362306a36Sopenharmony_ci
367462306a36Sopenharmony_ci			if (res && !ret)
367562306a36Sopenharmony_ci				ret = res;
367662306a36Sopenharmony_ci		}
367762306a36Sopenharmony_ci	}
367862306a36Sopenharmony_ci
367962306a36Sopenharmony_ciout:
368062306a36Sopenharmony_ci	memzero_explicit(authkey, optlen);
368162306a36Sopenharmony_ci	return ret;
368262306a36Sopenharmony_ci}
368362306a36Sopenharmony_ci
368462306a36Sopenharmony_ci/*
368562306a36Sopenharmony_ci * 7.1.21.  Get or set the active shared key (SCTP_AUTH_ACTIVE_KEY)
368662306a36Sopenharmony_ci *
368762306a36Sopenharmony_ci * This option will get or set the active shared key to be used to build
368862306a36Sopenharmony_ci * the association shared key.
368962306a36Sopenharmony_ci */
369062306a36Sopenharmony_cistatic int sctp_setsockopt_active_key(struct sock *sk,
369162306a36Sopenharmony_ci				      struct sctp_authkeyid *val,
369262306a36Sopenharmony_ci				      unsigned int optlen)
369362306a36Sopenharmony_ci{
369462306a36Sopenharmony_ci	struct sctp_endpoint *ep = sctp_sk(sk)->ep;
369562306a36Sopenharmony_ci	struct sctp_association *asoc;
369662306a36Sopenharmony_ci	int ret = 0;
369762306a36Sopenharmony_ci
369862306a36Sopenharmony_ci	if (optlen != sizeof(struct sctp_authkeyid))
369962306a36Sopenharmony_ci		return -EINVAL;
370062306a36Sopenharmony_ci
370162306a36Sopenharmony_ci	asoc = sctp_id2assoc(sk, val->scact_assoc_id);
370262306a36Sopenharmony_ci	if (!asoc && val->scact_assoc_id > SCTP_ALL_ASSOC &&
370362306a36Sopenharmony_ci	    sctp_style(sk, UDP))
370462306a36Sopenharmony_ci		return -EINVAL;
370562306a36Sopenharmony_ci
370662306a36Sopenharmony_ci	if (asoc)
370762306a36Sopenharmony_ci		return sctp_auth_set_active_key(ep, asoc, val->scact_keynumber);
370862306a36Sopenharmony_ci
370962306a36Sopenharmony_ci	if (sctp_style(sk, TCP))
371062306a36Sopenharmony_ci		val->scact_assoc_id = SCTP_FUTURE_ASSOC;
371162306a36Sopenharmony_ci
371262306a36Sopenharmony_ci	if (val->scact_assoc_id == SCTP_FUTURE_ASSOC ||
371362306a36Sopenharmony_ci	    val->scact_assoc_id == SCTP_ALL_ASSOC) {
371462306a36Sopenharmony_ci		ret = sctp_auth_set_active_key(ep, asoc, val->scact_keynumber);
371562306a36Sopenharmony_ci		if (ret)
371662306a36Sopenharmony_ci			return ret;
371762306a36Sopenharmony_ci	}
371862306a36Sopenharmony_ci
371962306a36Sopenharmony_ci	if (val->scact_assoc_id == SCTP_CURRENT_ASSOC ||
372062306a36Sopenharmony_ci	    val->scact_assoc_id == SCTP_ALL_ASSOC) {
372162306a36Sopenharmony_ci		list_for_each_entry(asoc, &ep->asocs, asocs) {
372262306a36Sopenharmony_ci			int res = sctp_auth_set_active_key(ep, asoc,
372362306a36Sopenharmony_ci							   val->scact_keynumber);
372462306a36Sopenharmony_ci
372562306a36Sopenharmony_ci			if (res && !ret)
372662306a36Sopenharmony_ci				ret = res;
372762306a36Sopenharmony_ci		}
372862306a36Sopenharmony_ci	}
372962306a36Sopenharmony_ci
373062306a36Sopenharmony_ci	return ret;
373162306a36Sopenharmony_ci}
373262306a36Sopenharmony_ci
373362306a36Sopenharmony_ci/*
373462306a36Sopenharmony_ci * 7.1.22.  Delete a shared key (SCTP_AUTH_DELETE_KEY)
373562306a36Sopenharmony_ci *
373662306a36Sopenharmony_ci * This set option will delete a shared secret key from use.
373762306a36Sopenharmony_ci */
373862306a36Sopenharmony_cistatic int sctp_setsockopt_del_key(struct sock *sk,
373962306a36Sopenharmony_ci				   struct sctp_authkeyid *val,
374062306a36Sopenharmony_ci				   unsigned int optlen)
374162306a36Sopenharmony_ci{
374262306a36Sopenharmony_ci	struct sctp_endpoint *ep = sctp_sk(sk)->ep;
374362306a36Sopenharmony_ci	struct sctp_association *asoc;
374462306a36Sopenharmony_ci	int ret = 0;
374562306a36Sopenharmony_ci
374662306a36Sopenharmony_ci	if (optlen != sizeof(struct sctp_authkeyid))
374762306a36Sopenharmony_ci		return -EINVAL;
374862306a36Sopenharmony_ci
374962306a36Sopenharmony_ci	asoc = sctp_id2assoc(sk, val->scact_assoc_id);
375062306a36Sopenharmony_ci	if (!asoc && val->scact_assoc_id > SCTP_ALL_ASSOC &&
375162306a36Sopenharmony_ci	    sctp_style(sk, UDP))
375262306a36Sopenharmony_ci		return -EINVAL;
375362306a36Sopenharmony_ci
375462306a36Sopenharmony_ci	if (asoc)
375562306a36Sopenharmony_ci		return sctp_auth_del_key_id(ep, asoc, val->scact_keynumber);
375662306a36Sopenharmony_ci
375762306a36Sopenharmony_ci	if (sctp_style(sk, TCP))
375862306a36Sopenharmony_ci		val->scact_assoc_id = SCTP_FUTURE_ASSOC;
375962306a36Sopenharmony_ci
376062306a36Sopenharmony_ci	if (val->scact_assoc_id == SCTP_FUTURE_ASSOC ||
376162306a36Sopenharmony_ci	    val->scact_assoc_id == SCTP_ALL_ASSOC) {
376262306a36Sopenharmony_ci		ret = sctp_auth_del_key_id(ep, asoc, val->scact_keynumber);
376362306a36Sopenharmony_ci		if (ret)
376462306a36Sopenharmony_ci			return ret;
376562306a36Sopenharmony_ci	}
376662306a36Sopenharmony_ci
376762306a36Sopenharmony_ci	if (val->scact_assoc_id == SCTP_CURRENT_ASSOC ||
376862306a36Sopenharmony_ci	    val->scact_assoc_id == SCTP_ALL_ASSOC) {
376962306a36Sopenharmony_ci		list_for_each_entry(asoc, &ep->asocs, asocs) {
377062306a36Sopenharmony_ci			int res = sctp_auth_del_key_id(ep, asoc,
377162306a36Sopenharmony_ci						       val->scact_keynumber);
377262306a36Sopenharmony_ci
377362306a36Sopenharmony_ci			if (res && !ret)
377462306a36Sopenharmony_ci				ret = res;
377562306a36Sopenharmony_ci		}
377662306a36Sopenharmony_ci	}
377762306a36Sopenharmony_ci
377862306a36Sopenharmony_ci	return ret;
377962306a36Sopenharmony_ci}
378062306a36Sopenharmony_ci
378162306a36Sopenharmony_ci/*
378262306a36Sopenharmony_ci * 8.3.4  Deactivate a Shared Key (SCTP_AUTH_DEACTIVATE_KEY)
378362306a36Sopenharmony_ci *
378462306a36Sopenharmony_ci * This set option will deactivate a shared secret key.
378562306a36Sopenharmony_ci */
378662306a36Sopenharmony_cistatic int sctp_setsockopt_deactivate_key(struct sock *sk,
378762306a36Sopenharmony_ci					  struct sctp_authkeyid *val,
378862306a36Sopenharmony_ci					  unsigned int optlen)
378962306a36Sopenharmony_ci{
379062306a36Sopenharmony_ci	struct sctp_endpoint *ep = sctp_sk(sk)->ep;
379162306a36Sopenharmony_ci	struct sctp_association *asoc;
379262306a36Sopenharmony_ci	int ret = 0;
379362306a36Sopenharmony_ci
379462306a36Sopenharmony_ci	if (optlen != sizeof(struct sctp_authkeyid))
379562306a36Sopenharmony_ci		return -EINVAL;
379662306a36Sopenharmony_ci
379762306a36Sopenharmony_ci	asoc = sctp_id2assoc(sk, val->scact_assoc_id);
379862306a36Sopenharmony_ci	if (!asoc && val->scact_assoc_id > SCTP_ALL_ASSOC &&
379962306a36Sopenharmony_ci	    sctp_style(sk, UDP))
380062306a36Sopenharmony_ci		return -EINVAL;
380162306a36Sopenharmony_ci
380262306a36Sopenharmony_ci	if (asoc)
380362306a36Sopenharmony_ci		return sctp_auth_deact_key_id(ep, asoc, val->scact_keynumber);
380462306a36Sopenharmony_ci
380562306a36Sopenharmony_ci	if (sctp_style(sk, TCP))
380662306a36Sopenharmony_ci		val->scact_assoc_id = SCTP_FUTURE_ASSOC;
380762306a36Sopenharmony_ci
380862306a36Sopenharmony_ci	if (val->scact_assoc_id == SCTP_FUTURE_ASSOC ||
380962306a36Sopenharmony_ci	    val->scact_assoc_id == SCTP_ALL_ASSOC) {
381062306a36Sopenharmony_ci		ret = sctp_auth_deact_key_id(ep, asoc, val->scact_keynumber);
381162306a36Sopenharmony_ci		if (ret)
381262306a36Sopenharmony_ci			return ret;
381362306a36Sopenharmony_ci	}
381462306a36Sopenharmony_ci
381562306a36Sopenharmony_ci	if (val->scact_assoc_id == SCTP_CURRENT_ASSOC ||
381662306a36Sopenharmony_ci	    val->scact_assoc_id == SCTP_ALL_ASSOC) {
381762306a36Sopenharmony_ci		list_for_each_entry(asoc, &ep->asocs, asocs) {
381862306a36Sopenharmony_ci			int res = sctp_auth_deact_key_id(ep, asoc,
381962306a36Sopenharmony_ci							 val->scact_keynumber);
382062306a36Sopenharmony_ci
382162306a36Sopenharmony_ci			if (res && !ret)
382262306a36Sopenharmony_ci				ret = res;
382362306a36Sopenharmony_ci		}
382462306a36Sopenharmony_ci	}
382562306a36Sopenharmony_ci
382662306a36Sopenharmony_ci	return ret;
382762306a36Sopenharmony_ci}
382862306a36Sopenharmony_ci
382962306a36Sopenharmony_ci/*
383062306a36Sopenharmony_ci * 8.1.23 SCTP_AUTO_ASCONF
383162306a36Sopenharmony_ci *
383262306a36Sopenharmony_ci * This option will enable or disable the use of the automatic generation of
383362306a36Sopenharmony_ci * ASCONF chunks to add and delete addresses to an existing association.  Note
383462306a36Sopenharmony_ci * that this option has two caveats namely: a) it only affects sockets that
383562306a36Sopenharmony_ci * are bound to all addresses available to the SCTP stack, and b) the system
383662306a36Sopenharmony_ci * administrator may have an overriding control that turns the ASCONF feature
383762306a36Sopenharmony_ci * off no matter what setting the socket option may have.
383862306a36Sopenharmony_ci * This option expects an integer boolean flag, where a non-zero value turns on
383962306a36Sopenharmony_ci * the option, and a zero value turns off the option.
384062306a36Sopenharmony_ci * Note. In this implementation, socket operation overrides default parameter
384162306a36Sopenharmony_ci * being set by sysctl as well as FreeBSD implementation
384262306a36Sopenharmony_ci */
384362306a36Sopenharmony_cistatic int sctp_setsockopt_auto_asconf(struct sock *sk, int *val,
384462306a36Sopenharmony_ci					unsigned int optlen)
384562306a36Sopenharmony_ci{
384662306a36Sopenharmony_ci	struct sctp_sock *sp = sctp_sk(sk);
384762306a36Sopenharmony_ci
384862306a36Sopenharmony_ci	if (optlen < sizeof(int))
384962306a36Sopenharmony_ci		return -EINVAL;
385062306a36Sopenharmony_ci	if (!sctp_is_ep_boundall(sk) && *val)
385162306a36Sopenharmony_ci		return -EINVAL;
385262306a36Sopenharmony_ci	if ((*val && sp->do_auto_asconf) || (!*val && !sp->do_auto_asconf))
385362306a36Sopenharmony_ci		return 0;
385462306a36Sopenharmony_ci
385562306a36Sopenharmony_ci	spin_lock_bh(&sock_net(sk)->sctp.addr_wq_lock);
385662306a36Sopenharmony_ci	if (*val == 0 && sp->do_auto_asconf) {
385762306a36Sopenharmony_ci		list_del(&sp->auto_asconf_list);
385862306a36Sopenharmony_ci		sp->do_auto_asconf = 0;
385962306a36Sopenharmony_ci	} else if (*val && !sp->do_auto_asconf) {
386062306a36Sopenharmony_ci		list_add_tail(&sp->auto_asconf_list,
386162306a36Sopenharmony_ci		    &sock_net(sk)->sctp.auto_asconf_splist);
386262306a36Sopenharmony_ci		sp->do_auto_asconf = 1;
386362306a36Sopenharmony_ci	}
386462306a36Sopenharmony_ci	spin_unlock_bh(&sock_net(sk)->sctp.addr_wq_lock);
386562306a36Sopenharmony_ci	return 0;
386662306a36Sopenharmony_ci}
386762306a36Sopenharmony_ci
386862306a36Sopenharmony_ci/*
386962306a36Sopenharmony_ci * SCTP_PEER_ADDR_THLDS
387062306a36Sopenharmony_ci *
387162306a36Sopenharmony_ci * This option allows us to alter the partially failed threshold for one or all
387262306a36Sopenharmony_ci * transports in an association.  See Section 6.1 of:
387362306a36Sopenharmony_ci * http://www.ietf.org/id/draft-nishida-tsvwg-sctp-failover-05.txt
387462306a36Sopenharmony_ci */
387562306a36Sopenharmony_cistatic int sctp_setsockopt_paddr_thresholds(struct sock *sk,
387662306a36Sopenharmony_ci					    struct sctp_paddrthlds_v2 *val,
387762306a36Sopenharmony_ci					    unsigned int optlen, bool v2)
387862306a36Sopenharmony_ci{
387962306a36Sopenharmony_ci	struct sctp_transport *trans;
388062306a36Sopenharmony_ci	struct sctp_association *asoc;
388162306a36Sopenharmony_ci	int len;
388262306a36Sopenharmony_ci
388362306a36Sopenharmony_ci	len = v2 ? sizeof(*val) : sizeof(struct sctp_paddrthlds);
388462306a36Sopenharmony_ci	if (optlen < len)
388562306a36Sopenharmony_ci		return -EINVAL;
388662306a36Sopenharmony_ci
388762306a36Sopenharmony_ci	if (v2 && val->spt_pathpfthld > val->spt_pathcpthld)
388862306a36Sopenharmony_ci		return -EINVAL;
388962306a36Sopenharmony_ci
389062306a36Sopenharmony_ci	if (!sctp_is_any(sk, (const union sctp_addr *)&val->spt_address)) {
389162306a36Sopenharmony_ci		trans = sctp_addr_id2transport(sk, &val->spt_address,
389262306a36Sopenharmony_ci					       val->spt_assoc_id);
389362306a36Sopenharmony_ci		if (!trans)
389462306a36Sopenharmony_ci			return -ENOENT;
389562306a36Sopenharmony_ci
389662306a36Sopenharmony_ci		if (val->spt_pathmaxrxt)
389762306a36Sopenharmony_ci			trans->pathmaxrxt = val->spt_pathmaxrxt;
389862306a36Sopenharmony_ci		if (v2)
389962306a36Sopenharmony_ci			trans->ps_retrans = val->spt_pathcpthld;
390062306a36Sopenharmony_ci		trans->pf_retrans = val->spt_pathpfthld;
390162306a36Sopenharmony_ci
390262306a36Sopenharmony_ci		return 0;
390362306a36Sopenharmony_ci	}
390462306a36Sopenharmony_ci
390562306a36Sopenharmony_ci	asoc = sctp_id2assoc(sk, val->spt_assoc_id);
390662306a36Sopenharmony_ci	if (!asoc && val->spt_assoc_id != SCTP_FUTURE_ASSOC &&
390762306a36Sopenharmony_ci	    sctp_style(sk, UDP))
390862306a36Sopenharmony_ci		return -EINVAL;
390962306a36Sopenharmony_ci
391062306a36Sopenharmony_ci	if (asoc) {
391162306a36Sopenharmony_ci		list_for_each_entry(trans, &asoc->peer.transport_addr_list,
391262306a36Sopenharmony_ci				    transports) {
391362306a36Sopenharmony_ci			if (val->spt_pathmaxrxt)
391462306a36Sopenharmony_ci				trans->pathmaxrxt = val->spt_pathmaxrxt;
391562306a36Sopenharmony_ci			if (v2)
391662306a36Sopenharmony_ci				trans->ps_retrans = val->spt_pathcpthld;
391762306a36Sopenharmony_ci			trans->pf_retrans = val->spt_pathpfthld;
391862306a36Sopenharmony_ci		}
391962306a36Sopenharmony_ci
392062306a36Sopenharmony_ci		if (val->spt_pathmaxrxt)
392162306a36Sopenharmony_ci			asoc->pathmaxrxt = val->spt_pathmaxrxt;
392262306a36Sopenharmony_ci		if (v2)
392362306a36Sopenharmony_ci			asoc->ps_retrans = val->spt_pathcpthld;
392462306a36Sopenharmony_ci		asoc->pf_retrans = val->spt_pathpfthld;
392562306a36Sopenharmony_ci	} else {
392662306a36Sopenharmony_ci		struct sctp_sock *sp = sctp_sk(sk);
392762306a36Sopenharmony_ci
392862306a36Sopenharmony_ci		if (val->spt_pathmaxrxt)
392962306a36Sopenharmony_ci			sp->pathmaxrxt = val->spt_pathmaxrxt;
393062306a36Sopenharmony_ci		if (v2)
393162306a36Sopenharmony_ci			sp->ps_retrans = val->spt_pathcpthld;
393262306a36Sopenharmony_ci		sp->pf_retrans = val->spt_pathpfthld;
393362306a36Sopenharmony_ci	}
393462306a36Sopenharmony_ci
393562306a36Sopenharmony_ci	return 0;
393662306a36Sopenharmony_ci}
393762306a36Sopenharmony_ci
393862306a36Sopenharmony_cistatic int sctp_setsockopt_recvrcvinfo(struct sock *sk, int *val,
393962306a36Sopenharmony_ci				       unsigned int optlen)
394062306a36Sopenharmony_ci{
394162306a36Sopenharmony_ci	if (optlen < sizeof(int))
394262306a36Sopenharmony_ci		return -EINVAL;
394362306a36Sopenharmony_ci
394462306a36Sopenharmony_ci	sctp_sk(sk)->recvrcvinfo = (*val == 0) ? 0 : 1;
394562306a36Sopenharmony_ci
394662306a36Sopenharmony_ci	return 0;
394762306a36Sopenharmony_ci}
394862306a36Sopenharmony_ci
394962306a36Sopenharmony_cistatic int sctp_setsockopt_recvnxtinfo(struct sock *sk, int *val,
395062306a36Sopenharmony_ci				       unsigned int optlen)
395162306a36Sopenharmony_ci{
395262306a36Sopenharmony_ci	if (optlen < sizeof(int))
395362306a36Sopenharmony_ci		return -EINVAL;
395462306a36Sopenharmony_ci
395562306a36Sopenharmony_ci	sctp_sk(sk)->recvnxtinfo = (*val == 0) ? 0 : 1;
395662306a36Sopenharmony_ci
395762306a36Sopenharmony_ci	return 0;
395862306a36Sopenharmony_ci}
395962306a36Sopenharmony_ci
396062306a36Sopenharmony_cistatic int sctp_setsockopt_pr_supported(struct sock *sk,
396162306a36Sopenharmony_ci					struct sctp_assoc_value *params,
396262306a36Sopenharmony_ci					unsigned int optlen)
396362306a36Sopenharmony_ci{
396462306a36Sopenharmony_ci	struct sctp_association *asoc;
396562306a36Sopenharmony_ci
396662306a36Sopenharmony_ci	if (optlen != sizeof(*params))
396762306a36Sopenharmony_ci		return -EINVAL;
396862306a36Sopenharmony_ci
396962306a36Sopenharmony_ci	asoc = sctp_id2assoc(sk, params->assoc_id);
397062306a36Sopenharmony_ci	if (!asoc && params->assoc_id != SCTP_FUTURE_ASSOC &&
397162306a36Sopenharmony_ci	    sctp_style(sk, UDP))
397262306a36Sopenharmony_ci		return -EINVAL;
397362306a36Sopenharmony_ci
397462306a36Sopenharmony_ci	sctp_sk(sk)->ep->prsctp_enable = !!params->assoc_value;
397562306a36Sopenharmony_ci
397662306a36Sopenharmony_ci	return 0;
397762306a36Sopenharmony_ci}
397862306a36Sopenharmony_ci
397962306a36Sopenharmony_cistatic int sctp_setsockopt_default_prinfo(struct sock *sk,
398062306a36Sopenharmony_ci					  struct sctp_default_prinfo *info,
398162306a36Sopenharmony_ci					  unsigned int optlen)
398262306a36Sopenharmony_ci{
398362306a36Sopenharmony_ci	struct sctp_sock *sp = sctp_sk(sk);
398462306a36Sopenharmony_ci	struct sctp_association *asoc;
398562306a36Sopenharmony_ci	int retval = -EINVAL;
398662306a36Sopenharmony_ci
398762306a36Sopenharmony_ci	if (optlen != sizeof(*info))
398862306a36Sopenharmony_ci		goto out;
398962306a36Sopenharmony_ci
399062306a36Sopenharmony_ci	if (info->pr_policy & ~SCTP_PR_SCTP_MASK)
399162306a36Sopenharmony_ci		goto out;
399262306a36Sopenharmony_ci
399362306a36Sopenharmony_ci	if (info->pr_policy == SCTP_PR_SCTP_NONE)
399462306a36Sopenharmony_ci		info->pr_value = 0;
399562306a36Sopenharmony_ci
399662306a36Sopenharmony_ci	asoc = sctp_id2assoc(sk, info->pr_assoc_id);
399762306a36Sopenharmony_ci	if (!asoc && info->pr_assoc_id > SCTP_ALL_ASSOC &&
399862306a36Sopenharmony_ci	    sctp_style(sk, UDP))
399962306a36Sopenharmony_ci		goto out;
400062306a36Sopenharmony_ci
400162306a36Sopenharmony_ci	retval = 0;
400262306a36Sopenharmony_ci
400362306a36Sopenharmony_ci	if (asoc) {
400462306a36Sopenharmony_ci		SCTP_PR_SET_POLICY(asoc->default_flags, info->pr_policy);
400562306a36Sopenharmony_ci		asoc->default_timetolive = info->pr_value;
400662306a36Sopenharmony_ci		goto out;
400762306a36Sopenharmony_ci	}
400862306a36Sopenharmony_ci
400962306a36Sopenharmony_ci	if (sctp_style(sk, TCP))
401062306a36Sopenharmony_ci		info->pr_assoc_id = SCTP_FUTURE_ASSOC;
401162306a36Sopenharmony_ci
401262306a36Sopenharmony_ci	if (info->pr_assoc_id == SCTP_FUTURE_ASSOC ||
401362306a36Sopenharmony_ci	    info->pr_assoc_id == SCTP_ALL_ASSOC) {
401462306a36Sopenharmony_ci		SCTP_PR_SET_POLICY(sp->default_flags, info->pr_policy);
401562306a36Sopenharmony_ci		sp->default_timetolive = info->pr_value;
401662306a36Sopenharmony_ci	}
401762306a36Sopenharmony_ci
401862306a36Sopenharmony_ci	if (info->pr_assoc_id == SCTP_CURRENT_ASSOC ||
401962306a36Sopenharmony_ci	    info->pr_assoc_id == SCTP_ALL_ASSOC) {
402062306a36Sopenharmony_ci		list_for_each_entry(asoc, &sp->ep->asocs, asocs) {
402162306a36Sopenharmony_ci			SCTP_PR_SET_POLICY(asoc->default_flags,
402262306a36Sopenharmony_ci					   info->pr_policy);
402362306a36Sopenharmony_ci			asoc->default_timetolive = info->pr_value;
402462306a36Sopenharmony_ci		}
402562306a36Sopenharmony_ci	}
402662306a36Sopenharmony_ci
402762306a36Sopenharmony_ciout:
402862306a36Sopenharmony_ci	return retval;
402962306a36Sopenharmony_ci}
403062306a36Sopenharmony_ci
403162306a36Sopenharmony_cistatic int sctp_setsockopt_reconfig_supported(struct sock *sk,
403262306a36Sopenharmony_ci					      struct sctp_assoc_value *params,
403362306a36Sopenharmony_ci					      unsigned int optlen)
403462306a36Sopenharmony_ci{
403562306a36Sopenharmony_ci	struct sctp_association *asoc;
403662306a36Sopenharmony_ci	int retval = -EINVAL;
403762306a36Sopenharmony_ci
403862306a36Sopenharmony_ci	if (optlen != sizeof(*params))
403962306a36Sopenharmony_ci		goto out;
404062306a36Sopenharmony_ci
404162306a36Sopenharmony_ci	asoc = sctp_id2assoc(sk, params->assoc_id);
404262306a36Sopenharmony_ci	if (!asoc && params->assoc_id != SCTP_FUTURE_ASSOC &&
404362306a36Sopenharmony_ci	    sctp_style(sk, UDP))
404462306a36Sopenharmony_ci		goto out;
404562306a36Sopenharmony_ci
404662306a36Sopenharmony_ci	sctp_sk(sk)->ep->reconf_enable = !!params->assoc_value;
404762306a36Sopenharmony_ci
404862306a36Sopenharmony_ci	retval = 0;
404962306a36Sopenharmony_ci
405062306a36Sopenharmony_ciout:
405162306a36Sopenharmony_ci	return retval;
405262306a36Sopenharmony_ci}
405362306a36Sopenharmony_ci
405462306a36Sopenharmony_cistatic int sctp_setsockopt_enable_strreset(struct sock *sk,
405562306a36Sopenharmony_ci					   struct sctp_assoc_value *params,
405662306a36Sopenharmony_ci					   unsigned int optlen)
405762306a36Sopenharmony_ci{
405862306a36Sopenharmony_ci	struct sctp_endpoint *ep = sctp_sk(sk)->ep;
405962306a36Sopenharmony_ci	struct sctp_association *asoc;
406062306a36Sopenharmony_ci	int retval = -EINVAL;
406162306a36Sopenharmony_ci
406262306a36Sopenharmony_ci	if (optlen != sizeof(*params))
406362306a36Sopenharmony_ci		goto out;
406462306a36Sopenharmony_ci
406562306a36Sopenharmony_ci	if (params->assoc_value & (~SCTP_ENABLE_STRRESET_MASK))
406662306a36Sopenharmony_ci		goto out;
406762306a36Sopenharmony_ci
406862306a36Sopenharmony_ci	asoc = sctp_id2assoc(sk, params->assoc_id);
406962306a36Sopenharmony_ci	if (!asoc && params->assoc_id > SCTP_ALL_ASSOC &&
407062306a36Sopenharmony_ci	    sctp_style(sk, UDP))
407162306a36Sopenharmony_ci		goto out;
407262306a36Sopenharmony_ci
407362306a36Sopenharmony_ci	retval = 0;
407462306a36Sopenharmony_ci
407562306a36Sopenharmony_ci	if (asoc) {
407662306a36Sopenharmony_ci		asoc->strreset_enable = params->assoc_value;
407762306a36Sopenharmony_ci		goto out;
407862306a36Sopenharmony_ci	}
407962306a36Sopenharmony_ci
408062306a36Sopenharmony_ci	if (sctp_style(sk, TCP))
408162306a36Sopenharmony_ci		params->assoc_id = SCTP_FUTURE_ASSOC;
408262306a36Sopenharmony_ci
408362306a36Sopenharmony_ci	if (params->assoc_id == SCTP_FUTURE_ASSOC ||
408462306a36Sopenharmony_ci	    params->assoc_id == SCTP_ALL_ASSOC)
408562306a36Sopenharmony_ci		ep->strreset_enable = params->assoc_value;
408662306a36Sopenharmony_ci
408762306a36Sopenharmony_ci	if (params->assoc_id == SCTP_CURRENT_ASSOC ||
408862306a36Sopenharmony_ci	    params->assoc_id == SCTP_ALL_ASSOC)
408962306a36Sopenharmony_ci		list_for_each_entry(asoc, &ep->asocs, asocs)
409062306a36Sopenharmony_ci			asoc->strreset_enable = params->assoc_value;
409162306a36Sopenharmony_ci
409262306a36Sopenharmony_ciout:
409362306a36Sopenharmony_ci	return retval;
409462306a36Sopenharmony_ci}
409562306a36Sopenharmony_ci
409662306a36Sopenharmony_cistatic int sctp_setsockopt_reset_streams(struct sock *sk,
409762306a36Sopenharmony_ci					 struct sctp_reset_streams *params,
409862306a36Sopenharmony_ci					 unsigned int optlen)
409962306a36Sopenharmony_ci{
410062306a36Sopenharmony_ci	struct sctp_association *asoc;
410162306a36Sopenharmony_ci
410262306a36Sopenharmony_ci	if (optlen < sizeof(*params))
410362306a36Sopenharmony_ci		return -EINVAL;
410462306a36Sopenharmony_ci	/* srs_number_streams is u16, so optlen can't be bigger than this. */
410562306a36Sopenharmony_ci	optlen = min_t(unsigned int, optlen, USHRT_MAX +
410662306a36Sopenharmony_ci					     sizeof(__u16) * sizeof(*params));
410762306a36Sopenharmony_ci
410862306a36Sopenharmony_ci	if (params->srs_number_streams * sizeof(__u16) >
410962306a36Sopenharmony_ci	    optlen - sizeof(*params))
411062306a36Sopenharmony_ci		return -EINVAL;
411162306a36Sopenharmony_ci
411262306a36Sopenharmony_ci	asoc = sctp_id2assoc(sk, params->srs_assoc_id);
411362306a36Sopenharmony_ci	if (!asoc)
411462306a36Sopenharmony_ci		return -EINVAL;
411562306a36Sopenharmony_ci
411662306a36Sopenharmony_ci	return sctp_send_reset_streams(asoc, params);
411762306a36Sopenharmony_ci}
411862306a36Sopenharmony_ci
411962306a36Sopenharmony_cistatic int sctp_setsockopt_reset_assoc(struct sock *sk, sctp_assoc_t *associd,
412062306a36Sopenharmony_ci				       unsigned int optlen)
412162306a36Sopenharmony_ci{
412262306a36Sopenharmony_ci	struct sctp_association *asoc;
412362306a36Sopenharmony_ci
412462306a36Sopenharmony_ci	if (optlen != sizeof(*associd))
412562306a36Sopenharmony_ci		return -EINVAL;
412662306a36Sopenharmony_ci
412762306a36Sopenharmony_ci	asoc = sctp_id2assoc(sk, *associd);
412862306a36Sopenharmony_ci	if (!asoc)
412962306a36Sopenharmony_ci		return -EINVAL;
413062306a36Sopenharmony_ci
413162306a36Sopenharmony_ci	return sctp_send_reset_assoc(asoc);
413262306a36Sopenharmony_ci}
413362306a36Sopenharmony_ci
413462306a36Sopenharmony_cistatic int sctp_setsockopt_add_streams(struct sock *sk,
413562306a36Sopenharmony_ci				       struct sctp_add_streams *params,
413662306a36Sopenharmony_ci				       unsigned int optlen)
413762306a36Sopenharmony_ci{
413862306a36Sopenharmony_ci	struct sctp_association *asoc;
413962306a36Sopenharmony_ci
414062306a36Sopenharmony_ci	if (optlen != sizeof(*params))
414162306a36Sopenharmony_ci		return -EINVAL;
414262306a36Sopenharmony_ci
414362306a36Sopenharmony_ci	asoc = sctp_id2assoc(sk, params->sas_assoc_id);
414462306a36Sopenharmony_ci	if (!asoc)
414562306a36Sopenharmony_ci		return -EINVAL;
414662306a36Sopenharmony_ci
414762306a36Sopenharmony_ci	return sctp_send_add_streams(asoc, params);
414862306a36Sopenharmony_ci}
414962306a36Sopenharmony_ci
415062306a36Sopenharmony_cistatic int sctp_setsockopt_scheduler(struct sock *sk,
415162306a36Sopenharmony_ci				     struct sctp_assoc_value *params,
415262306a36Sopenharmony_ci				     unsigned int optlen)
415362306a36Sopenharmony_ci{
415462306a36Sopenharmony_ci	struct sctp_sock *sp = sctp_sk(sk);
415562306a36Sopenharmony_ci	struct sctp_association *asoc;
415662306a36Sopenharmony_ci	int retval = 0;
415762306a36Sopenharmony_ci
415862306a36Sopenharmony_ci	if (optlen < sizeof(*params))
415962306a36Sopenharmony_ci		return -EINVAL;
416062306a36Sopenharmony_ci
416162306a36Sopenharmony_ci	if (params->assoc_value > SCTP_SS_MAX)
416262306a36Sopenharmony_ci		return -EINVAL;
416362306a36Sopenharmony_ci
416462306a36Sopenharmony_ci	asoc = sctp_id2assoc(sk, params->assoc_id);
416562306a36Sopenharmony_ci	if (!asoc && params->assoc_id > SCTP_ALL_ASSOC &&
416662306a36Sopenharmony_ci	    sctp_style(sk, UDP))
416762306a36Sopenharmony_ci		return -EINVAL;
416862306a36Sopenharmony_ci
416962306a36Sopenharmony_ci	if (asoc)
417062306a36Sopenharmony_ci		return sctp_sched_set_sched(asoc, params->assoc_value);
417162306a36Sopenharmony_ci
417262306a36Sopenharmony_ci	if (sctp_style(sk, TCP))
417362306a36Sopenharmony_ci		params->assoc_id = SCTP_FUTURE_ASSOC;
417462306a36Sopenharmony_ci
417562306a36Sopenharmony_ci	if (params->assoc_id == SCTP_FUTURE_ASSOC ||
417662306a36Sopenharmony_ci	    params->assoc_id == SCTP_ALL_ASSOC)
417762306a36Sopenharmony_ci		sp->default_ss = params->assoc_value;
417862306a36Sopenharmony_ci
417962306a36Sopenharmony_ci	if (params->assoc_id == SCTP_CURRENT_ASSOC ||
418062306a36Sopenharmony_ci	    params->assoc_id == SCTP_ALL_ASSOC) {
418162306a36Sopenharmony_ci		list_for_each_entry(asoc, &sp->ep->asocs, asocs) {
418262306a36Sopenharmony_ci			int ret = sctp_sched_set_sched(asoc,
418362306a36Sopenharmony_ci						       params->assoc_value);
418462306a36Sopenharmony_ci
418562306a36Sopenharmony_ci			if (ret && !retval)
418662306a36Sopenharmony_ci				retval = ret;
418762306a36Sopenharmony_ci		}
418862306a36Sopenharmony_ci	}
418962306a36Sopenharmony_ci
419062306a36Sopenharmony_ci	return retval;
419162306a36Sopenharmony_ci}
419262306a36Sopenharmony_ci
419362306a36Sopenharmony_cistatic int sctp_setsockopt_scheduler_value(struct sock *sk,
419462306a36Sopenharmony_ci					   struct sctp_stream_value *params,
419562306a36Sopenharmony_ci					   unsigned int optlen)
419662306a36Sopenharmony_ci{
419762306a36Sopenharmony_ci	struct sctp_association *asoc;
419862306a36Sopenharmony_ci	int retval = -EINVAL;
419962306a36Sopenharmony_ci
420062306a36Sopenharmony_ci	if (optlen < sizeof(*params))
420162306a36Sopenharmony_ci		goto out;
420262306a36Sopenharmony_ci
420362306a36Sopenharmony_ci	asoc = sctp_id2assoc(sk, params->assoc_id);
420462306a36Sopenharmony_ci	if (!asoc && params->assoc_id != SCTP_CURRENT_ASSOC &&
420562306a36Sopenharmony_ci	    sctp_style(sk, UDP))
420662306a36Sopenharmony_ci		goto out;
420762306a36Sopenharmony_ci
420862306a36Sopenharmony_ci	if (asoc) {
420962306a36Sopenharmony_ci		retval = sctp_sched_set_value(asoc, params->stream_id,
421062306a36Sopenharmony_ci					      params->stream_value, GFP_KERNEL);
421162306a36Sopenharmony_ci		goto out;
421262306a36Sopenharmony_ci	}
421362306a36Sopenharmony_ci
421462306a36Sopenharmony_ci	retval = 0;
421562306a36Sopenharmony_ci
421662306a36Sopenharmony_ci	list_for_each_entry(asoc, &sctp_sk(sk)->ep->asocs, asocs) {
421762306a36Sopenharmony_ci		int ret = sctp_sched_set_value(asoc, params->stream_id,
421862306a36Sopenharmony_ci					       params->stream_value,
421962306a36Sopenharmony_ci					       GFP_KERNEL);
422062306a36Sopenharmony_ci		if (ret && !retval) /* try to return the 1st error. */
422162306a36Sopenharmony_ci			retval = ret;
422262306a36Sopenharmony_ci	}
422362306a36Sopenharmony_ci
422462306a36Sopenharmony_ciout:
422562306a36Sopenharmony_ci	return retval;
422662306a36Sopenharmony_ci}
422762306a36Sopenharmony_ci
422862306a36Sopenharmony_cistatic int sctp_setsockopt_interleaving_supported(struct sock *sk,
422962306a36Sopenharmony_ci						  struct sctp_assoc_value *p,
423062306a36Sopenharmony_ci						  unsigned int optlen)
423162306a36Sopenharmony_ci{
423262306a36Sopenharmony_ci	struct sctp_sock *sp = sctp_sk(sk);
423362306a36Sopenharmony_ci	struct sctp_association *asoc;
423462306a36Sopenharmony_ci
423562306a36Sopenharmony_ci	if (optlen < sizeof(*p))
423662306a36Sopenharmony_ci		return -EINVAL;
423762306a36Sopenharmony_ci
423862306a36Sopenharmony_ci	asoc = sctp_id2assoc(sk, p->assoc_id);
423962306a36Sopenharmony_ci	if (!asoc && p->assoc_id != SCTP_FUTURE_ASSOC && sctp_style(sk, UDP))
424062306a36Sopenharmony_ci		return -EINVAL;
424162306a36Sopenharmony_ci
424262306a36Sopenharmony_ci	if (!sock_net(sk)->sctp.intl_enable || !sp->frag_interleave) {
424362306a36Sopenharmony_ci		return -EPERM;
424462306a36Sopenharmony_ci	}
424562306a36Sopenharmony_ci
424662306a36Sopenharmony_ci	sp->ep->intl_enable = !!p->assoc_value;
424762306a36Sopenharmony_ci	return 0;
424862306a36Sopenharmony_ci}
424962306a36Sopenharmony_ci
425062306a36Sopenharmony_cistatic int sctp_setsockopt_reuse_port(struct sock *sk, int *val,
425162306a36Sopenharmony_ci				      unsigned int optlen)
425262306a36Sopenharmony_ci{
425362306a36Sopenharmony_ci	if (!sctp_style(sk, TCP))
425462306a36Sopenharmony_ci		return -EOPNOTSUPP;
425562306a36Sopenharmony_ci
425662306a36Sopenharmony_ci	if (sctp_sk(sk)->ep->base.bind_addr.port)
425762306a36Sopenharmony_ci		return -EFAULT;
425862306a36Sopenharmony_ci
425962306a36Sopenharmony_ci	if (optlen < sizeof(int))
426062306a36Sopenharmony_ci		return -EINVAL;
426162306a36Sopenharmony_ci
426262306a36Sopenharmony_ci	sctp_sk(sk)->reuse = !!*val;
426362306a36Sopenharmony_ci
426462306a36Sopenharmony_ci	return 0;
426562306a36Sopenharmony_ci}
426662306a36Sopenharmony_ci
426762306a36Sopenharmony_cistatic int sctp_assoc_ulpevent_type_set(struct sctp_event *param,
426862306a36Sopenharmony_ci					struct sctp_association *asoc)
426962306a36Sopenharmony_ci{
427062306a36Sopenharmony_ci	struct sctp_ulpevent *event;
427162306a36Sopenharmony_ci
427262306a36Sopenharmony_ci	sctp_ulpevent_type_set(&asoc->subscribe, param->se_type, param->se_on);
427362306a36Sopenharmony_ci
427462306a36Sopenharmony_ci	if (param->se_type == SCTP_SENDER_DRY_EVENT && param->se_on) {
427562306a36Sopenharmony_ci		if (sctp_outq_is_empty(&asoc->outqueue)) {
427662306a36Sopenharmony_ci			event = sctp_ulpevent_make_sender_dry_event(asoc,
427762306a36Sopenharmony_ci					GFP_USER | __GFP_NOWARN);
427862306a36Sopenharmony_ci			if (!event)
427962306a36Sopenharmony_ci				return -ENOMEM;
428062306a36Sopenharmony_ci
428162306a36Sopenharmony_ci			asoc->stream.si->enqueue_event(&asoc->ulpq, event);
428262306a36Sopenharmony_ci		}
428362306a36Sopenharmony_ci	}
428462306a36Sopenharmony_ci
428562306a36Sopenharmony_ci	return 0;
428662306a36Sopenharmony_ci}
428762306a36Sopenharmony_ci
428862306a36Sopenharmony_cistatic int sctp_setsockopt_event(struct sock *sk, struct sctp_event *param,
428962306a36Sopenharmony_ci				 unsigned int optlen)
429062306a36Sopenharmony_ci{
429162306a36Sopenharmony_ci	struct sctp_sock *sp = sctp_sk(sk);
429262306a36Sopenharmony_ci	struct sctp_association *asoc;
429362306a36Sopenharmony_ci	int retval = 0;
429462306a36Sopenharmony_ci
429562306a36Sopenharmony_ci	if (optlen < sizeof(*param))
429662306a36Sopenharmony_ci		return -EINVAL;
429762306a36Sopenharmony_ci
429862306a36Sopenharmony_ci	if (param->se_type < SCTP_SN_TYPE_BASE ||
429962306a36Sopenharmony_ci	    param->se_type > SCTP_SN_TYPE_MAX)
430062306a36Sopenharmony_ci		return -EINVAL;
430162306a36Sopenharmony_ci
430262306a36Sopenharmony_ci	asoc = sctp_id2assoc(sk, param->se_assoc_id);
430362306a36Sopenharmony_ci	if (!asoc && param->se_assoc_id > SCTP_ALL_ASSOC &&
430462306a36Sopenharmony_ci	    sctp_style(sk, UDP))
430562306a36Sopenharmony_ci		return -EINVAL;
430662306a36Sopenharmony_ci
430762306a36Sopenharmony_ci	if (asoc)
430862306a36Sopenharmony_ci		return sctp_assoc_ulpevent_type_set(param, asoc);
430962306a36Sopenharmony_ci
431062306a36Sopenharmony_ci	if (sctp_style(sk, TCP))
431162306a36Sopenharmony_ci		param->se_assoc_id = SCTP_FUTURE_ASSOC;
431262306a36Sopenharmony_ci
431362306a36Sopenharmony_ci	if (param->se_assoc_id == SCTP_FUTURE_ASSOC ||
431462306a36Sopenharmony_ci	    param->se_assoc_id == SCTP_ALL_ASSOC)
431562306a36Sopenharmony_ci		sctp_ulpevent_type_set(&sp->subscribe,
431662306a36Sopenharmony_ci				       param->se_type, param->se_on);
431762306a36Sopenharmony_ci
431862306a36Sopenharmony_ci	if (param->se_assoc_id == SCTP_CURRENT_ASSOC ||
431962306a36Sopenharmony_ci	    param->se_assoc_id == SCTP_ALL_ASSOC) {
432062306a36Sopenharmony_ci		list_for_each_entry(asoc, &sp->ep->asocs, asocs) {
432162306a36Sopenharmony_ci			int ret = sctp_assoc_ulpevent_type_set(param, asoc);
432262306a36Sopenharmony_ci
432362306a36Sopenharmony_ci			if (ret && !retval)
432462306a36Sopenharmony_ci				retval = ret;
432562306a36Sopenharmony_ci		}
432662306a36Sopenharmony_ci	}
432762306a36Sopenharmony_ci
432862306a36Sopenharmony_ci	return retval;
432962306a36Sopenharmony_ci}
433062306a36Sopenharmony_ci
433162306a36Sopenharmony_cistatic int sctp_setsockopt_asconf_supported(struct sock *sk,
433262306a36Sopenharmony_ci					    struct sctp_assoc_value *params,
433362306a36Sopenharmony_ci					    unsigned int optlen)
433462306a36Sopenharmony_ci{
433562306a36Sopenharmony_ci	struct sctp_association *asoc;
433662306a36Sopenharmony_ci	struct sctp_endpoint *ep;
433762306a36Sopenharmony_ci	int retval = -EINVAL;
433862306a36Sopenharmony_ci
433962306a36Sopenharmony_ci	if (optlen != sizeof(*params))
434062306a36Sopenharmony_ci		goto out;
434162306a36Sopenharmony_ci
434262306a36Sopenharmony_ci	asoc = sctp_id2assoc(sk, params->assoc_id);
434362306a36Sopenharmony_ci	if (!asoc && params->assoc_id != SCTP_FUTURE_ASSOC &&
434462306a36Sopenharmony_ci	    sctp_style(sk, UDP))
434562306a36Sopenharmony_ci		goto out;
434662306a36Sopenharmony_ci
434762306a36Sopenharmony_ci	ep = sctp_sk(sk)->ep;
434862306a36Sopenharmony_ci	ep->asconf_enable = !!params->assoc_value;
434962306a36Sopenharmony_ci
435062306a36Sopenharmony_ci	if (ep->asconf_enable && ep->auth_enable) {
435162306a36Sopenharmony_ci		sctp_auth_ep_add_chunkid(ep, SCTP_CID_ASCONF);
435262306a36Sopenharmony_ci		sctp_auth_ep_add_chunkid(ep, SCTP_CID_ASCONF_ACK);
435362306a36Sopenharmony_ci	}
435462306a36Sopenharmony_ci
435562306a36Sopenharmony_ci	retval = 0;
435662306a36Sopenharmony_ci
435762306a36Sopenharmony_ciout:
435862306a36Sopenharmony_ci	return retval;
435962306a36Sopenharmony_ci}
436062306a36Sopenharmony_ci
436162306a36Sopenharmony_cistatic int sctp_setsockopt_auth_supported(struct sock *sk,
436262306a36Sopenharmony_ci					  struct sctp_assoc_value *params,
436362306a36Sopenharmony_ci					  unsigned int optlen)
436462306a36Sopenharmony_ci{
436562306a36Sopenharmony_ci	struct sctp_association *asoc;
436662306a36Sopenharmony_ci	struct sctp_endpoint *ep;
436762306a36Sopenharmony_ci	int retval = -EINVAL;
436862306a36Sopenharmony_ci
436962306a36Sopenharmony_ci	if (optlen != sizeof(*params))
437062306a36Sopenharmony_ci		goto out;
437162306a36Sopenharmony_ci
437262306a36Sopenharmony_ci	asoc = sctp_id2assoc(sk, params->assoc_id);
437362306a36Sopenharmony_ci	if (!asoc && params->assoc_id != SCTP_FUTURE_ASSOC &&
437462306a36Sopenharmony_ci	    sctp_style(sk, UDP))
437562306a36Sopenharmony_ci		goto out;
437662306a36Sopenharmony_ci
437762306a36Sopenharmony_ci	ep = sctp_sk(sk)->ep;
437862306a36Sopenharmony_ci	if (params->assoc_value) {
437962306a36Sopenharmony_ci		retval = sctp_auth_init(ep, GFP_KERNEL);
438062306a36Sopenharmony_ci		if (retval)
438162306a36Sopenharmony_ci			goto out;
438262306a36Sopenharmony_ci		if (ep->asconf_enable) {
438362306a36Sopenharmony_ci			sctp_auth_ep_add_chunkid(ep, SCTP_CID_ASCONF);
438462306a36Sopenharmony_ci			sctp_auth_ep_add_chunkid(ep, SCTP_CID_ASCONF_ACK);
438562306a36Sopenharmony_ci		}
438662306a36Sopenharmony_ci	}
438762306a36Sopenharmony_ci
438862306a36Sopenharmony_ci	ep->auth_enable = !!params->assoc_value;
438962306a36Sopenharmony_ci	retval = 0;
439062306a36Sopenharmony_ci
439162306a36Sopenharmony_ciout:
439262306a36Sopenharmony_ci	return retval;
439362306a36Sopenharmony_ci}
439462306a36Sopenharmony_ci
439562306a36Sopenharmony_cistatic int sctp_setsockopt_ecn_supported(struct sock *sk,
439662306a36Sopenharmony_ci					 struct sctp_assoc_value *params,
439762306a36Sopenharmony_ci					 unsigned int optlen)
439862306a36Sopenharmony_ci{
439962306a36Sopenharmony_ci	struct sctp_association *asoc;
440062306a36Sopenharmony_ci	int retval = -EINVAL;
440162306a36Sopenharmony_ci
440262306a36Sopenharmony_ci	if (optlen != sizeof(*params))
440362306a36Sopenharmony_ci		goto out;
440462306a36Sopenharmony_ci
440562306a36Sopenharmony_ci	asoc = sctp_id2assoc(sk, params->assoc_id);
440662306a36Sopenharmony_ci	if (!asoc && params->assoc_id != SCTP_FUTURE_ASSOC &&
440762306a36Sopenharmony_ci	    sctp_style(sk, UDP))
440862306a36Sopenharmony_ci		goto out;
440962306a36Sopenharmony_ci
441062306a36Sopenharmony_ci	sctp_sk(sk)->ep->ecn_enable = !!params->assoc_value;
441162306a36Sopenharmony_ci	retval = 0;
441262306a36Sopenharmony_ci
441362306a36Sopenharmony_ciout:
441462306a36Sopenharmony_ci	return retval;
441562306a36Sopenharmony_ci}
441662306a36Sopenharmony_ci
441762306a36Sopenharmony_cistatic int sctp_setsockopt_pf_expose(struct sock *sk,
441862306a36Sopenharmony_ci				     struct sctp_assoc_value *params,
441962306a36Sopenharmony_ci				     unsigned int optlen)
442062306a36Sopenharmony_ci{
442162306a36Sopenharmony_ci	struct sctp_association *asoc;
442262306a36Sopenharmony_ci	int retval = -EINVAL;
442362306a36Sopenharmony_ci
442462306a36Sopenharmony_ci	if (optlen != sizeof(*params))
442562306a36Sopenharmony_ci		goto out;
442662306a36Sopenharmony_ci
442762306a36Sopenharmony_ci	if (params->assoc_value > SCTP_PF_EXPOSE_MAX)
442862306a36Sopenharmony_ci		goto out;
442962306a36Sopenharmony_ci
443062306a36Sopenharmony_ci	asoc = sctp_id2assoc(sk, params->assoc_id);
443162306a36Sopenharmony_ci	if (!asoc && params->assoc_id != SCTP_FUTURE_ASSOC &&
443262306a36Sopenharmony_ci	    sctp_style(sk, UDP))
443362306a36Sopenharmony_ci		goto out;
443462306a36Sopenharmony_ci
443562306a36Sopenharmony_ci	if (asoc)
443662306a36Sopenharmony_ci		asoc->pf_expose = params->assoc_value;
443762306a36Sopenharmony_ci	else
443862306a36Sopenharmony_ci		sctp_sk(sk)->pf_expose = params->assoc_value;
443962306a36Sopenharmony_ci	retval = 0;
444062306a36Sopenharmony_ci
444162306a36Sopenharmony_ciout:
444262306a36Sopenharmony_ci	return retval;
444362306a36Sopenharmony_ci}
444462306a36Sopenharmony_ci
444562306a36Sopenharmony_cistatic int sctp_setsockopt_encap_port(struct sock *sk,
444662306a36Sopenharmony_ci				      struct sctp_udpencaps *encap,
444762306a36Sopenharmony_ci				      unsigned int optlen)
444862306a36Sopenharmony_ci{
444962306a36Sopenharmony_ci	struct sctp_association *asoc;
445062306a36Sopenharmony_ci	struct sctp_transport *t;
445162306a36Sopenharmony_ci	__be16 encap_port;
445262306a36Sopenharmony_ci
445362306a36Sopenharmony_ci	if (optlen != sizeof(*encap))
445462306a36Sopenharmony_ci		return -EINVAL;
445562306a36Sopenharmony_ci
445662306a36Sopenharmony_ci	/* If an address other than INADDR_ANY is specified, and
445762306a36Sopenharmony_ci	 * no transport is found, then the request is invalid.
445862306a36Sopenharmony_ci	 */
445962306a36Sopenharmony_ci	encap_port = (__force __be16)encap->sue_port;
446062306a36Sopenharmony_ci	if (!sctp_is_any(sk, (union sctp_addr *)&encap->sue_address)) {
446162306a36Sopenharmony_ci		t = sctp_addr_id2transport(sk, &encap->sue_address,
446262306a36Sopenharmony_ci					   encap->sue_assoc_id);
446362306a36Sopenharmony_ci		if (!t)
446462306a36Sopenharmony_ci			return -EINVAL;
446562306a36Sopenharmony_ci
446662306a36Sopenharmony_ci		t->encap_port = encap_port;
446762306a36Sopenharmony_ci		return 0;
446862306a36Sopenharmony_ci	}
446962306a36Sopenharmony_ci
447062306a36Sopenharmony_ci	/* Get association, if assoc_id != SCTP_FUTURE_ASSOC and the
447162306a36Sopenharmony_ci	 * socket is a one to many style socket, and an association
447262306a36Sopenharmony_ci	 * was not found, then the id was invalid.
447362306a36Sopenharmony_ci	 */
447462306a36Sopenharmony_ci	asoc = sctp_id2assoc(sk, encap->sue_assoc_id);
447562306a36Sopenharmony_ci	if (!asoc && encap->sue_assoc_id != SCTP_FUTURE_ASSOC &&
447662306a36Sopenharmony_ci	    sctp_style(sk, UDP))
447762306a36Sopenharmony_ci		return -EINVAL;
447862306a36Sopenharmony_ci
447962306a36Sopenharmony_ci	/* If changes are for association, also apply encap_port to
448062306a36Sopenharmony_ci	 * each transport.
448162306a36Sopenharmony_ci	 */
448262306a36Sopenharmony_ci	if (asoc) {
448362306a36Sopenharmony_ci		list_for_each_entry(t, &asoc->peer.transport_addr_list,
448462306a36Sopenharmony_ci				    transports)
448562306a36Sopenharmony_ci			t->encap_port = encap_port;
448662306a36Sopenharmony_ci
448762306a36Sopenharmony_ci		asoc->encap_port = encap_port;
448862306a36Sopenharmony_ci		return 0;
448962306a36Sopenharmony_ci	}
449062306a36Sopenharmony_ci
449162306a36Sopenharmony_ci	sctp_sk(sk)->encap_port = encap_port;
449262306a36Sopenharmony_ci	return 0;
449362306a36Sopenharmony_ci}
449462306a36Sopenharmony_ci
449562306a36Sopenharmony_cistatic int sctp_setsockopt_probe_interval(struct sock *sk,
449662306a36Sopenharmony_ci					  struct sctp_probeinterval *params,
449762306a36Sopenharmony_ci					  unsigned int optlen)
449862306a36Sopenharmony_ci{
449962306a36Sopenharmony_ci	struct sctp_association *asoc;
450062306a36Sopenharmony_ci	struct sctp_transport *t;
450162306a36Sopenharmony_ci	__u32 probe_interval;
450262306a36Sopenharmony_ci
450362306a36Sopenharmony_ci	if (optlen != sizeof(*params))
450462306a36Sopenharmony_ci		return -EINVAL;
450562306a36Sopenharmony_ci
450662306a36Sopenharmony_ci	probe_interval = params->spi_interval;
450762306a36Sopenharmony_ci	if (probe_interval && probe_interval < SCTP_PROBE_TIMER_MIN)
450862306a36Sopenharmony_ci		return -EINVAL;
450962306a36Sopenharmony_ci
451062306a36Sopenharmony_ci	/* If an address other than INADDR_ANY is specified, and
451162306a36Sopenharmony_ci	 * no transport is found, then the request is invalid.
451262306a36Sopenharmony_ci	 */
451362306a36Sopenharmony_ci	if (!sctp_is_any(sk, (union sctp_addr *)&params->spi_address)) {
451462306a36Sopenharmony_ci		t = sctp_addr_id2transport(sk, &params->spi_address,
451562306a36Sopenharmony_ci					   params->spi_assoc_id);
451662306a36Sopenharmony_ci		if (!t)
451762306a36Sopenharmony_ci			return -EINVAL;
451862306a36Sopenharmony_ci
451962306a36Sopenharmony_ci		t->probe_interval = msecs_to_jiffies(probe_interval);
452062306a36Sopenharmony_ci		sctp_transport_pl_reset(t);
452162306a36Sopenharmony_ci		return 0;
452262306a36Sopenharmony_ci	}
452362306a36Sopenharmony_ci
452462306a36Sopenharmony_ci	/* Get association, if assoc_id != SCTP_FUTURE_ASSOC and the
452562306a36Sopenharmony_ci	 * socket is a one to many style socket, and an association
452662306a36Sopenharmony_ci	 * was not found, then the id was invalid.
452762306a36Sopenharmony_ci	 */
452862306a36Sopenharmony_ci	asoc = sctp_id2assoc(sk, params->spi_assoc_id);
452962306a36Sopenharmony_ci	if (!asoc && params->spi_assoc_id != SCTP_FUTURE_ASSOC &&
453062306a36Sopenharmony_ci	    sctp_style(sk, UDP))
453162306a36Sopenharmony_ci		return -EINVAL;
453262306a36Sopenharmony_ci
453362306a36Sopenharmony_ci	/* If changes are for association, also apply probe_interval to
453462306a36Sopenharmony_ci	 * each transport.
453562306a36Sopenharmony_ci	 */
453662306a36Sopenharmony_ci	if (asoc) {
453762306a36Sopenharmony_ci		list_for_each_entry(t, &asoc->peer.transport_addr_list, transports) {
453862306a36Sopenharmony_ci			t->probe_interval = msecs_to_jiffies(probe_interval);
453962306a36Sopenharmony_ci			sctp_transport_pl_reset(t);
454062306a36Sopenharmony_ci		}
454162306a36Sopenharmony_ci
454262306a36Sopenharmony_ci		asoc->probe_interval = msecs_to_jiffies(probe_interval);
454362306a36Sopenharmony_ci		return 0;
454462306a36Sopenharmony_ci	}
454562306a36Sopenharmony_ci
454662306a36Sopenharmony_ci	sctp_sk(sk)->probe_interval = probe_interval;
454762306a36Sopenharmony_ci	return 0;
454862306a36Sopenharmony_ci}
454962306a36Sopenharmony_ci
455062306a36Sopenharmony_ci/* API 6.2 setsockopt(), getsockopt()
455162306a36Sopenharmony_ci *
455262306a36Sopenharmony_ci * Applications use setsockopt() and getsockopt() to set or retrieve
455362306a36Sopenharmony_ci * socket options.  Socket options are used to change the default
455462306a36Sopenharmony_ci * behavior of sockets calls.  They are described in Section 7.
455562306a36Sopenharmony_ci *
455662306a36Sopenharmony_ci * The syntax is:
455762306a36Sopenharmony_ci *
455862306a36Sopenharmony_ci *   ret = getsockopt(int sd, int level, int optname, void __user *optval,
455962306a36Sopenharmony_ci *                    int __user *optlen);
456062306a36Sopenharmony_ci *   ret = setsockopt(int sd, int level, int optname, const void __user *optval,
456162306a36Sopenharmony_ci *                    int optlen);
456262306a36Sopenharmony_ci *
456362306a36Sopenharmony_ci *   sd      - the socket descript.
456462306a36Sopenharmony_ci *   level   - set to IPPROTO_SCTP for all SCTP options.
456562306a36Sopenharmony_ci *   optname - the option name.
456662306a36Sopenharmony_ci *   optval  - the buffer to store the value of the option.
456762306a36Sopenharmony_ci *   optlen  - the size of the buffer.
456862306a36Sopenharmony_ci */
456962306a36Sopenharmony_cistatic int sctp_setsockopt(struct sock *sk, int level, int optname,
457062306a36Sopenharmony_ci			   sockptr_t optval, unsigned int optlen)
457162306a36Sopenharmony_ci{
457262306a36Sopenharmony_ci	void *kopt = NULL;
457362306a36Sopenharmony_ci	int retval = 0;
457462306a36Sopenharmony_ci
457562306a36Sopenharmony_ci	pr_debug("%s: sk:%p, optname:%d\n", __func__, sk, optname);
457662306a36Sopenharmony_ci
457762306a36Sopenharmony_ci	/* I can hardly begin to describe how wrong this is.  This is
457862306a36Sopenharmony_ci	 * so broken as to be worse than useless.  The API draft
457962306a36Sopenharmony_ci	 * REALLY is NOT helpful here...  I am not convinced that the
458062306a36Sopenharmony_ci	 * semantics of setsockopt() with a level OTHER THAN SOL_SCTP
458162306a36Sopenharmony_ci	 * are at all well-founded.
458262306a36Sopenharmony_ci	 */
458362306a36Sopenharmony_ci	if (level != SOL_SCTP) {
458462306a36Sopenharmony_ci		struct sctp_af *af = sctp_sk(sk)->pf->af;
458562306a36Sopenharmony_ci
458662306a36Sopenharmony_ci		return af->setsockopt(sk, level, optname, optval, optlen);
458762306a36Sopenharmony_ci	}
458862306a36Sopenharmony_ci
458962306a36Sopenharmony_ci	if (optlen > 0) {
459062306a36Sopenharmony_ci		/* Trim it to the biggest size sctp sockopt may need if necessary */
459162306a36Sopenharmony_ci		optlen = min_t(unsigned int, optlen,
459262306a36Sopenharmony_ci			       PAGE_ALIGN(USHRT_MAX +
459362306a36Sopenharmony_ci					  sizeof(__u16) * sizeof(struct sctp_reset_streams)));
459462306a36Sopenharmony_ci		kopt = memdup_sockptr(optval, optlen);
459562306a36Sopenharmony_ci		if (IS_ERR(kopt))
459662306a36Sopenharmony_ci			return PTR_ERR(kopt);
459762306a36Sopenharmony_ci	}
459862306a36Sopenharmony_ci
459962306a36Sopenharmony_ci	lock_sock(sk);
460062306a36Sopenharmony_ci
460162306a36Sopenharmony_ci	switch (optname) {
460262306a36Sopenharmony_ci	case SCTP_SOCKOPT_BINDX_ADD:
460362306a36Sopenharmony_ci		/* 'optlen' is the size of the addresses buffer. */
460462306a36Sopenharmony_ci		retval = sctp_setsockopt_bindx(sk, kopt, optlen,
460562306a36Sopenharmony_ci					       SCTP_BINDX_ADD_ADDR);
460662306a36Sopenharmony_ci		break;
460762306a36Sopenharmony_ci
460862306a36Sopenharmony_ci	case SCTP_SOCKOPT_BINDX_REM:
460962306a36Sopenharmony_ci		/* 'optlen' is the size of the addresses buffer. */
461062306a36Sopenharmony_ci		retval = sctp_setsockopt_bindx(sk, kopt, optlen,
461162306a36Sopenharmony_ci					       SCTP_BINDX_REM_ADDR);
461262306a36Sopenharmony_ci		break;
461362306a36Sopenharmony_ci
461462306a36Sopenharmony_ci	case SCTP_SOCKOPT_CONNECTX_OLD:
461562306a36Sopenharmony_ci		/* 'optlen' is the size of the addresses buffer. */
461662306a36Sopenharmony_ci		retval = sctp_setsockopt_connectx_old(sk, kopt, optlen);
461762306a36Sopenharmony_ci		break;
461862306a36Sopenharmony_ci
461962306a36Sopenharmony_ci	case SCTP_SOCKOPT_CONNECTX:
462062306a36Sopenharmony_ci		/* 'optlen' is the size of the addresses buffer. */
462162306a36Sopenharmony_ci		retval = sctp_setsockopt_connectx(sk, kopt, optlen);
462262306a36Sopenharmony_ci		break;
462362306a36Sopenharmony_ci
462462306a36Sopenharmony_ci	case SCTP_DISABLE_FRAGMENTS:
462562306a36Sopenharmony_ci		retval = sctp_setsockopt_disable_fragments(sk, kopt, optlen);
462662306a36Sopenharmony_ci		break;
462762306a36Sopenharmony_ci
462862306a36Sopenharmony_ci	case SCTP_EVENTS:
462962306a36Sopenharmony_ci		retval = sctp_setsockopt_events(sk, kopt, optlen);
463062306a36Sopenharmony_ci		break;
463162306a36Sopenharmony_ci
463262306a36Sopenharmony_ci	case SCTP_AUTOCLOSE:
463362306a36Sopenharmony_ci		retval = sctp_setsockopt_autoclose(sk, kopt, optlen);
463462306a36Sopenharmony_ci		break;
463562306a36Sopenharmony_ci
463662306a36Sopenharmony_ci	case SCTP_PEER_ADDR_PARAMS:
463762306a36Sopenharmony_ci		retval = sctp_setsockopt_peer_addr_params(sk, kopt, optlen);
463862306a36Sopenharmony_ci		break;
463962306a36Sopenharmony_ci
464062306a36Sopenharmony_ci	case SCTP_DELAYED_SACK:
464162306a36Sopenharmony_ci		retval = sctp_setsockopt_delayed_ack(sk, kopt, optlen);
464262306a36Sopenharmony_ci		break;
464362306a36Sopenharmony_ci	case SCTP_PARTIAL_DELIVERY_POINT:
464462306a36Sopenharmony_ci		retval = sctp_setsockopt_partial_delivery_point(sk, kopt, optlen);
464562306a36Sopenharmony_ci		break;
464662306a36Sopenharmony_ci
464762306a36Sopenharmony_ci	case SCTP_INITMSG:
464862306a36Sopenharmony_ci		retval = sctp_setsockopt_initmsg(sk, kopt, optlen);
464962306a36Sopenharmony_ci		break;
465062306a36Sopenharmony_ci	case SCTP_DEFAULT_SEND_PARAM:
465162306a36Sopenharmony_ci		retval = sctp_setsockopt_default_send_param(sk, kopt, optlen);
465262306a36Sopenharmony_ci		break;
465362306a36Sopenharmony_ci	case SCTP_DEFAULT_SNDINFO:
465462306a36Sopenharmony_ci		retval = sctp_setsockopt_default_sndinfo(sk, kopt, optlen);
465562306a36Sopenharmony_ci		break;
465662306a36Sopenharmony_ci	case SCTP_PRIMARY_ADDR:
465762306a36Sopenharmony_ci		retval = sctp_setsockopt_primary_addr(sk, kopt, optlen);
465862306a36Sopenharmony_ci		break;
465962306a36Sopenharmony_ci	case SCTP_SET_PEER_PRIMARY_ADDR:
466062306a36Sopenharmony_ci		retval = sctp_setsockopt_peer_primary_addr(sk, kopt, optlen);
466162306a36Sopenharmony_ci		break;
466262306a36Sopenharmony_ci	case SCTP_NODELAY:
466362306a36Sopenharmony_ci		retval = sctp_setsockopt_nodelay(sk, kopt, optlen);
466462306a36Sopenharmony_ci		break;
466562306a36Sopenharmony_ci	case SCTP_RTOINFO:
466662306a36Sopenharmony_ci		retval = sctp_setsockopt_rtoinfo(sk, kopt, optlen);
466762306a36Sopenharmony_ci		break;
466862306a36Sopenharmony_ci	case SCTP_ASSOCINFO:
466962306a36Sopenharmony_ci		retval = sctp_setsockopt_associnfo(sk, kopt, optlen);
467062306a36Sopenharmony_ci		break;
467162306a36Sopenharmony_ci	case SCTP_I_WANT_MAPPED_V4_ADDR:
467262306a36Sopenharmony_ci		retval = sctp_setsockopt_mappedv4(sk, kopt, optlen);
467362306a36Sopenharmony_ci		break;
467462306a36Sopenharmony_ci	case SCTP_MAXSEG:
467562306a36Sopenharmony_ci		retval = sctp_setsockopt_maxseg(sk, kopt, optlen);
467662306a36Sopenharmony_ci		break;
467762306a36Sopenharmony_ci	case SCTP_ADAPTATION_LAYER:
467862306a36Sopenharmony_ci		retval = sctp_setsockopt_adaptation_layer(sk, kopt, optlen);
467962306a36Sopenharmony_ci		break;
468062306a36Sopenharmony_ci	case SCTP_CONTEXT:
468162306a36Sopenharmony_ci		retval = sctp_setsockopt_context(sk, kopt, optlen);
468262306a36Sopenharmony_ci		break;
468362306a36Sopenharmony_ci	case SCTP_FRAGMENT_INTERLEAVE:
468462306a36Sopenharmony_ci		retval = sctp_setsockopt_fragment_interleave(sk, kopt, optlen);
468562306a36Sopenharmony_ci		break;
468662306a36Sopenharmony_ci	case SCTP_MAX_BURST:
468762306a36Sopenharmony_ci		retval = sctp_setsockopt_maxburst(sk, kopt, optlen);
468862306a36Sopenharmony_ci		break;
468962306a36Sopenharmony_ci	case SCTP_AUTH_CHUNK:
469062306a36Sopenharmony_ci		retval = sctp_setsockopt_auth_chunk(sk, kopt, optlen);
469162306a36Sopenharmony_ci		break;
469262306a36Sopenharmony_ci	case SCTP_HMAC_IDENT:
469362306a36Sopenharmony_ci		retval = sctp_setsockopt_hmac_ident(sk, kopt, optlen);
469462306a36Sopenharmony_ci		break;
469562306a36Sopenharmony_ci	case SCTP_AUTH_KEY:
469662306a36Sopenharmony_ci		retval = sctp_setsockopt_auth_key(sk, kopt, optlen);
469762306a36Sopenharmony_ci		break;
469862306a36Sopenharmony_ci	case SCTP_AUTH_ACTIVE_KEY:
469962306a36Sopenharmony_ci		retval = sctp_setsockopt_active_key(sk, kopt, optlen);
470062306a36Sopenharmony_ci		break;
470162306a36Sopenharmony_ci	case SCTP_AUTH_DELETE_KEY:
470262306a36Sopenharmony_ci		retval = sctp_setsockopt_del_key(sk, kopt, optlen);
470362306a36Sopenharmony_ci		break;
470462306a36Sopenharmony_ci	case SCTP_AUTH_DEACTIVATE_KEY:
470562306a36Sopenharmony_ci		retval = sctp_setsockopt_deactivate_key(sk, kopt, optlen);
470662306a36Sopenharmony_ci		break;
470762306a36Sopenharmony_ci	case SCTP_AUTO_ASCONF:
470862306a36Sopenharmony_ci		retval = sctp_setsockopt_auto_asconf(sk, kopt, optlen);
470962306a36Sopenharmony_ci		break;
471062306a36Sopenharmony_ci	case SCTP_PEER_ADDR_THLDS:
471162306a36Sopenharmony_ci		retval = sctp_setsockopt_paddr_thresholds(sk, kopt, optlen,
471262306a36Sopenharmony_ci							  false);
471362306a36Sopenharmony_ci		break;
471462306a36Sopenharmony_ci	case SCTP_PEER_ADDR_THLDS_V2:
471562306a36Sopenharmony_ci		retval = sctp_setsockopt_paddr_thresholds(sk, kopt, optlen,
471662306a36Sopenharmony_ci							  true);
471762306a36Sopenharmony_ci		break;
471862306a36Sopenharmony_ci	case SCTP_RECVRCVINFO:
471962306a36Sopenharmony_ci		retval = sctp_setsockopt_recvrcvinfo(sk, kopt, optlen);
472062306a36Sopenharmony_ci		break;
472162306a36Sopenharmony_ci	case SCTP_RECVNXTINFO:
472262306a36Sopenharmony_ci		retval = sctp_setsockopt_recvnxtinfo(sk, kopt, optlen);
472362306a36Sopenharmony_ci		break;
472462306a36Sopenharmony_ci	case SCTP_PR_SUPPORTED:
472562306a36Sopenharmony_ci		retval = sctp_setsockopt_pr_supported(sk, kopt, optlen);
472662306a36Sopenharmony_ci		break;
472762306a36Sopenharmony_ci	case SCTP_DEFAULT_PRINFO:
472862306a36Sopenharmony_ci		retval = sctp_setsockopt_default_prinfo(sk, kopt, optlen);
472962306a36Sopenharmony_ci		break;
473062306a36Sopenharmony_ci	case SCTP_RECONFIG_SUPPORTED:
473162306a36Sopenharmony_ci		retval = sctp_setsockopt_reconfig_supported(sk, kopt, optlen);
473262306a36Sopenharmony_ci		break;
473362306a36Sopenharmony_ci	case SCTP_ENABLE_STREAM_RESET:
473462306a36Sopenharmony_ci		retval = sctp_setsockopt_enable_strreset(sk, kopt, optlen);
473562306a36Sopenharmony_ci		break;
473662306a36Sopenharmony_ci	case SCTP_RESET_STREAMS:
473762306a36Sopenharmony_ci		retval = sctp_setsockopt_reset_streams(sk, kopt, optlen);
473862306a36Sopenharmony_ci		break;
473962306a36Sopenharmony_ci	case SCTP_RESET_ASSOC:
474062306a36Sopenharmony_ci		retval = sctp_setsockopt_reset_assoc(sk, kopt, optlen);
474162306a36Sopenharmony_ci		break;
474262306a36Sopenharmony_ci	case SCTP_ADD_STREAMS:
474362306a36Sopenharmony_ci		retval = sctp_setsockopt_add_streams(sk, kopt, optlen);
474462306a36Sopenharmony_ci		break;
474562306a36Sopenharmony_ci	case SCTP_STREAM_SCHEDULER:
474662306a36Sopenharmony_ci		retval = sctp_setsockopt_scheduler(sk, kopt, optlen);
474762306a36Sopenharmony_ci		break;
474862306a36Sopenharmony_ci	case SCTP_STREAM_SCHEDULER_VALUE:
474962306a36Sopenharmony_ci		retval = sctp_setsockopt_scheduler_value(sk, kopt, optlen);
475062306a36Sopenharmony_ci		break;
475162306a36Sopenharmony_ci	case SCTP_INTERLEAVING_SUPPORTED:
475262306a36Sopenharmony_ci		retval = sctp_setsockopt_interleaving_supported(sk, kopt,
475362306a36Sopenharmony_ci								optlen);
475462306a36Sopenharmony_ci		break;
475562306a36Sopenharmony_ci	case SCTP_REUSE_PORT:
475662306a36Sopenharmony_ci		retval = sctp_setsockopt_reuse_port(sk, kopt, optlen);
475762306a36Sopenharmony_ci		break;
475862306a36Sopenharmony_ci	case SCTP_EVENT:
475962306a36Sopenharmony_ci		retval = sctp_setsockopt_event(sk, kopt, optlen);
476062306a36Sopenharmony_ci		break;
476162306a36Sopenharmony_ci	case SCTP_ASCONF_SUPPORTED:
476262306a36Sopenharmony_ci		retval = sctp_setsockopt_asconf_supported(sk, kopt, optlen);
476362306a36Sopenharmony_ci		break;
476462306a36Sopenharmony_ci	case SCTP_AUTH_SUPPORTED:
476562306a36Sopenharmony_ci		retval = sctp_setsockopt_auth_supported(sk, kopt, optlen);
476662306a36Sopenharmony_ci		break;
476762306a36Sopenharmony_ci	case SCTP_ECN_SUPPORTED:
476862306a36Sopenharmony_ci		retval = sctp_setsockopt_ecn_supported(sk, kopt, optlen);
476962306a36Sopenharmony_ci		break;
477062306a36Sopenharmony_ci	case SCTP_EXPOSE_POTENTIALLY_FAILED_STATE:
477162306a36Sopenharmony_ci		retval = sctp_setsockopt_pf_expose(sk, kopt, optlen);
477262306a36Sopenharmony_ci		break;
477362306a36Sopenharmony_ci	case SCTP_REMOTE_UDP_ENCAPS_PORT:
477462306a36Sopenharmony_ci		retval = sctp_setsockopt_encap_port(sk, kopt, optlen);
477562306a36Sopenharmony_ci		break;
477662306a36Sopenharmony_ci	case SCTP_PLPMTUD_PROBE_INTERVAL:
477762306a36Sopenharmony_ci		retval = sctp_setsockopt_probe_interval(sk, kopt, optlen);
477862306a36Sopenharmony_ci		break;
477962306a36Sopenharmony_ci	default:
478062306a36Sopenharmony_ci		retval = -ENOPROTOOPT;
478162306a36Sopenharmony_ci		break;
478262306a36Sopenharmony_ci	}
478362306a36Sopenharmony_ci
478462306a36Sopenharmony_ci	release_sock(sk);
478562306a36Sopenharmony_ci	kfree(kopt);
478662306a36Sopenharmony_ci	return retval;
478762306a36Sopenharmony_ci}
478862306a36Sopenharmony_ci
478962306a36Sopenharmony_ci/* API 3.1.6 connect() - UDP Style Syntax
479062306a36Sopenharmony_ci *
479162306a36Sopenharmony_ci * An application may use the connect() call in the UDP model to initiate an
479262306a36Sopenharmony_ci * association without sending data.
479362306a36Sopenharmony_ci *
479462306a36Sopenharmony_ci * The syntax is:
479562306a36Sopenharmony_ci *
479662306a36Sopenharmony_ci * ret = connect(int sd, const struct sockaddr *nam, socklen_t len);
479762306a36Sopenharmony_ci *
479862306a36Sopenharmony_ci * sd: the socket descriptor to have a new association added to.
479962306a36Sopenharmony_ci *
480062306a36Sopenharmony_ci * nam: the address structure (either struct sockaddr_in or struct
480162306a36Sopenharmony_ci *    sockaddr_in6 defined in RFC2553 [7]).
480262306a36Sopenharmony_ci *
480362306a36Sopenharmony_ci * len: the size of the address.
480462306a36Sopenharmony_ci */
480562306a36Sopenharmony_cistatic int sctp_connect(struct sock *sk, struct sockaddr *addr,
480662306a36Sopenharmony_ci			int addr_len, int flags)
480762306a36Sopenharmony_ci{
480862306a36Sopenharmony_ci	struct sctp_af *af;
480962306a36Sopenharmony_ci	int err = -EINVAL;
481062306a36Sopenharmony_ci
481162306a36Sopenharmony_ci	lock_sock(sk);
481262306a36Sopenharmony_ci	pr_debug("%s: sk:%p, sockaddr:%p, addr_len:%d\n", __func__, sk,
481362306a36Sopenharmony_ci		 addr, addr_len);
481462306a36Sopenharmony_ci
481562306a36Sopenharmony_ci	/* Validate addr_len before calling common connect/connectx routine. */
481662306a36Sopenharmony_ci	af = sctp_get_af_specific(addr->sa_family);
481762306a36Sopenharmony_ci	if (af && addr_len >= af->sockaddr_len)
481862306a36Sopenharmony_ci		err = __sctp_connect(sk, addr, af->sockaddr_len, flags, NULL);
481962306a36Sopenharmony_ci
482062306a36Sopenharmony_ci	release_sock(sk);
482162306a36Sopenharmony_ci	return err;
482262306a36Sopenharmony_ci}
482362306a36Sopenharmony_ci
482462306a36Sopenharmony_ciint sctp_inet_connect(struct socket *sock, struct sockaddr *uaddr,
482562306a36Sopenharmony_ci		      int addr_len, int flags)
482662306a36Sopenharmony_ci{
482762306a36Sopenharmony_ci	if (addr_len < sizeof(uaddr->sa_family))
482862306a36Sopenharmony_ci		return -EINVAL;
482962306a36Sopenharmony_ci
483062306a36Sopenharmony_ci	if (uaddr->sa_family == AF_UNSPEC)
483162306a36Sopenharmony_ci		return -EOPNOTSUPP;
483262306a36Sopenharmony_ci
483362306a36Sopenharmony_ci	return sctp_connect(sock->sk, uaddr, addr_len, flags);
483462306a36Sopenharmony_ci}
483562306a36Sopenharmony_ci
483662306a36Sopenharmony_ci/* FIXME: Write comments. */
483762306a36Sopenharmony_cistatic int sctp_disconnect(struct sock *sk, int flags)
483862306a36Sopenharmony_ci{
483962306a36Sopenharmony_ci	return -EOPNOTSUPP; /* STUB */
484062306a36Sopenharmony_ci}
484162306a36Sopenharmony_ci
484262306a36Sopenharmony_ci/* 4.1.4 accept() - TCP Style Syntax
484362306a36Sopenharmony_ci *
484462306a36Sopenharmony_ci * Applications use accept() call to remove an established SCTP
484562306a36Sopenharmony_ci * association from the accept queue of the endpoint.  A new socket
484662306a36Sopenharmony_ci * descriptor will be returned from accept() to represent the newly
484762306a36Sopenharmony_ci * formed association.
484862306a36Sopenharmony_ci */
484962306a36Sopenharmony_cistatic struct sock *sctp_accept(struct sock *sk, int flags, int *err, bool kern)
485062306a36Sopenharmony_ci{
485162306a36Sopenharmony_ci	struct sctp_sock *sp;
485262306a36Sopenharmony_ci	struct sctp_endpoint *ep;
485362306a36Sopenharmony_ci	struct sock *newsk = NULL;
485462306a36Sopenharmony_ci	struct sctp_association *asoc;
485562306a36Sopenharmony_ci	long timeo;
485662306a36Sopenharmony_ci	int error = 0;
485762306a36Sopenharmony_ci
485862306a36Sopenharmony_ci	lock_sock(sk);
485962306a36Sopenharmony_ci
486062306a36Sopenharmony_ci	sp = sctp_sk(sk);
486162306a36Sopenharmony_ci	ep = sp->ep;
486262306a36Sopenharmony_ci
486362306a36Sopenharmony_ci	if (!sctp_style(sk, TCP)) {
486462306a36Sopenharmony_ci		error = -EOPNOTSUPP;
486562306a36Sopenharmony_ci		goto out;
486662306a36Sopenharmony_ci	}
486762306a36Sopenharmony_ci
486862306a36Sopenharmony_ci	if (!sctp_sstate(sk, LISTENING)) {
486962306a36Sopenharmony_ci		error = -EINVAL;
487062306a36Sopenharmony_ci		goto out;
487162306a36Sopenharmony_ci	}
487262306a36Sopenharmony_ci
487362306a36Sopenharmony_ci	timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK);
487462306a36Sopenharmony_ci
487562306a36Sopenharmony_ci	error = sctp_wait_for_accept(sk, timeo);
487662306a36Sopenharmony_ci	if (error)
487762306a36Sopenharmony_ci		goto out;
487862306a36Sopenharmony_ci
487962306a36Sopenharmony_ci	/* We treat the list of associations on the endpoint as the accept
488062306a36Sopenharmony_ci	 * queue and pick the first association on the list.
488162306a36Sopenharmony_ci	 */
488262306a36Sopenharmony_ci	asoc = list_entry(ep->asocs.next, struct sctp_association, asocs);
488362306a36Sopenharmony_ci
488462306a36Sopenharmony_ci	newsk = sp->pf->create_accept_sk(sk, asoc, kern);
488562306a36Sopenharmony_ci	if (!newsk) {
488662306a36Sopenharmony_ci		error = -ENOMEM;
488762306a36Sopenharmony_ci		goto out;
488862306a36Sopenharmony_ci	}
488962306a36Sopenharmony_ci
489062306a36Sopenharmony_ci	/* Populate the fields of the newsk from the oldsk and migrate the
489162306a36Sopenharmony_ci	 * asoc to the newsk.
489262306a36Sopenharmony_ci	 */
489362306a36Sopenharmony_ci	error = sctp_sock_migrate(sk, newsk, asoc, SCTP_SOCKET_TCP);
489462306a36Sopenharmony_ci	if (error) {
489562306a36Sopenharmony_ci		sk_common_release(newsk);
489662306a36Sopenharmony_ci		newsk = NULL;
489762306a36Sopenharmony_ci	}
489862306a36Sopenharmony_ci
489962306a36Sopenharmony_ciout:
490062306a36Sopenharmony_ci	release_sock(sk);
490162306a36Sopenharmony_ci	*err = error;
490262306a36Sopenharmony_ci	return newsk;
490362306a36Sopenharmony_ci}
490462306a36Sopenharmony_ci
490562306a36Sopenharmony_ci/* The SCTP ioctl handler. */
490662306a36Sopenharmony_cistatic int sctp_ioctl(struct sock *sk, int cmd, int *karg)
490762306a36Sopenharmony_ci{
490862306a36Sopenharmony_ci	int rc = -ENOTCONN;
490962306a36Sopenharmony_ci
491062306a36Sopenharmony_ci	lock_sock(sk);
491162306a36Sopenharmony_ci
491262306a36Sopenharmony_ci	/*
491362306a36Sopenharmony_ci	 * SEQPACKET-style sockets in LISTENING state are valid, for
491462306a36Sopenharmony_ci	 * SCTP, so only discard TCP-style sockets in LISTENING state.
491562306a36Sopenharmony_ci	 */
491662306a36Sopenharmony_ci	if (sctp_style(sk, TCP) && sctp_sstate(sk, LISTENING))
491762306a36Sopenharmony_ci		goto out;
491862306a36Sopenharmony_ci
491962306a36Sopenharmony_ci	switch (cmd) {
492062306a36Sopenharmony_ci	case SIOCINQ: {
492162306a36Sopenharmony_ci		struct sk_buff *skb;
492262306a36Sopenharmony_ci		*karg = 0;
492362306a36Sopenharmony_ci
492462306a36Sopenharmony_ci		skb = skb_peek(&sk->sk_receive_queue);
492562306a36Sopenharmony_ci		if (skb != NULL) {
492662306a36Sopenharmony_ci			/*
492762306a36Sopenharmony_ci			 * We will only return the amount of this packet since
492862306a36Sopenharmony_ci			 * that is all that will be read.
492962306a36Sopenharmony_ci			 */
493062306a36Sopenharmony_ci			*karg = skb->len;
493162306a36Sopenharmony_ci		}
493262306a36Sopenharmony_ci		rc = 0;
493362306a36Sopenharmony_ci		break;
493462306a36Sopenharmony_ci	}
493562306a36Sopenharmony_ci	default:
493662306a36Sopenharmony_ci		rc = -ENOIOCTLCMD;
493762306a36Sopenharmony_ci		break;
493862306a36Sopenharmony_ci	}
493962306a36Sopenharmony_ciout:
494062306a36Sopenharmony_ci	release_sock(sk);
494162306a36Sopenharmony_ci	return rc;
494262306a36Sopenharmony_ci}
494362306a36Sopenharmony_ci
494462306a36Sopenharmony_ci/* This is the function which gets called during socket creation to
494562306a36Sopenharmony_ci * initialized the SCTP-specific portion of the sock.
494662306a36Sopenharmony_ci * The sock structure should already be zero-filled memory.
494762306a36Sopenharmony_ci */
494862306a36Sopenharmony_cistatic int sctp_init_sock(struct sock *sk)
494962306a36Sopenharmony_ci{
495062306a36Sopenharmony_ci	struct net *net = sock_net(sk);
495162306a36Sopenharmony_ci	struct sctp_sock *sp;
495262306a36Sopenharmony_ci
495362306a36Sopenharmony_ci	pr_debug("%s: sk:%p\n", __func__, sk);
495462306a36Sopenharmony_ci
495562306a36Sopenharmony_ci	sp = sctp_sk(sk);
495662306a36Sopenharmony_ci
495762306a36Sopenharmony_ci	/* Initialize the SCTP per socket area.  */
495862306a36Sopenharmony_ci	switch (sk->sk_type) {
495962306a36Sopenharmony_ci	case SOCK_SEQPACKET:
496062306a36Sopenharmony_ci		sp->type = SCTP_SOCKET_UDP;
496162306a36Sopenharmony_ci		break;
496262306a36Sopenharmony_ci	case SOCK_STREAM:
496362306a36Sopenharmony_ci		sp->type = SCTP_SOCKET_TCP;
496462306a36Sopenharmony_ci		break;
496562306a36Sopenharmony_ci	default:
496662306a36Sopenharmony_ci		return -ESOCKTNOSUPPORT;
496762306a36Sopenharmony_ci	}
496862306a36Sopenharmony_ci
496962306a36Sopenharmony_ci	sk->sk_gso_type = SKB_GSO_SCTP;
497062306a36Sopenharmony_ci
497162306a36Sopenharmony_ci	/* Initialize default send parameters. These parameters can be
497262306a36Sopenharmony_ci	 * modified with the SCTP_DEFAULT_SEND_PARAM socket option.
497362306a36Sopenharmony_ci	 */
497462306a36Sopenharmony_ci	sp->default_stream = 0;
497562306a36Sopenharmony_ci	sp->default_ppid = 0;
497662306a36Sopenharmony_ci	sp->default_flags = 0;
497762306a36Sopenharmony_ci	sp->default_context = 0;
497862306a36Sopenharmony_ci	sp->default_timetolive = 0;
497962306a36Sopenharmony_ci
498062306a36Sopenharmony_ci	sp->default_rcv_context = 0;
498162306a36Sopenharmony_ci	sp->max_burst = net->sctp.max_burst;
498262306a36Sopenharmony_ci
498362306a36Sopenharmony_ci	sp->sctp_hmac_alg = net->sctp.sctp_hmac_alg;
498462306a36Sopenharmony_ci
498562306a36Sopenharmony_ci	/* Initialize default setup parameters. These parameters
498662306a36Sopenharmony_ci	 * can be modified with the SCTP_INITMSG socket option or
498762306a36Sopenharmony_ci	 * overridden by the SCTP_INIT CMSG.
498862306a36Sopenharmony_ci	 */
498962306a36Sopenharmony_ci	sp->initmsg.sinit_num_ostreams   = sctp_max_outstreams;
499062306a36Sopenharmony_ci	sp->initmsg.sinit_max_instreams  = sctp_max_instreams;
499162306a36Sopenharmony_ci	sp->initmsg.sinit_max_attempts   = net->sctp.max_retrans_init;
499262306a36Sopenharmony_ci	sp->initmsg.sinit_max_init_timeo = net->sctp.rto_max;
499362306a36Sopenharmony_ci
499462306a36Sopenharmony_ci	/* Initialize default RTO related parameters.  These parameters can
499562306a36Sopenharmony_ci	 * be modified for with the SCTP_RTOINFO socket option.
499662306a36Sopenharmony_ci	 */
499762306a36Sopenharmony_ci	sp->rtoinfo.srto_initial = net->sctp.rto_initial;
499862306a36Sopenharmony_ci	sp->rtoinfo.srto_max     = net->sctp.rto_max;
499962306a36Sopenharmony_ci	sp->rtoinfo.srto_min     = net->sctp.rto_min;
500062306a36Sopenharmony_ci
500162306a36Sopenharmony_ci	/* Initialize default association related parameters. These parameters
500262306a36Sopenharmony_ci	 * can be modified with the SCTP_ASSOCINFO socket option.
500362306a36Sopenharmony_ci	 */
500462306a36Sopenharmony_ci	sp->assocparams.sasoc_asocmaxrxt = net->sctp.max_retrans_association;
500562306a36Sopenharmony_ci	sp->assocparams.sasoc_number_peer_destinations = 0;
500662306a36Sopenharmony_ci	sp->assocparams.sasoc_peer_rwnd = 0;
500762306a36Sopenharmony_ci	sp->assocparams.sasoc_local_rwnd = 0;
500862306a36Sopenharmony_ci	sp->assocparams.sasoc_cookie_life = net->sctp.valid_cookie_life;
500962306a36Sopenharmony_ci
501062306a36Sopenharmony_ci	/* Initialize default event subscriptions. By default, all the
501162306a36Sopenharmony_ci	 * options are off.
501262306a36Sopenharmony_ci	 */
501362306a36Sopenharmony_ci	sp->subscribe = 0;
501462306a36Sopenharmony_ci
501562306a36Sopenharmony_ci	/* Default Peer Address Parameters.  These defaults can
501662306a36Sopenharmony_ci	 * be modified via SCTP_PEER_ADDR_PARAMS
501762306a36Sopenharmony_ci	 */
501862306a36Sopenharmony_ci	sp->hbinterval  = net->sctp.hb_interval;
501962306a36Sopenharmony_ci	sp->udp_port    = htons(net->sctp.udp_port);
502062306a36Sopenharmony_ci	sp->encap_port  = htons(net->sctp.encap_port);
502162306a36Sopenharmony_ci	sp->pathmaxrxt  = net->sctp.max_retrans_path;
502262306a36Sopenharmony_ci	sp->pf_retrans  = net->sctp.pf_retrans;
502362306a36Sopenharmony_ci	sp->ps_retrans  = net->sctp.ps_retrans;
502462306a36Sopenharmony_ci	sp->pf_expose   = net->sctp.pf_expose;
502562306a36Sopenharmony_ci	sp->pathmtu     = 0; /* allow default discovery */
502662306a36Sopenharmony_ci	sp->sackdelay   = net->sctp.sack_timeout;
502762306a36Sopenharmony_ci	sp->sackfreq	= 2;
502862306a36Sopenharmony_ci	sp->param_flags = SPP_HB_ENABLE |
502962306a36Sopenharmony_ci			  SPP_PMTUD_ENABLE |
503062306a36Sopenharmony_ci			  SPP_SACKDELAY_ENABLE;
503162306a36Sopenharmony_ci	sp->default_ss = SCTP_SS_DEFAULT;
503262306a36Sopenharmony_ci
503362306a36Sopenharmony_ci	/* If enabled no SCTP message fragmentation will be performed.
503462306a36Sopenharmony_ci	 * Configure through SCTP_DISABLE_FRAGMENTS socket option.
503562306a36Sopenharmony_ci	 */
503662306a36Sopenharmony_ci	sp->disable_fragments = 0;
503762306a36Sopenharmony_ci
503862306a36Sopenharmony_ci	/* Enable Nagle algorithm by default.  */
503962306a36Sopenharmony_ci	sp->nodelay           = 0;
504062306a36Sopenharmony_ci
504162306a36Sopenharmony_ci	sp->recvrcvinfo = 0;
504262306a36Sopenharmony_ci	sp->recvnxtinfo = 0;
504362306a36Sopenharmony_ci
504462306a36Sopenharmony_ci	/* Enable by default. */
504562306a36Sopenharmony_ci	sp->v4mapped          = 1;
504662306a36Sopenharmony_ci
504762306a36Sopenharmony_ci	/* Auto-close idle associations after the configured
504862306a36Sopenharmony_ci	 * number of seconds.  A value of 0 disables this
504962306a36Sopenharmony_ci	 * feature.  Configure through the SCTP_AUTOCLOSE socket option,
505062306a36Sopenharmony_ci	 * for UDP-style sockets only.
505162306a36Sopenharmony_ci	 */
505262306a36Sopenharmony_ci	sp->autoclose         = 0;
505362306a36Sopenharmony_ci
505462306a36Sopenharmony_ci	/* User specified fragmentation limit. */
505562306a36Sopenharmony_ci	sp->user_frag         = 0;
505662306a36Sopenharmony_ci
505762306a36Sopenharmony_ci	sp->adaptation_ind = 0;
505862306a36Sopenharmony_ci
505962306a36Sopenharmony_ci	sp->pf = sctp_get_pf_specific(sk->sk_family);
506062306a36Sopenharmony_ci
506162306a36Sopenharmony_ci	/* Control variables for partial data delivery. */
506262306a36Sopenharmony_ci	atomic_set(&sp->pd_mode, 0);
506362306a36Sopenharmony_ci	skb_queue_head_init(&sp->pd_lobby);
506462306a36Sopenharmony_ci	sp->frag_interleave = 0;
506562306a36Sopenharmony_ci	sp->probe_interval = net->sctp.probe_interval;
506662306a36Sopenharmony_ci
506762306a36Sopenharmony_ci	/* Create a per socket endpoint structure.  Even if we
506862306a36Sopenharmony_ci	 * change the data structure relationships, this may still
506962306a36Sopenharmony_ci	 * be useful for storing pre-connect address information.
507062306a36Sopenharmony_ci	 */
507162306a36Sopenharmony_ci	sp->ep = sctp_endpoint_new(sk, GFP_KERNEL);
507262306a36Sopenharmony_ci	if (!sp->ep)
507362306a36Sopenharmony_ci		return -ENOMEM;
507462306a36Sopenharmony_ci
507562306a36Sopenharmony_ci	sp->hmac = NULL;
507662306a36Sopenharmony_ci
507762306a36Sopenharmony_ci	sk->sk_destruct = sctp_destruct_sock;
507862306a36Sopenharmony_ci
507962306a36Sopenharmony_ci	SCTP_DBG_OBJCNT_INC(sock);
508062306a36Sopenharmony_ci
508162306a36Sopenharmony_ci	sk_sockets_allocated_inc(sk);
508262306a36Sopenharmony_ci	sock_prot_inuse_add(net, sk->sk_prot, 1);
508362306a36Sopenharmony_ci
508462306a36Sopenharmony_ci	return 0;
508562306a36Sopenharmony_ci}
508662306a36Sopenharmony_ci
508762306a36Sopenharmony_ci/* Cleanup any SCTP per socket resources. Must be called with
508862306a36Sopenharmony_ci * sock_net(sk)->sctp.addr_wq_lock held if sp->do_auto_asconf is true
508962306a36Sopenharmony_ci */
509062306a36Sopenharmony_cistatic void sctp_destroy_sock(struct sock *sk)
509162306a36Sopenharmony_ci{
509262306a36Sopenharmony_ci	struct sctp_sock *sp;
509362306a36Sopenharmony_ci
509462306a36Sopenharmony_ci	pr_debug("%s: sk:%p\n", __func__, sk);
509562306a36Sopenharmony_ci
509662306a36Sopenharmony_ci	/* Release our hold on the endpoint. */
509762306a36Sopenharmony_ci	sp = sctp_sk(sk);
509862306a36Sopenharmony_ci	/* This could happen during socket init, thus we bail out
509962306a36Sopenharmony_ci	 * early, since the rest of the below is not setup either.
510062306a36Sopenharmony_ci	 */
510162306a36Sopenharmony_ci	if (sp->ep == NULL)
510262306a36Sopenharmony_ci		return;
510362306a36Sopenharmony_ci
510462306a36Sopenharmony_ci	if (sp->do_auto_asconf) {
510562306a36Sopenharmony_ci		sp->do_auto_asconf = 0;
510662306a36Sopenharmony_ci		list_del(&sp->auto_asconf_list);
510762306a36Sopenharmony_ci	}
510862306a36Sopenharmony_ci	sctp_endpoint_free(sp->ep);
510962306a36Sopenharmony_ci	sk_sockets_allocated_dec(sk);
511062306a36Sopenharmony_ci	sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1);
511162306a36Sopenharmony_ci}
511262306a36Sopenharmony_ci
511362306a36Sopenharmony_ci/* Triggered when there are no references on the socket anymore */
511462306a36Sopenharmony_cistatic void sctp_destruct_common(struct sock *sk)
511562306a36Sopenharmony_ci{
511662306a36Sopenharmony_ci	struct sctp_sock *sp = sctp_sk(sk);
511762306a36Sopenharmony_ci
511862306a36Sopenharmony_ci	/* Free up the HMAC transform. */
511962306a36Sopenharmony_ci	crypto_free_shash(sp->hmac);
512062306a36Sopenharmony_ci}
512162306a36Sopenharmony_ci
512262306a36Sopenharmony_cistatic void sctp_destruct_sock(struct sock *sk)
512362306a36Sopenharmony_ci{
512462306a36Sopenharmony_ci	sctp_destruct_common(sk);
512562306a36Sopenharmony_ci	inet_sock_destruct(sk);
512662306a36Sopenharmony_ci}
512762306a36Sopenharmony_ci
512862306a36Sopenharmony_ci/* API 4.1.7 shutdown() - TCP Style Syntax
512962306a36Sopenharmony_ci *     int shutdown(int socket, int how);
513062306a36Sopenharmony_ci *
513162306a36Sopenharmony_ci *     sd      - the socket descriptor of the association to be closed.
513262306a36Sopenharmony_ci *     how     - Specifies the type of shutdown.  The  values  are
513362306a36Sopenharmony_ci *               as follows:
513462306a36Sopenharmony_ci *               SHUT_RD
513562306a36Sopenharmony_ci *                     Disables further receive operations. No SCTP
513662306a36Sopenharmony_ci *                     protocol action is taken.
513762306a36Sopenharmony_ci *               SHUT_WR
513862306a36Sopenharmony_ci *                     Disables further send operations, and initiates
513962306a36Sopenharmony_ci *                     the SCTP shutdown sequence.
514062306a36Sopenharmony_ci *               SHUT_RDWR
514162306a36Sopenharmony_ci *                     Disables further send  and  receive  operations
514262306a36Sopenharmony_ci *                     and initiates the SCTP shutdown sequence.
514362306a36Sopenharmony_ci */
514462306a36Sopenharmony_cistatic void sctp_shutdown(struct sock *sk, int how)
514562306a36Sopenharmony_ci{
514662306a36Sopenharmony_ci	struct net *net = sock_net(sk);
514762306a36Sopenharmony_ci	struct sctp_endpoint *ep;
514862306a36Sopenharmony_ci
514962306a36Sopenharmony_ci	if (!sctp_style(sk, TCP))
515062306a36Sopenharmony_ci		return;
515162306a36Sopenharmony_ci
515262306a36Sopenharmony_ci	ep = sctp_sk(sk)->ep;
515362306a36Sopenharmony_ci	if (how & SEND_SHUTDOWN && !list_empty(&ep->asocs)) {
515462306a36Sopenharmony_ci		struct sctp_association *asoc;
515562306a36Sopenharmony_ci
515662306a36Sopenharmony_ci		inet_sk_set_state(sk, SCTP_SS_CLOSING);
515762306a36Sopenharmony_ci		asoc = list_entry(ep->asocs.next,
515862306a36Sopenharmony_ci				  struct sctp_association, asocs);
515962306a36Sopenharmony_ci		sctp_primitive_SHUTDOWN(net, asoc, NULL);
516062306a36Sopenharmony_ci	}
516162306a36Sopenharmony_ci}
516262306a36Sopenharmony_ci
516362306a36Sopenharmony_ciint sctp_get_sctp_info(struct sock *sk, struct sctp_association *asoc,
516462306a36Sopenharmony_ci		       struct sctp_info *info)
516562306a36Sopenharmony_ci{
516662306a36Sopenharmony_ci	struct sctp_transport *prim;
516762306a36Sopenharmony_ci	struct list_head *pos;
516862306a36Sopenharmony_ci	int mask;
516962306a36Sopenharmony_ci
517062306a36Sopenharmony_ci	memset(info, 0, sizeof(*info));
517162306a36Sopenharmony_ci	if (!asoc) {
517262306a36Sopenharmony_ci		struct sctp_sock *sp = sctp_sk(sk);
517362306a36Sopenharmony_ci
517462306a36Sopenharmony_ci		info->sctpi_s_autoclose = sp->autoclose;
517562306a36Sopenharmony_ci		info->sctpi_s_adaptation_ind = sp->adaptation_ind;
517662306a36Sopenharmony_ci		info->sctpi_s_pd_point = sp->pd_point;
517762306a36Sopenharmony_ci		info->sctpi_s_nodelay = sp->nodelay;
517862306a36Sopenharmony_ci		info->sctpi_s_disable_fragments = sp->disable_fragments;
517962306a36Sopenharmony_ci		info->sctpi_s_v4mapped = sp->v4mapped;
518062306a36Sopenharmony_ci		info->sctpi_s_frag_interleave = sp->frag_interleave;
518162306a36Sopenharmony_ci		info->sctpi_s_type = sp->type;
518262306a36Sopenharmony_ci
518362306a36Sopenharmony_ci		return 0;
518462306a36Sopenharmony_ci	}
518562306a36Sopenharmony_ci
518662306a36Sopenharmony_ci	info->sctpi_tag = asoc->c.my_vtag;
518762306a36Sopenharmony_ci	info->sctpi_state = asoc->state;
518862306a36Sopenharmony_ci	info->sctpi_rwnd = asoc->a_rwnd;
518962306a36Sopenharmony_ci	info->sctpi_unackdata = asoc->unack_data;
519062306a36Sopenharmony_ci	info->sctpi_penddata = sctp_tsnmap_pending(&asoc->peer.tsn_map);
519162306a36Sopenharmony_ci	info->sctpi_instrms = asoc->stream.incnt;
519262306a36Sopenharmony_ci	info->sctpi_outstrms = asoc->stream.outcnt;
519362306a36Sopenharmony_ci	list_for_each(pos, &asoc->base.inqueue.in_chunk_list)
519462306a36Sopenharmony_ci		info->sctpi_inqueue++;
519562306a36Sopenharmony_ci	list_for_each(pos, &asoc->outqueue.out_chunk_list)
519662306a36Sopenharmony_ci		info->sctpi_outqueue++;
519762306a36Sopenharmony_ci	info->sctpi_overall_error = asoc->overall_error_count;
519862306a36Sopenharmony_ci	info->sctpi_max_burst = asoc->max_burst;
519962306a36Sopenharmony_ci	info->sctpi_maxseg = asoc->frag_point;
520062306a36Sopenharmony_ci	info->sctpi_peer_rwnd = asoc->peer.rwnd;
520162306a36Sopenharmony_ci	info->sctpi_peer_tag = asoc->c.peer_vtag;
520262306a36Sopenharmony_ci
520362306a36Sopenharmony_ci	mask = asoc->peer.intl_capable << 1;
520462306a36Sopenharmony_ci	mask = (mask | asoc->peer.ecn_capable) << 1;
520562306a36Sopenharmony_ci	mask = (mask | asoc->peer.ipv4_address) << 1;
520662306a36Sopenharmony_ci	mask = (mask | asoc->peer.ipv6_address) << 1;
520762306a36Sopenharmony_ci	mask = (mask | asoc->peer.reconf_capable) << 1;
520862306a36Sopenharmony_ci	mask = (mask | asoc->peer.asconf_capable) << 1;
520962306a36Sopenharmony_ci	mask = (mask | asoc->peer.prsctp_capable) << 1;
521062306a36Sopenharmony_ci	mask = (mask | asoc->peer.auth_capable);
521162306a36Sopenharmony_ci	info->sctpi_peer_capable = mask;
521262306a36Sopenharmony_ci	mask = asoc->peer.sack_needed << 1;
521362306a36Sopenharmony_ci	mask = (mask | asoc->peer.sack_generation) << 1;
521462306a36Sopenharmony_ci	mask = (mask | asoc->peer.zero_window_announced);
521562306a36Sopenharmony_ci	info->sctpi_peer_sack = mask;
521662306a36Sopenharmony_ci
521762306a36Sopenharmony_ci	info->sctpi_isacks = asoc->stats.isacks;
521862306a36Sopenharmony_ci	info->sctpi_osacks = asoc->stats.osacks;
521962306a36Sopenharmony_ci	info->sctpi_opackets = asoc->stats.opackets;
522062306a36Sopenharmony_ci	info->sctpi_ipackets = asoc->stats.ipackets;
522162306a36Sopenharmony_ci	info->sctpi_rtxchunks = asoc->stats.rtxchunks;
522262306a36Sopenharmony_ci	info->sctpi_outofseqtsns = asoc->stats.outofseqtsns;
522362306a36Sopenharmony_ci	info->sctpi_idupchunks = asoc->stats.idupchunks;
522462306a36Sopenharmony_ci	info->sctpi_gapcnt = asoc->stats.gapcnt;
522562306a36Sopenharmony_ci	info->sctpi_ouodchunks = asoc->stats.ouodchunks;
522662306a36Sopenharmony_ci	info->sctpi_iuodchunks = asoc->stats.iuodchunks;
522762306a36Sopenharmony_ci	info->sctpi_oodchunks = asoc->stats.oodchunks;
522862306a36Sopenharmony_ci	info->sctpi_iodchunks = asoc->stats.iodchunks;
522962306a36Sopenharmony_ci	info->sctpi_octrlchunks = asoc->stats.octrlchunks;
523062306a36Sopenharmony_ci	info->sctpi_ictrlchunks = asoc->stats.ictrlchunks;
523162306a36Sopenharmony_ci
523262306a36Sopenharmony_ci	prim = asoc->peer.primary_path;
523362306a36Sopenharmony_ci	memcpy(&info->sctpi_p_address, &prim->ipaddr, sizeof(prim->ipaddr));
523462306a36Sopenharmony_ci	info->sctpi_p_state = prim->state;
523562306a36Sopenharmony_ci	info->sctpi_p_cwnd = prim->cwnd;
523662306a36Sopenharmony_ci	info->sctpi_p_srtt = prim->srtt;
523762306a36Sopenharmony_ci	info->sctpi_p_rto = jiffies_to_msecs(prim->rto);
523862306a36Sopenharmony_ci	info->sctpi_p_hbinterval = prim->hbinterval;
523962306a36Sopenharmony_ci	info->sctpi_p_pathmaxrxt = prim->pathmaxrxt;
524062306a36Sopenharmony_ci	info->sctpi_p_sackdelay = jiffies_to_msecs(prim->sackdelay);
524162306a36Sopenharmony_ci	info->sctpi_p_ssthresh = prim->ssthresh;
524262306a36Sopenharmony_ci	info->sctpi_p_partial_bytes_acked = prim->partial_bytes_acked;
524362306a36Sopenharmony_ci	info->sctpi_p_flight_size = prim->flight_size;
524462306a36Sopenharmony_ci	info->sctpi_p_error = prim->error_count;
524562306a36Sopenharmony_ci
524662306a36Sopenharmony_ci	return 0;
524762306a36Sopenharmony_ci}
524862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sctp_get_sctp_info);
524962306a36Sopenharmony_ci
525062306a36Sopenharmony_ci/* use callback to avoid exporting the core structure */
525162306a36Sopenharmony_civoid sctp_transport_walk_start(struct rhashtable_iter *iter) __acquires(RCU)
525262306a36Sopenharmony_ci{
525362306a36Sopenharmony_ci	rhltable_walk_enter(&sctp_transport_hashtable, iter);
525462306a36Sopenharmony_ci
525562306a36Sopenharmony_ci	rhashtable_walk_start(iter);
525662306a36Sopenharmony_ci}
525762306a36Sopenharmony_ci
525862306a36Sopenharmony_civoid sctp_transport_walk_stop(struct rhashtable_iter *iter) __releases(RCU)
525962306a36Sopenharmony_ci{
526062306a36Sopenharmony_ci	rhashtable_walk_stop(iter);
526162306a36Sopenharmony_ci	rhashtable_walk_exit(iter);
526262306a36Sopenharmony_ci}
526362306a36Sopenharmony_ci
526462306a36Sopenharmony_cistruct sctp_transport *sctp_transport_get_next(struct net *net,
526562306a36Sopenharmony_ci					       struct rhashtable_iter *iter)
526662306a36Sopenharmony_ci{
526762306a36Sopenharmony_ci	struct sctp_transport *t;
526862306a36Sopenharmony_ci
526962306a36Sopenharmony_ci	t = rhashtable_walk_next(iter);
527062306a36Sopenharmony_ci	for (; t; t = rhashtable_walk_next(iter)) {
527162306a36Sopenharmony_ci		if (IS_ERR(t)) {
527262306a36Sopenharmony_ci			if (PTR_ERR(t) == -EAGAIN)
527362306a36Sopenharmony_ci				continue;
527462306a36Sopenharmony_ci			break;
527562306a36Sopenharmony_ci		}
527662306a36Sopenharmony_ci
527762306a36Sopenharmony_ci		if (!sctp_transport_hold(t))
527862306a36Sopenharmony_ci			continue;
527962306a36Sopenharmony_ci
528062306a36Sopenharmony_ci		if (net_eq(t->asoc->base.net, net) &&
528162306a36Sopenharmony_ci		    t->asoc->peer.primary_path == t)
528262306a36Sopenharmony_ci			break;
528362306a36Sopenharmony_ci
528462306a36Sopenharmony_ci		sctp_transport_put(t);
528562306a36Sopenharmony_ci	}
528662306a36Sopenharmony_ci
528762306a36Sopenharmony_ci	return t;
528862306a36Sopenharmony_ci}
528962306a36Sopenharmony_ci
529062306a36Sopenharmony_cistruct sctp_transport *sctp_transport_get_idx(struct net *net,
529162306a36Sopenharmony_ci					      struct rhashtable_iter *iter,
529262306a36Sopenharmony_ci					      int pos)
529362306a36Sopenharmony_ci{
529462306a36Sopenharmony_ci	struct sctp_transport *t;
529562306a36Sopenharmony_ci
529662306a36Sopenharmony_ci	if (!pos)
529762306a36Sopenharmony_ci		return SEQ_START_TOKEN;
529862306a36Sopenharmony_ci
529962306a36Sopenharmony_ci	while ((t = sctp_transport_get_next(net, iter)) && !IS_ERR(t)) {
530062306a36Sopenharmony_ci		if (!--pos)
530162306a36Sopenharmony_ci			break;
530262306a36Sopenharmony_ci		sctp_transport_put(t);
530362306a36Sopenharmony_ci	}
530462306a36Sopenharmony_ci
530562306a36Sopenharmony_ci	return t;
530662306a36Sopenharmony_ci}
530762306a36Sopenharmony_ci
530862306a36Sopenharmony_ciint sctp_for_each_endpoint(int (*cb)(struct sctp_endpoint *, void *),
530962306a36Sopenharmony_ci			   void *p) {
531062306a36Sopenharmony_ci	int err = 0;
531162306a36Sopenharmony_ci	int hash = 0;
531262306a36Sopenharmony_ci	struct sctp_endpoint *ep;
531362306a36Sopenharmony_ci	struct sctp_hashbucket *head;
531462306a36Sopenharmony_ci
531562306a36Sopenharmony_ci	for (head = sctp_ep_hashtable; hash < sctp_ep_hashsize;
531662306a36Sopenharmony_ci	     hash++, head++) {
531762306a36Sopenharmony_ci		read_lock_bh(&head->lock);
531862306a36Sopenharmony_ci		sctp_for_each_hentry(ep, &head->chain) {
531962306a36Sopenharmony_ci			err = cb(ep, p);
532062306a36Sopenharmony_ci			if (err)
532162306a36Sopenharmony_ci				break;
532262306a36Sopenharmony_ci		}
532362306a36Sopenharmony_ci		read_unlock_bh(&head->lock);
532462306a36Sopenharmony_ci	}
532562306a36Sopenharmony_ci
532662306a36Sopenharmony_ci	return err;
532762306a36Sopenharmony_ci}
532862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sctp_for_each_endpoint);
532962306a36Sopenharmony_ci
533062306a36Sopenharmony_ciint sctp_transport_lookup_process(sctp_callback_t cb, struct net *net,
533162306a36Sopenharmony_ci				  const union sctp_addr *laddr,
533262306a36Sopenharmony_ci				  const union sctp_addr *paddr, void *p, int dif)
533362306a36Sopenharmony_ci{
533462306a36Sopenharmony_ci	struct sctp_transport *transport;
533562306a36Sopenharmony_ci	struct sctp_endpoint *ep;
533662306a36Sopenharmony_ci	int err = -ENOENT;
533762306a36Sopenharmony_ci
533862306a36Sopenharmony_ci	rcu_read_lock();
533962306a36Sopenharmony_ci	transport = sctp_addrs_lookup_transport(net, laddr, paddr, dif, dif);
534062306a36Sopenharmony_ci	if (!transport) {
534162306a36Sopenharmony_ci		rcu_read_unlock();
534262306a36Sopenharmony_ci		return err;
534362306a36Sopenharmony_ci	}
534462306a36Sopenharmony_ci	ep = transport->asoc->ep;
534562306a36Sopenharmony_ci	if (!sctp_endpoint_hold(ep)) { /* asoc can be peeled off */
534662306a36Sopenharmony_ci		sctp_transport_put(transport);
534762306a36Sopenharmony_ci		rcu_read_unlock();
534862306a36Sopenharmony_ci		return err;
534962306a36Sopenharmony_ci	}
535062306a36Sopenharmony_ci	rcu_read_unlock();
535162306a36Sopenharmony_ci
535262306a36Sopenharmony_ci	err = cb(ep, transport, p);
535362306a36Sopenharmony_ci	sctp_endpoint_put(ep);
535462306a36Sopenharmony_ci	sctp_transport_put(transport);
535562306a36Sopenharmony_ci	return err;
535662306a36Sopenharmony_ci}
535762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sctp_transport_lookup_process);
535862306a36Sopenharmony_ci
535962306a36Sopenharmony_ciint sctp_transport_traverse_process(sctp_callback_t cb, sctp_callback_t cb_done,
536062306a36Sopenharmony_ci				    struct net *net, int *pos, void *p)
536162306a36Sopenharmony_ci{
536262306a36Sopenharmony_ci	struct rhashtable_iter hti;
536362306a36Sopenharmony_ci	struct sctp_transport *tsp;
536462306a36Sopenharmony_ci	struct sctp_endpoint *ep;
536562306a36Sopenharmony_ci	int ret;
536662306a36Sopenharmony_ci
536762306a36Sopenharmony_ciagain:
536862306a36Sopenharmony_ci	ret = 0;
536962306a36Sopenharmony_ci	sctp_transport_walk_start(&hti);
537062306a36Sopenharmony_ci
537162306a36Sopenharmony_ci	tsp = sctp_transport_get_idx(net, &hti, *pos + 1);
537262306a36Sopenharmony_ci	for (; !IS_ERR_OR_NULL(tsp); tsp = sctp_transport_get_next(net, &hti)) {
537362306a36Sopenharmony_ci		ep = tsp->asoc->ep;
537462306a36Sopenharmony_ci		if (sctp_endpoint_hold(ep)) { /* asoc can be peeled off */
537562306a36Sopenharmony_ci			ret = cb(ep, tsp, p);
537662306a36Sopenharmony_ci			if (ret)
537762306a36Sopenharmony_ci				break;
537862306a36Sopenharmony_ci			sctp_endpoint_put(ep);
537962306a36Sopenharmony_ci		}
538062306a36Sopenharmony_ci		(*pos)++;
538162306a36Sopenharmony_ci		sctp_transport_put(tsp);
538262306a36Sopenharmony_ci	}
538362306a36Sopenharmony_ci	sctp_transport_walk_stop(&hti);
538462306a36Sopenharmony_ci
538562306a36Sopenharmony_ci	if (ret) {
538662306a36Sopenharmony_ci		if (cb_done && !cb_done(ep, tsp, p)) {
538762306a36Sopenharmony_ci			(*pos)++;
538862306a36Sopenharmony_ci			sctp_endpoint_put(ep);
538962306a36Sopenharmony_ci			sctp_transport_put(tsp);
539062306a36Sopenharmony_ci			goto again;
539162306a36Sopenharmony_ci		}
539262306a36Sopenharmony_ci		sctp_endpoint_put(ep);
539362306a36Sopenharmony_ci		sctp_transport_put(tsp);
539462306a36Sopenharmony_ci	}
539562306a36Sopenharmony_ci
539662306a36Sopenharmony_ci	return ret;
539762306a36Sopenharmony_ci}
539862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sctp_transport_traverse_process);
539962306a36Sopenharmony_ci
540062306a36Sopenharmony_ci/* 7.2.1 Association Status (SCTP_STATUS)
540162306a36Sopenharmony_ci
540262306a36Sopenharmony_ci * Applications can retrieve current status information about an
540362306a36Sopenharmony_ci * association, including association state, peer receiver window size,
540462306a36Sopenharmony_ci * number of unacked data chunks, and number of data chunks pending
540562306a36Sopenharmony_ci * receipt.  This information is read-only.
540662306a36Sopenharmony_ci */
540762306a36Sopenharmony_cistatic int sctp_getsockopt_sctp_status(struct sock *sk, int len,
540862306a36Sopenharmony_ci				       char __user *optval,
540962306a36Sopenharmony_ci				       int __user *optlen)
541062306a36Sopenharmony_ci{
541162306a36Sopenharmony_ci	struct sctp_status status;
541262306a36Sopenharmony_ci	struct sctp_association *asoc = NULL;
541362306a36Sopenharmony_ci	struct sctp_transport *transport;
541462306a36Sopenharmony_ci	sctp_assoc_t associd;
541562306a36Sopenharmony_ci	int retval = 0;
541662306a36Sopenharmony_ci
541762306a36Sopenharmony_ci	if (len < sizeof(status)) {
541862306a36Sopenharmony_ci		retval = -EINVAL;
541962306a36Sopenharmony_ci		goto out;
542062306a36Sopenharmony_ci	}
542162306a36Sopenharmony_ci
542262306a36Sopenharmony_ci	len = sizeof(status);
542362306a36Sopenharmony_ci	if (copy_from_user(&status, optval, len)) {
542462306a36Sopenharmony_ci		retval = -EFAULT;
542562306a36Sopenharmony_ci		goto out;
542662306a36Sopenharmony_ci	}
542762306a36Sopenharmony_ci
542862306a36Sopenharmony_ci	associd = status.sstat_assoc_id;
542962306a36Sopenharmony_ci	asoc = sctp_id2assoc(sk, associd);
543062306a36Sopenharmony_ci	if (!asoc) {
543162306a36Sopenharmony_ci		retval = -EINVAL;
543262306a36Sopenharmony_ci		goto out;
543362306a36Sopenharmony_ci	}
543462306a36Sopenharmony_ci
543562306a36Sopenharmony_ci	transport = asoc->peer.primary_path;
543662306a36Sopenharmony_ci
543762306a36Sopenharmony_ci	status.sstat_assoc_id = sctp_assoc2id(asoc);
543862306a36Sopenharmony_ci	status.sstat_state = sctp_assoc_to_state(asoc);
543962306a36Sopenharmony_ci	status.sstat_rwnd =  asoc->peer.rwnd;
544062306a36Sopenharmony_ci	status.sstat_unackdata = asoc->unack_data;
544162306a36Sopenharmony_ci
544262306a36Sopenharmony_ci	status.sstat_penddata = sctp_tsnmap_pending(&asoc->peer.tsn_map);
544362306a36Sopenharmony_ci	status.sstat_instrms = asoc->stream.incnt;
544462306a36Sopenharmony_ci	status.sstat_outstrms = asoc->stream.outcnt;
544562306a36Sopenharmony_ci	status.sstat_fragmentation_point = asoc->frag_point;
544662306a36Sopenharmony_ci	status.sstat_primary.spinfo_assoc_id = sctp_assoc2id(transport->asoc);
544762306a36Sopenharmony_ci	memcpy(&status.sstat_primary.spinfo_address, &transport->ipaddr,
544862306a36Sopenharmony_ci			transport->af_specific->sockaddr_len);
544962306a36Sopenharmony_ci	/* Map ipv4 address into v4-mapped-on-v6 address.  */
545062306a36Sopenharmony_ci	sctp_get_pf_specific(sk->sk_family)->addr_to_user(sctp_sk(sk),
545162306a36Sopenharmony_ci		(union sctp_addr *)&status.sstat_primary.spinfo_address);
545262306a36Sopenharmony_ci	status.sstat_primary.spinfo_state = transport->state;
545362306a36Sopenharmony_ci	status.sstat_primary.spinfo_cwnd = transport->cwnd;
545462306a36Sopenharmony_ci	status.sstat_primary.spinfo_srtt = transport->srtt;
545562306a36Sopenharmony_ci	status.sstat_primary.spinfo_rto = jiffies_to_msecs(transport->rto);
545662306a36Sopenharmony_ci	status.sstat_primary.spinfo_mtu = transport->pathmtu;
545762306a36Sopenharmony_ci
545862306a36Sopenharmony_ci	if (status.sstat_primary.spinfo_state == SCTP_UNKNOWN)
545962306a36Sopenharmony_ci		status.sstat_primary.spinfo_state = SCTP_ACTIVE;
546062306a36Sopenharmony_ci
546162306a36Sopenharmony_ci	if (put_user(len, optlen)) {
546262306a36Sopenharmony_ci		retval = -EFAULT;
546362306a36Sopenharmony_ci		goto out;
546462306a36Sopenharmony_ci	}
546562306a36Sopenharmony_ci
546662306a36Sopenharmony_ci	pr_debug("%s: len:%d, state:%d, rwnd:%d, assoc_id:%d\n",
546762306a36Sopenharmony_ci		 __func__, len, status.sstat_state, status.sstat_rwnd,
546862306a36Sopenharmony_ci		 status.sstat_assoc_id);
546962306a36Sopenharmony_ci
547062306a36Sopenharmony_ci	if (copy_to_user(optval, &status, len)) {
547162306a36Sopenharmony_ci		retval = -EFAULT;
547262306a36Sopenharmony_ci		goto out;
547362306a36Sopenharmony_ci	}
547462306a36Sopenharmony_ci
547562306a36Sopenharmony_ciout:
547662306a36Sopenharmony_ci	return retval;
547762306a36Sopenharmony_ci}
547862306a36Sopenharmony_ci
547962306a36Sopenharmony_ci
548062306a36Sopenharmony_ci/* 7.2.2 Peer Address Information (SCTP_GET_PEER_ADDR_INFO)
548162306a36Sopenharmony_ci *
548262306a36Sopenharmony_ci * Applications can retrieve information about a specific peer address
548362306a36Sopenharmony_ci * of an association, including its reachability state, congestion
548462306a36Sopenharmony_ci * window, and retransmission timer values.  This information is
548562306a36Sopenharmony_ci * read-only.
548662306a36Sopenharmony_ci */
548762306a36Sopenharmony_cistatic int sctp_getsockopt_peer_addr_info(struct sock *sk, int len,
548862306a36Sopenharmony_ci					  char __user *optval,
548962306a36Sopenharmony_ci					  int __user *optlen)
549062306a36Sopenharmony_ci{
549162306a36Sopenharmony_ci	struct sctp_paddrinfo pinfo;
549262306a36Sopenharmony_ci	struct sctp_transport *transport;
549362306a36Sopenharmony_ci	int retval = 0;
549462306a36Sopenharmony_ci
549562306a36Sopenharmony_ci	if (len < sizeof(pinfo)) {
549662306a36Sopenharmony_ci		retval = -EINVAL;
549762306a36Sopenharmony_ci		goto out;
549862306a36Sopenharmony_ci	}
549962306a36Sopenharmony_ci
550062306a36Sopenharmony_ci	len = sizeof(pinfo);
550162306a36Sopenharmony_ci	if (copy_from_user(&pinfo, optval, len)) {
550262306a36Sopenharmony_ci		retval = -EFAULT;
550362306a36Sopenharmony_ci		goto out;
550462306a36Sopenharmony_ci	}
550562306a36Sopenharmony_ci
550662306a36Sopenharmony_ci	transport = sctp_addr_id2transport(sk, &pinfo.spinfo_address,
550762306a36Sopenharmony_ci					   pinfo.spinfo_assoc_id);
550862306a36Sopenharmony_ci	if (!transport) {
550962306a36Sopenharmony_ci		retval = -EINVAL;
551062306a36Sopenharmony_ci		goto out;
551162306a36Sopenharmony_ci	}
551262306a36Sopenharmony_ci
551362306a36Sopenharmony_ci	if (transport->state == SCTP_PF &&
551462306a36Sopenharmony_ci	    transport->asoc->pf_expose == SCTP_PF_EXPOSE_DISABLE) {
551562306a36Sopenharmony_ci		retval = -EACCES;
551662306a36Sopenharmony_ci		goto out;
551762306a36Sopenharmony_ci	}
551862306a36Sopenharmony_ci
551962306a36Sopenharmony_ci	pinfo.spinfo_assoc_id = sctp_assoc2id(transport->asoc);
552062306a36Sopenharmony_ci	pinfo.spinfo_state = transport->state;
552162306a36Sopenharmony_ci	pinfo.spinfo_cwnd = transport->cwnd;
552262306a36Sopenharmony_ci	pinfo.spinfo_srtt = transport->srtt;
552362306a36Sopenharmony_ci	pinfo.spinfo_rto = jiffies_to_msecs(transport->rto);
552462306a36Sopenharmony_ci	pinfo.spinfo_mtu = transport->pathmtu;
552562306a36Sopenharmony_ci
552662306a36Sopenharmony_ci	if (pinfo.spinfo_state == SCTP_UNKNOWN)
552762306a36Sopenharmony_ci		pinfo.spinfo_state = SCTP_ACTIVE;
552862306a36Sopenharmony_ci
552962306a36Sopenharmony_ci	if (put_user(len, optlen)) {
553062306a36Sopenharmony_ci		retval = -EFAULT;
553162306a36Sopenharmony_ci		goto out;
553262306a36Sopenharmony_ci	}
553362306a36Sopenharmony_ci
553462306a36Sopenharmony_ci	if (copy_to_user(optval, &pinfo, len)) {
553562306a36Sopenharmony_ci		retval = -EFAULT;
553662306a36Sopenharmony_ci		goto out;
553762306a36Sopenharmony_ci	}
553862306a36Sopenharmony_ci
553962306a36Sopenharmony_ciout:
554062306a36Sopenharmony_ci	return retval;
554162306a36Sopenharmony_ci}
554262306a36Sopenharmony_ci
554362306a36Sopenharmony_ci/* 7.1.12 Enable/Disable message fragmentation (SCTP_DISABLE_FRAGMENTS)
554462306a36Sopenharmony_ci *
554562306a36Sopenharmony_ci * This option is a on/off flag.  If enabled no SCTP message
554662306a36Sopenharmony_ci * fragmentation will be performed.  Instead if a message being sent
554762306a36Sopenharmony_ci * exceeds the current PMTU size, the message will NOT be sent and
554862306a36Sopenharmony_ci * instead a error will be indicated to the user.
554962306a36Sopenharmony_ci */
555062306a36Sopenharmony_cistatic int sctp_getsockopt_disable_fragments(struct sock *sk, int len,
555162306a36Sopenharmony_ci					char __user *optval, int __user *optlen)
555262306a36Sopenharmony_ci{
555362306a36Sopenharmony_ci	int val;
555462306a36Sopenharmony_ci
555562306a36Sopenharmony_ci	if (len < sizeof(int))
555662306a36Sopenharmony_ci		return -EINVAL;
555762306a36Sopenharmony_ci
555862306a36Sopenharmony_ci	len = sizeof(int);
555962306a36Sopenharmony_ci	val = (sctp_sk(sk)->disable_fragments == 1);
556062306a36Sopenharmony_ci	if (put_user(len, optlen))
556162306a36Sopenharmony_ci		return -EFAULT;
556262306a36Sopenharmony_ci	if (copy_to_user(optval, &val, len))
556362306a36Sopenharmony_ci		return -EFAULT;
556462306a36Sopenharmony_ci	return 0;
556562306a36Sopenharmony_ci}
556662306a36Sopenharmony_ci
556762306a36Sopenharmony_ci/* 7.1.15 Set notification and ancillary events (SCTP_EVENTS)
556862306a36Sopenharmony_ci *
556962306a36Sopenharmony_ci * This socket option is used to specify various notifications and
557062306a36Sopenharmony_ci * ancillary data the user wishes to receive.
557162306a36Sopenharmony_ci */
557262306a36Sopenharmony_cistatic int sctp_getsockopt_events(struct sock *sk, int len, char __user *optval,
557362306a36Sopenharmony_ci				  int __user *optlen)
557462306a36Sopenharmony_ci{
557562306a36Sopenharmony_ci	struct sctp_event_subscribe subscribe;
557662306a36Sopenharmony_ci	__u8 *sn_type = (__u8 *)&subscribe;
557762306a36Sopenharmony_ci	int i;
557862306a36Sopenharmony_ci
557962306a36Sopenharmony_ci	if (len == 0)
558062306a36Sopenharmony_ci		return -EINVAL;
558162306a36Sopenharmony_ci	if (len > sizeof(struct sctp_event_subscribe))
558262306a36Sopenharmony_ci		len = sizeof(struct sctp_event_subscribe);
558362306a36Sopenharmony_ci	if (put_user(len, optlen))
558462306a36Sopenharmony_ci		return -EFAULT;
558562306a36Sopenharmony_ci
558662306a36Sopenharmony_ci	for (i = 0; i < len; i++)
558762306a36Sopenharmony_ci		sn_type[i] = sctp_ulpevent_type_enabled(sctp_sk(sk)->subscribe,
558862306a36Sopenharmony_ci							SCTP_SN_TYPE_BASE + i);
558962306a36Sopenharmony_ci
559062306a36Sopenharmony_ci	if (copy_to_user(optval, &subscribe, len))
559162306a36Sopenharmony_ci		return -EFAULT;
559262306a36Sopenharmony_ci
559362306a36Sopenharmony_ci	return 0;
559462306a36Sopenharmony_ci}
559562306a36Sopenharmony_ci
559662306a36Sopenharmony_ci/* 7.1.8 Automatic Close of associations (SCTP_AUTOCLOSE)
559762306a36Sopenharmony_ci *
559862306a36Sopenharmony_ci * This socket option is applicable to the UDP-style socket only.  When
559962306a36Sopenharmony_ci * set it will cause associations that are idle for more than the
560062306a36Sopenharmony_ci * specified number of seconds to automatically close.  An association
560162306a36Sopenharmony_ci * being idle is defined an association that has NOT sent or received
560262306a36Sopenharmony_ci * user data.  The special value of '0' indicates that no automatic
560362306a36Sopenharmony_ci * close of any associations should be performed.  The option expects an
560462306a36Sopenharmony_ci * integer defining the number of seconds of idle time before an
560562306a36Sopenharmony_ci * association is closed.
560662306a36Sopenharmony_ci */
560762306a36Sopenharmony_cistatic int sctp_getsockopt_autoclose(struct sock *sk, int len, char __user *optval, int __user *optlen)
560862306a36Sopenharmony_ci{
560962306a36Sopenharmony_ci	/* Applicable to UDP-style socket only */
561062306a36Sopenharmony_ci	if (sctp_style(sk, TCP))
561162306a36Sopenharmony_ci		return -EOPNOTSUPP;
561262306a36Sopenharmony_ci	if (len < sizeof(int))
561362306a36Sopenharmony_ci		return -EINVAL;
561462306a36Sopenharmony_ci	len = sizeof(int);
561562306a36Sopenharmony_ci	if (put_user(len, optlen))
561662306a36Sopenharmony_ci		return -EFAULT;
561762306a36Sopenharmony_ci	if (put_user(sctp_sk(sk)->autoclose, (int __user *)optval))
561862306a36Sopenharmony_ci		return -EFAULT;
561962306a36Sopenharmony_ci	return 0;
562062306a36Sopenharmony_ci}
562162306a36Sopenharmony_ci
562262306a36Sopenharmony_ci/* Helper routine to branch off an association to a new socket.  */
562362306a36Sopenharmony_ciint sctp_do_peeloff(struct sock *sk, sctp_assoc_t id, struct socket **sockp)
562462306a36Sopenharmony_ci{
562562306a36Sopenharmony_ci	struct sctp_association *asoc = sctp_id2assoc(sk, id);
562662306a36Sopenharmony_ci	struct sctp_sock *sp = sctp_sk(sk);
562762306a36Sopenharmony_ci	struct socket *sock;
562862306a36Sopenharmony_ci	int err = 0;
562962306a36Sopenharmony_ci
563062306a36Sopenharmony_ci	/* Do not peel off from one netns to another one. */
563162306a36Sopenharmony_ci	if (!net_eq(current->nsproxy->net_ns, sock_net(sk)))
563262306a36Sopenharmony_ci		return -EINVAL;
563362306a36Sopenharmony_ci
563462306a36Sopenharmony_ci	if (!asoc)
563562306a36Sopenharmony_ci		return -EINVAL;
563662306a36Sopenharmony_ci
563762306a36Sopenharmony_ci	/* An association cannot be branched off from an already peeled-off
563862306a36Sopenharmony_ci	 * socket, nor is this supported for tcp style sockets.
563962306a36Sopenharmony_ci	 */
564062306a36Sopenharmony_ci	if (!sctp_style(sk, UDP))
564162306a36Sopenharmony_ci		return -EINVAL;
564262306a36Sopenharmony_ci
564362306a36Sopenharmony_ci	/* Create a new socket.  */
564462306a36Sopenharmony_ci	err = sock_create(sk->sk_family, SOCK_SEQPACKET, IPPROTO_SCTP, &sock);
564562306a36Sopenharmony_ci	if (err < 0)
564662306a36Sopenharmony_ci		return err;
564762306a36Sopenharmony_ci
564862306a36Sopenharmony_ci	sctp_copy_sock(sock->sk, sk, asoc);
564962306a36Sopenharmony_ci
565062306a36Sopenharmony_ci	/* Make peeled-off sockets more like 1-1 accepted sockets.
565162306a36Sopenharmony_ci	 * Set the daddr and initialize id to something more random and also
565262306a36Sopenharmony_ci	 * copy over any ip options.
565362306a36Sopenharmony_ci	 */
565462306a36Sopenharmony_ci	sp->pf->to_sk_daddr(&asoc->peer.primary_addr, sock->sk);
565562306a36Sopenharmony_ci	sp->pf->copy_ip_options(sk, sock->sk);
565662306a36Sopenharmony_ci
565762306a36Sopenharmony_ci	/* Populate the fields of the newsk from the oldsk and migrate the
565862306a36Sopenharmony_ci	 * asoc to the newsk.
565962306a36Sopenharmony_ci	 */
566062306a36Sopenharmony_ci	err = sctp_sock_migrate(sk, sock->sk, asoc,
566162306a36Sopenharmony_ci				SCTP_SOCKET_UDP_HIGH_BANDWIDTH);
566262306a36Sopenharmony_ci	if (err) {
566362306a36Sopenharmony_ci		sock_release(sock);
566462306a36Sopenharmony_ci		sock = NULL;
566562306a36Sopenharmony_ci	}
566662306a36Sopenharmony_ci
566762306a36Sopenharmony_ci	*sockp = sock;
566862306a36Sopenharmony_ci
566962306a36Sopenharmony_ci	return err;
567062306a36Sopenharmony_ci}
567162306a36Sopenharmony_ciEXPORT_SYMBOL(sctp_do_peeloff);
567262306a36Sopenharmony_ci
567362306a36Sopenharmony_cistatic int sctp_getsockopt_peeloff_common(struct sock *sk, sctp_peeloff_arg_t *peeloff,
567462306a36Sopenharmony_ci					  struct file **newfile, unsigned flags)
567562306a36Sopenharmony_ci{
567662306a36Sopenharmony_ci	struct socket *newsock;
567762306a36Sopenharmony_ci	int retval;
567862306a36Sopenharmony_ci
567962306a36Sopenharmony_ci	retval = sctp_do_peeloff(sk, peeloff->associd, &newsock);
568062306a36Sopenharmony_ci	if (retval < 0)
568162306a36Sopenharmony_ci		goto out;
568262306a36Sopenharmony_ci
568362306a36Sopenharmony_ci	/* Map the socket to an unused fd that can be returned to the user.  */
568462306a36Sopenharmony_ci	retval = get_unused_fd_flags(flags & SOCK_CLOEXEC);
568562306a36Sopenharmony_ci	if (retval < 0) {
568662306a36Sopenharmony_ci		sock_release(newsock);
568762306a36Sopenharmony_ci		goto out;
568862306a36Sopenharmony_ci	}
568962306a36Sopenharmony_ci
569062306a36Sopenharmony_ci	*newfile = sock_alloc_file(newsock, 0, NULL);
569162306a36Sopenharmony_ci	if (IS_ERR(*newfile)) {
569262306a36Sopenharmony_ci		put_unused_fd(retval);
569362306a36Sopenharmony_ci		retval = PTR_ERR(*newfile);
569462306a36Sopenharmony_ci		*newfile = NULL;
569562306a36Sopenharmony_ci		return retval;
569662306a36Sopenharmony_ci	}
569762306a36Sopenharmony_ci
569862306a36Sopenharmony_ci	pr_debug("%s: sk:%p, newsk:%p, sd:%d\n", __func__, sk, newsock->sk,
569962306a36Sopenharmony_ci		 retval);
570062306a36Sopenharmony_ci
570162306a36Sopenharmony_ci	peeloff->sd = retval;
570262306a36Sopenharmony_ci
570362306a36Sopenharmony_ci	if (flags & SOCK_NONBLOCK)
570462306a36Sopenharmony_ci		(*newfile)->f_flags |= O_NONBLOCK;
570562306a36Sopenharmony_ciout:
570662306a36Sopenharmony_ci	return retval;
570762306a36Sopenharmony_ci}
570862306a36Sopenharmony_ci
570962306a36Sopenharmony_cistatic int sctp_getsockopt_peeloff(struct sock *sk, int len, char __user *optval, int __user *optlen)
571062306a36Sopenharmony_ci{
571162306a36Sopenharmony_ci	sctp_peeloff_arg_t peeloff;
571262306a36Sopenharmony_ci	struct file *newfile = NULL;
571362306a36Sopenharmony_ci	int retval = 0;
571462306a36Sopenharmony_ci
571562306a36Sopenharmony_ci	if (len < sizeof(sctp_peeloff_arg_t))
571662306a36Sopenharmony_ci		return -EINVAL;
571762306a36Sopenharmony_ci	len = sizeof(sctp_peeloff_arg_t);
571862306a36Sopenharmony_ci	if (copy_from_user(&peeloff, optval, len))
571962306a36Sopenharmony_ci		return -EFAULT;
572062306a36Sopenharmony_ci
572162306a36Sopenharmony_ci	retval = sctp_getsockopt_peeloff_common(sk, &peeloff, &newfile, 0);
572262306a36Sopenharmony_ci	if (retval < 0)
572362306a36Sopenharmony_ci		goto out;
572462306a36Sopenharmony_ci
572562306a36Sopenharmony_ci	/* Return the fd mapped to the new socket.  */
572662306a36Sopenharmony_ci	if (put_user(len, optlen)) {
572762306a36Sopenharmony_ci		fput(newfile);
572862306a36Sopenharmony_ci		put_unused_fd(retval);
572962306a36Sopenharmony_ci		return -EFAULT;
573062306a36Sopenharmony_ci	}
573162306a36Sopenharmony_ci
573262306a36Sopenharmony_ci	if (copy_to_user(optval, &peeloff, len)) {
573362306a36Sopenharmony_ci		fput(newfile);
573462306a36Sopenharmony_ci		put_unused_fd(retval);
573562306a36Sopenharmony_ci		return -EFAULT;
573662306a36Sopenharmony_ci	}
573762306a36Sopenharmony_ci	fd_install(retval, newfile);
573862306a36Sopenharmony_ciout:
573962306a36Sopenharmony_ci	return retval;
574062306a36Sopenharmony_ci}
574162306a36Sopenharmony_ci
574262306a36Sopenharmony_cistatic int sctp_getsockopt_peeloff_flags(struct sock *sk, int len,
574362306a36Sopenharmony_ci					 char __user *optval, int __user *optlen)
574462306a36Sopenharmony_ci{
574562306a36Sopenharmony_ci	sctp_peeloff_flags_arg_t peeloff;
574662306a36Sopenharmony_ci	struct file *newfile = NULL;
574762306a36Sopenharmony_ci	int retval = 0;
574862306a36Sopenharmony_ci
574962306a36Sopenharmony_ci	if (len < sizeof(sctp_peeloff_flags_arg_t))
575062306a36Sopenharmony_ci		return -EINVAL;
575162306a36Sopenharmony_ci	len = sizeof(sctp_peeloff_flags_arg_t);
575262306a36Sopenharmony_ci	if (copy_from_user(&peeloff, optval, len))
575362306a36Sopenharmony_ci		return -EFAULT;
575462306a36Sopenharmony_ci
575562306a36Sopenharmony_ci	retval = sctp_getsockopt_peeloff_common(sk, &peeloff.p_arg,
575662306a36Sopenharmony_ci						&newfile, peeloff.flags);
575762306a36Sopenharmony_ci	if (retval < 0)
575862306a36Sopenharmony_ci		goto out;
575962306a36Sopenharmony_ci
576062306a36Sopenharmony_ci	/* Return the fd mapped to the new socket.  */
576162306a36Sopenharmony_ci	if (put_user(len, optlen)) {
576262306a36Sopenharmony_ci		fput(newfile);
576362306a36Sopenharmony_ci		put_unused_fd(retval);
576462306a36Sopenharmony_ci		return -EFAULT;
576562306a36Sopenharmony_ci	}
576662306a36Sopenharmony_ci
576762306a36Sopenharmony_ci	if (copy_to_user(optval, &peeloff, len)) {
576862306a36Sopenharmony_ci		fput(newfile);
576962306a36Sopenharmony_ci		put_unused_fd(retval);
577062306a36Sopenharmony_ci		return -EFAULT;
577162306a36Sopenharmony_ci	}
577262306a36Sopenharmony_ci	fd_install(retval, newfile);
577362306a36Sopenharmony_ciout:
577462306a36Sopenharmony_ci	return retval;
577562306a36Sopenharmony_ci}
577662306a36Sopenharmony_ci
577762306a36Sopenharmony_ci/* 7.1.13 Peer Address Parameters (SCTP_PEER_ADDR_PARAMS)
577862306a36Sopenharmony_ci *
577962306a36Sopenharmony_ci * Applications can enable or disable heartbeats for any peer address of
578062306a36Sopenharmony_ci * an association, modify an address's heartbeat interval, force a
578162306a36Sopenharmony_ci * heartbeat to be sent immediately, and adjust the address's maximum
578262306a36Sopenharmony_ci * number of retransmissions sent before an address is considered
578362306a36Sopenharmony_ci * unreachable.  The following structure is used to access and modify an
578462306a36Sopenharmony_ci * address's parameters:
578562306a36Sopenharmony_ci *
578662306a36Sopenharmony_ci *  struct sctp_paddrparams {
578762306a36Sopenharmony_ci *     sctp_assoc_t            spp_assoc_id;
578862306a36Sopenharmony_ci *     struct sockaddr_storage spp_address;
578962306a36Sopenharmony_ci *     uint32_t                spp_hbinterval;
579062306a36Sopenharmony_ci *     uint16_t                spp_pathmaxrxt;
579162306a36Sopenharmony_ci *     uint32_t                spp_pathmtu;
579262306a36Sopenharmony_ci *     uint32_t                spp_sackdelay;
579362306a36Sopenharmony_ci *     uint32_t                spp_flags;
579462306a36Sopenharmony_ci * };
579562306a36Sopenharmony_ci *
579662306a36Sopenharmony_ci *   spp_assoc_id    - (one-to-many style socket) This is filled in the
579762306a36Sopenharmony_ci *                     application, and identifies the association for
579862306a36Sopenharmony_ci *                     this query.
579962306a36Sopenharmony_ci *   spp_address     - This specifies which address is of interest.
580062306a36Sopenharmony_ci *   spp_hbinterval  - This contains the value of the heartbeat interval,
580162306a36Sopenharmony_ci *                     in milliseconds.  If a  value of zero
580262306a36Sopenharmony_ci *                     is present in this field then no changes are to
580362306a36Sopenharmony_ci *                     be made to this parameter.
580462306a36Sopenharmony_ci *   spp_pathmaxrxt  - This contains the maximum number of
580562306a36Sopenharmony_ci *                     retransmissions before this address shall be
580662306a36Sopenharmony_ci *                     considered unreachable. If a  value of zero
580762306a36Sopenharmony_ci *                     is present in this field then no changes are to
580862306a36Sopenharmony_ci *                     be made to this parameter.
580962306a36Sopenharmony_ci *   spp_pathmtu     - When Path MTU discovery is disabled the value
581062306a36Sopenharmony_ci *                     specified here will be the "fixed" path mtu.
581162306a36Sopenharmony_ci *                     Note that if the spp_address field is empty
581262306a36Sopenharmony_ci *                     then all associations on this address will
581362306a36Sopenharmony_ci *                     have this fixed path mtu set upon them.
581462306a36Sopenharmony_ci *
581562306a36Sopenharmony_ci *   spp_sackdelay   - When delayed sack is enabled, this value specifies
581662306a36Sopenharmony_ci *                     the number of milliseconds that sacks will be delayed
581762306a36Sopenharmony_ci *                     for. This value will apply to all addresses of an
581862306a36Sopenharmony_ci *                     association if the spp_address field is empty. Note
581962306a36Sopenharmony_ci *                     also, that if delayed sack is enabled and this
582062306a36Sopenharmony_ci *                     value is set to 0, no change is made to the last
582162306a36Sopenharmony_ci *                     recorded delayed sack timer value.
582262306a36Sopenharmony_ci *
582362306a36Sopenharmony_ci *   spp_flags       - These flags are used to control various features
582462306a36Sopenharmony_ci *                     on an association. The flag field may contain
582562306a36Sopenharmony_ci *                     zero or more of the following options.
582662306a36Sopenharmony_ci *
582762306a36Sopenharmony_ci *                     SPP_HB_ENABLE  - Enable heartbeats on the
582862306a36Sopenharmony_ci *                     specified address. Note that if the address
582962306a36Sopenharmony_ci *                     field is empty all addresses for the association
583062306a36Sopenharmony_ci *                     have heartbeats enabled upon them.
583162306a36Sopenharmony_ci *
583262306a36Sopenharmony_ci *                     SPP_HB_DISABLE - Disable heartbeats on the
583362306a36Sopenharmony_ci *                     speicifed address. Note that if the address
583462306a36Sopenharmony_ci *                     field is empty all addresses for the association
583562306a36Sopenharmony_ci *                     will have their heartbeats disabled. Note also
583662306a36Sopenharmony_ci *                     that SPP_HB_ENABLE and SPP_HB_DISABLE are
583762306a36Sopenharmony_ci *                     mutually exclusive, only one of these two should
583862306a36Sopenharmony_ci *                     be specified. Enabling both fields will have
583962306a36Sopenharmony_ci *                     undetermined results.
584062306a36Sopenharmony_ci *
584162306a36Sopenharmony_ci *                     SPP_HB_DEMAND - Request a user initiated heartbeat
584262306a36Sopenharmony_ci *                     to be made immediately.
584362306a36Sopenharmony_ci *
584462306a36Sopenharmony_ci *                     SPP_PMTUD_ENABLE - This field will enable PMTU
584562306a36Sopenharmony_ci *                     discovery upon the specified address. Note that
584662306a36Sopenharmony_ci *                     if the address feild is empty then all addresses
584762306a36Sopenharmony_ci *                     on the association are effected.
584862306a36Sopenharmony_ci *
584962306a36Sopenharmony_ci *                     SPP_PMTUD_DISABLE - This field will disable PMTU
585062306a36Sopenharmony_ci *                     discovery upon the specified address. Note that
585162306a36Sopenharmony_ci *                     if the address feild is empty then all addresses
585262306a36Sopenharmony_ci *                     on the association are effected. Not also that
585362306a36Sopenharmony_ci *                     SPP_PMTUD_ENABLE and SPP_PMTUD_DISABLE are mutually
585462306a36Sopenharmony_ci *                     exclusive. Enabling both will have undetermined
585562306a36Sopenharmony_ci *                     results.
585662306a36Sopenharmony_ci *
585762306a36Sopenharmony_ci *                     SPP_SACKDELAY_ENABLE - Setting this flag turns
585862306a36Sopenharmony_ci *                     on delayed sack. The time specified in spp_sackdelay
585962306a36Sopenharmony_ci *                     is used to specify the sack delay for this address. Note
586062306a36Sopenharmony_ci *                     that if spp_address is empty then all addresses will
586162306a36Sopenharmony_ci *                     enable delayed sack and take on the sack delay
586262306a36Sopenharmony_ci *                     value specified in spp_sackdelay.
586362306a36Sopenharmony_ci *                     SPP_SACKDELAY_DISABLE - Setting this flag turns
586462306a36Sopenharmony_ci *                     off delayed sack. If the spp_address field is blank then
586562306a36Sopenharmony_ci *                     delayed sack is disabled for the entire association. Note
586662306a36Sopenharmony_ci *                     also that this field is mutually exclusive to
586762306a36Sopenharmony_ci *                     SPP_SACKDELAY_ENABLE, setting both will have undefined
586862306a36Sopenharmony_ci *                     results.
586962306a36Sopenharmony_ci *
587062306a36Sopenharmony_ci *                     SPP_IPV6_FLOWLABEL:  Setting this flag enables the
587162306a36Sopenharmony_ci *                     setting of the IPV6 flow label value.  The value is
587262306a36Sopenharmony_ci *                     contained in the spp_ipv6_flowlabel field.
587362306a36Sopenharmony_ci *                     Upon retrieval, this flag will be set to indicate that
587462306a36Sopenharmony_ci *                     the spp_ipv6_flowlabel field has a valid value returned.
587562306a36Sopenharmony_ci *                     If a specific destination address is set (in the
587662306a36Sopenharmony_ci *                     spp_address field), then the value returned is that of
587762306a36Sopenharmony_ci *                     the address.  If just an association is specified (and
587862306a36Sopenharmony_ci *                     no address), then the association's default flow label
587962306a36Sopenharmony_ci *                     is returned.  If neither an association nor a destination
588062306a36Sopenharmony_ci *                     is specified, then the socket's default flow label is
588162306a36Sopenharmony_ci *                     returned.  For non-IPv6 sockets, this flag will be left
588262306a36Sopenharmony_ci *                     cleared.
588362306a36Sopenharmony_ci *
588462306a36Sopenharmony_ci *                     SPP_DSCP:  Setting this flag enables the setting of the
588562306a36Sopenharmony_ci *                     Differentiated Services Code Point (DSCP) value
588662306a36Sopenharmony_ci *                     associated with either the association or a specific
588762306a36Sopenharmony_ci *                     address.  The value is obtained in the spp_dscp field.
588862306a36Sopenharmony_ci *                     Upon retrieval, this flag will be set to indicate that
588962306a36Sopenharmony_ci *                     the spp_dscp field has a valid value returned.  If a
589062306a36Sopenharmony_ci *                     specific destination address is set when called (in the
589162306a36Sopenharmony_ci *                     spp_address field), then that specific destination
589262306a36Sopenharmony_ci *                     address's DSCP value is returned.  If just an association
589362306a36Sopenharmony_ci *                     is specified, then the association's default DSCP is
589462306a36Sopenharmony_ci *                     returned.  If neither an association nor a destination is
589562306a36Sopenharmony_ci *                     specified, then the socket's default DSCP is returned.
589662306a36Sopenharmony_ci *
589762306a36Sopenharmony_ci *   spp_ipv6_flowlabel
589862306a36Sopenharmony_ci *                   - This field is used in conjunction with the
589962306a36Sopenharmony_ci *                     SPP_IPV6_FLOWLABEL flag and contains the IPv6 flow label.
590062306a36Sopenharmony_ci *                     The 20 least significant bits are used for the flow
590162306a36Sopenharmony_ci *                     label.  This setting has precedence over any IPv6-layer
590262306a36Sopenharmony_ci *                     setting.
590362306a36Sopenharmony_ci *
590462306a36Sopenharmony_ci *   spp_dscp        - This field is used in conjunction with the SPP_DSCP flag
590562306a36Sopenharmony_ci *                     and contains the DSCP.  The 6 most significant bits are
590662306a36Sopenharmony_ci *                     used for the DSCP.  This setting has precedence over any
590762306a36Sopenharmony_ci *                     IPv4- or IPv6- layer setting.
590862306a36Sopenharmony_ci */
590962306a36Sopenharmony_cistatic int sctp_getsockopt_peer_addr_params(struct sock *sk, int len,
591062306a36Sopenharmony_ci					    char __user *optval, int __user *optlen)
591162306a36Sopenharmony_ci{
591262306a36Sopenharmony_ci	struct sctp_paddrparams  params;
591362306a36Sopenharmony_ci	struct sctp_transport   *trans = NULL;
591462306a36Sopenharmony_ci	struct sctp_association *asoc = NULL;
591562306a36Sopenharmony_ci	struct sctp_sock        *sp = sctp_sk(sk);
591662306a36Sopenharmony_ci
591762306a36Sopenharmony_ci	if (len >= sizeof(params))
591862306a36Sopenharmony_ci		len = sizeof(params);
591962306a36Sopenharmony_ci	else if (len >= ALIGN(offsetof(struct sctp_paddrparams,
592062306a36Sopenharmony_ci				       spp_ipv6_flowlabel), 4))
592162306a36Sopenharmony_ci		len = ALIGN(offsetof(struct sctp_paddrparams,
592262306a36Sopenharmony_ci				     spp_ipv6_flowlabel), 4);
592362306a36Sopenharmony_ci	else
592462306a36Sopenharmony_ci		return -EINVAL;
592562306a36Sopenharmony_ci
592662306a36Sopenharmony_ci	if (copy_from_user(&params, optval, len))
592762306a36Sopenharmony_ci		return -EFAULT;
592862306a36Sopenharmony_ci
592962306a36Sopenharmony_ci	/* If an address other than INADDR_ANY is specified, and
593062306a36Sopenharmony_ci	 * no transport is found, then the request is invalid.
593162306a36Sopenharmony_ci	 */
593262306a36Sopenharmony_ci	if (!sctp_is_any(sk, (union sctp_addr *)&params.spp_address)) {
593362306a36Sopenharmony_ci		trans = sctp_addr_id2transport(sk, &params.spp_address,
593462306a36Sopenharmony_ci					       params.spp_assoc_id);
593562306a36Sopenharmony_ci		if (!trans) {
593662306a36Sopenharmony_ci			pr_debug("%s: failed no transport\n", __func__);
593762306a36Sopenharmony_ci			return -EINVAL;
593862306a36Sopenharmony_ci		}
593962306a36Sopenharmony_ci	}
594062306a36Sopenharmony_ci
594162306a36Sopenharmony_ci	/* Get association, if assoc_id != SCTP_FUTURE_ASSOC and the
594262306a36Sopenharmony_ci	 * socket is a one to many style socket, and an association
594362306a36Sopenharmony_ci	 * was not found, then the id was invalid.
594462306a36Sopenharmony_ci	 */
594562306a36Sopenharmony_ci	asoc = sctp_id2assoc(sk, params.spp_assoc_id);
594662306a36Sopenharmony_ci	if (!asoc && params.spp_assoc_id != SCTP_FUTURE_ASSOC &&
594762306a36Sopenharmony_ci	    sctp_style(sk, UDP)) {
594862306a36Sopenharmony_ci		pr_debug("%s: failed no association\n", __func__);
594962306a36Sopenharmony_ci		return -EINVAL;
595062306a36Sopenharmony_ci	}
595162306a36Sopenharmony_ci
595262306a36Sopenharmony_ci	if (trans) {
595362306a36Sopenharmony_ci		/* Fetch transport values. */
595462306a36Sopenharmony_ci		params.spp_hbinterval = jiffies_to_msecs(trans->hbinterval);
595562306a36Sopenharmony_ci		params.spp_pathmtu    = trans->pathmtu;
595662306a36Sopenharmony_ci		params.spp_pathmaxrxt = trans->pathmaxrxt;
595762306a36Sopenharmony_ci		params.spp_sackdelay  = jiffies_to_msecs(trans->sackdelay);
595862306a36Sopenharmony_ci
595962306a36Sopenharmony_ci		/*draft-11 doesn't say what to return in spp_flags*/
596062306a36Sopenharmony_ci		params.spp_flags      = trans->param_flags;
596162306a36Sopenharmony_ci		if (trans->flowlabel & SCTP_FLOWLABEL_SET_MASK) {
596262306a36Sopenharmony_ci			params.spp_ipv6_flowlabel = trans->flowlabel &
596362306a36Sopenharmony_ci						    SCTP_FLOWLABEL_VAL_MASK;
596462306a36Sopenharmony_ci			params.spp_flags |= SPP_IPV6_FLOWLABEL;
596562306a36Sopenharmony_ci		}
596662306a36Sopenharmony_ci		if (trans->dscp & SCTP_DSCP_SET_MASK) {
596762306a36Sopenharmony_ci			params.spp_dscp	= trans->dscp & SCTP_DSCP_VAL_MASK;
596862306a36Sopenharmony_ci			params.spp_flags |= SPP_DSCP;
596962306a36Sopenharmony_ci		}
597062306a36Sopenharmony_ci	} else if (asoc) {
597162306a36Sopenharmony_ci		/* Fetch association values. */
597262306a36Sopenharmony_ci		params.spp_hbinterval = jiffies_to_msecs(asoc->hbinterval);
597362306a36Sopenharmony_ci		params.spp_pathmtu    = asoc->pathmtu;
597462306a36Sopenharmony_ci		params.spp_pathmaxrxt = asoc->pathmaxrxt;
597562306a36Sopenharmony_ci		params.spp_sackdelay  = jiffies_to_msecs(asoc->sackdelay);
597662306a36Sopenharmony_ci
597762306a36Sopenharmony_ci		/*draft-11 doesn't say what to return in spp_flags*/
597862306a36Sopenharmony_ci		params.spp_flags      = asoc->param_flags;
597962306a36Sopenharmony_ci		if (asoc->flowlabel & SCTP_FLOWLABEL_SET_MASK) {
598062306a36Sopenharmony_ci			params.spp_ipv6_flowlabel = asoc->flowlabel &
598162306a36Sopenharmony_ci						    SCTP_FLOWLABEL_VAL_MASK;
598262306a36Sopenharmony_ci			params.spp_flags |= SPP_IPV6_FLOWLABEL;
598362306a36Sopenharmony_ci		}
598462306a36Sopenharmony_ci		if (asoc->dscp & SCTP_DSCP_SET_MASK) {
598562306a36Sopenharmony_ci			params.spp_dscp	= asoc->dscp & SCTP_DSCP_VAL_MASK;
598662306a36Sopenharmony_ci			params.spp_flags |= SPP_DSCP;
598762306a36Sopenharmony_ci		}
598862306a36Sopenharmony_ci	} else {
598962306a36Sopenharmony_ci		/* Fetch socket values. */
599062306a36Sopenharmony_ci		params.spp_hbinterval = sp->hbinterval;
599162306a36Sopenharmony_ci		params.spp_pathmtu    = sp->pathmtu;
599262306a36Sopenharmony_ci		params.spp_sackdelay  = sp->sackdelay;
599362306a36Sopenharmony_ci		params.spp_pathmaxrxt = sp->pathmaxrxt;
599462306a36Sopenharmony_ci
599562306a36Sopenharmony_ci		/*draft-11 doesn't say what to return in spp_flags*/
599662306a36Sopenharmony_ci		params.spp_flags      = sp->param_flags;
599762306a36Sopenharmony_ci		if (sp->flowlabel & SCTP_FLOWLABEL_SET_MASK) {
599862306a36Sopenharmony_ci			params.spp_ipv6_flowlabel = sp->flowlabel &
599962306a36Sopenharmony_ci						    SCTP_FLOWLABEL_VAL_MASK;
600062306a36Sopenharmony_ci			params.spp_flags |= SPP_IPV6_FLOWLABEL;
600162306a36Sopenharmony_ci		}
600262306a36Sopenharmony_ci		if (sp->dscp & SCTP_DSCP_SET_MASK) {
600362306a36Sopenharmony_ci			params.spp_dscp	= sp->dscp & SCTP_DSCP_VAL_MASK;
600462306a36Sopenharmony_ci			params.spp_flags |= SPP_DSCP;
600562306a36Sopenharmony_ci		}
600662306a36Sopenharmony_ci	}
600762306a36Sopenharmony_ci
600862306a36Sopenharmony_ci	if (copy_to_user(optval, &params, len))
600962306a36Sopenharmony_ci		return -EFAULT;
601062306a36Sopenharmony_ci
601162306a36Sopenharmony_ci	if (put_user(len, optlen))
601262306a36Sopenharmony_ci		return -EFAULT;
601362306a36Sopenharmony_ci
601462306a36Sopenharmony_ci	return 0;
601562306a36Sopenharmony_ci}
601662306a36Sopenharmony_ci
601762306a36Sopenharmony_ci/*
601862306a36Sopenharmony_ci * 7.1.23.  Get or set delayed ack timer (SCTP_DELAYED_SACK)
601962306a36Sopenharmony_ci *
602062306a36Sopenharmony_ci * This option will effect the way delayed acks are performed.  This
602162306a36Sopenharmony_ci * option allows you to get or set the delayed ack time, in
602262306a36Sopenharmony_ci * milliseconds.  It also allows changing the delayed ack frequency.
602362306a36Sopenharmony_ci * Changing the frequency to 1 disables the delayed sack algorithm.  If
602462306a36Sopenharmony_ci * the assoc_id is 0, then this sets or gets the endpoints default
602562306a36Sopenharmony_ci * values.  If the assoc_id field is non-zero, then the set or get
602662306a36Sopenharmony_ci * effects the specified association for the one to many model (the
602762306a36Sopenharmony_ci * assoc_id field is ignored by the one to one model).  Note that if
602862306a36Sopenharmony_ci * sack_delay or sack_freq are 0 when setting this option, then the
602962306a36Sopenharmony_ci * current values will remain unchanged.
603062306a36Sopenharmony_ci *
603162306a36Sopenharmony_ci * struct sctp_sack_info {
603262306a36Sopenharmony_ci *     sctp_assoc_t            sack_assoc_id;
603362306a36Sopenharmony_ci *     uint32_t                sack_delay;
603462306a36Sopenharmony_ci *     uint32_t                sack_freq;
603562306a36Sopenharmony_ci * };
603662306a36Sopenharmony_ci *
603762306a36Sopenharmony_ci * sack_assoc_id -  This parameter, indicates which association the user
603862306a36Sopenharmony_ci *    is performing an action upon.  Note that if this field's value is
603962306a36Sopenharmony_ci *    zero then the endpoints default value is changed (effecting future
604062306a36Sopenharmony_ci *    associations only).
604162306a36Sopenharmony_ci *
604262306a36Sopenharmony_ci * sack_delay -  This parameter contains the number of milliseconds that
604362306a36Sopenharmony_ci *    the user is requesting the delayed ACK timer be set to.  Note that
604462306a36Sopenharmony_ci *    this value is defined in the standard to be between 200 and 500
604562306a36Sopenharmony_ci *    milliseconds.
604662306a36Sopenharmony_ci *
604762306a36Sopenharmony_ci * sack_freq -  This parameter contains the number of packets that must
604862306a36Sopenharmony_ci *    be received before a sack is sent without waiting for the delay
604962306a36Sopenharmony_ci *    timer to expire.  The default value for this is 2, setting this
605062306a36Sopenharmony_ci *    value to 1 will disable the delayed sack algorithm.
605162306a36Sopenharmony_ci */
605262306a36Sopenharmony_cistatic int sctp_getsockopt_delayed_ack(struct sock *sk, int len,
605362306a36Sopenharmony_ci					    char __user *optval,
605462306a36Sopenharmony_ci					    int __user *optlen)
605562306a36Sopenharmony_ci{
605662306a36Sopenharmony_ci	struct sctp_sack_info    params;
605762306a36Sopenharmony_ci	struct sctp_association *asoc = NULL;
605862306a36Sopenharmony_ci	struct sctp_sock        *sp = sctp_sk(sk);
605962306a36Sopenharmony_ci
606062306a36Sopenharmony_ci	if (len >= sizeof(struct sctp_sack_info)) {
606162306a36Sopenharmony_ci		len = sizeof(struct sctp_sack_info);
606262306a36Sopenharmony_ci
606362306a36Sopenharmony_ci		if (copy_from_user(&params, optval, len))
606462306a36Sopenharmony_ci			return -EFAULT;
606562306a36Sopenharmony_ci	} else if (len == sizeof(struct sctp_assoc_value)) {
606662306a36Sopenharmony_ci		pr_warn_ratelimited(DEPRECATED
606762306a36Sopenharmony_ci				    "%s (pid %d) "
606862306a36Sopenharmony_ci				    "Use of struct sctp_assoc_value in delayed_ack socket option.\n"
606962306a36Sopenharmony_ci				    "Use struct sctp_sack_info instead\n",
607062306a36Sopenharmony_ci				    current->comm, task_pid_nr(current));
607162306a36Sopenharmony_ci		if (copy_from_user(&params, optval, len))
607262306a36Sopenharmony_ci			return -EFAULT;
607362306a36Sopenharmony_ci	} else
607462306a36Sopenharmony_ci		return -EINVAL;
607562306a36Sopenharmony_ci
607662306a36Sopenharmony_ci	/* Get association, if sack_assoc_id != SCTP_FUTURE_ASSOC and the
607762306a36Sopenharmony_ci	 * socket is a one to many style socket, and an association
607862306a36Sopenharmony_ci	 * was not found, then the id was invalid.
607962306a36Sopenharmony_ci	 */
608062306a36Sopenharmony_ci	asoc = sctp_id2assoc(sk, params.sack_assoc_id);
608162306a36Sopenharmony_ci	if (!asoc && params.sack_assoc_id != SCTP_FUTURE_ASSOC &&
608262306a36Sopenharmony_ci	    sctp_style(sk, UDP))
608362306a36Sopenharmony_ci		return -EINVAL;
608462306a36Sopenharmony_ci
608562306a36Sopenharmony_ci	if (asoc) {
608662306a36Sopenharmony_ci		/* Fetch association values. */
608762306a36Sopenharmony_ci		if (asoc->param_flags & SPP_SACKDELAY_ENABLE) {
608862306a36Sopenharmony_ci			params.sack_delay = jiffies_to_msecs(asoc->sackdelay);
608962306a36Sopenharmony_ci			params.sack_freq = asoc->sackfreq;
609062306a36Sopenharmony_ci
609162306a36Sopenharmony_ci		} else {
609262306a36Sopenharmony_ci			params.sack_delay = 0;
609362306a36Sopenharmony_ci			params.sack_freq = 1;
609462306a36Sopenharmony_ci		}
609562306a36Sopenharmony_ci	} else {
609662306a36Sopenharmony_ci		/* Fetch socket values. */
609762306a36Sopenharmony_ci		if (sp->param_flags & SPP_SACKDELAY_ENABLE) {
609862306a36Sopenharmony_ci			params.sack_delay  = sp->sackdelay;
609962306a36Sopenharmony_ci			params.sack_freq = sp->sackfreq;
610062306a36Sopenharmony_ci		} else {
610162306a36Sopenharmony_ci			params.sack_delay  = 0;
610262306a36Sopenharmony_ci			params.sack_freq = 1;
610362306a36Sopenharmony_ci		}
610462306a36Sopenharmony_ci	}
610562306a36Sopenharmony_ci
610662306a36Sopenharmony_ci	if (copy_to_user(optval, &params, len))
610762306a36Sopenharmony_ci		return -EFAULT;
610862306a36Sopenharmony_ci
610962306a36Sopenharmony_ci	if (put_user(len, optlen))
611062306a36Sopenharmony_ci		return -EFAULT;
611162306a36Sopenharmony_ci
611262306a36Sopenharmony_ci	return 0;
611362306a36Sopenharmony_ci}
611462306a36Sopenharmony_ci
611562306a36Sopenharmony_ci/* 7.1.3 Initialization Parameters (SCTP_INITMSG)
611662306a36Sopenharmony_ci *
611762306a36Sopenharmony_ci * Applications can specify protocol parameters for the default association
611862306a36Sopenharmony_ci * initialization.  The option name argument to setsockopt() and getsockopt()
611962306a36Sopenharmony_ci * is SCTP_INITMSG.
612062306a36Sopenharmony_ci *
612162306a36Sopenharmony_ci * Setting initialization parameters is effective only on an unconnected
612262306a36Sopenharmony_ci * socket (for UDP-style sockets only future associations are effected
612362306a36Sopenharmony_ci * by the change).  With TCP-style sockets, this option is inherited by
612462306a36Sopenharmony_ci * sockets derived from a listener socket.
612562306a36Sopenharmony_ci */
612662306a36Sopenharmony_cistatic int sctp_getsockopt_initmsg(struct sock *sk, int len, char __user *optval, int __user *optlen)
612762306a36Sopenharmony_ci{
612862306a36Sopenharmony_ci	if (len < sizeof(struct sctp_initmsg))
612962306a36Sopenharmony_ci		return -EINVAL;
613062306a36Sopenharmony_ci	len = sizeof(struct sctp_initmsg);
613162306a36Sopenharmony_ci	if (put_user(len, optlen))
613262306a36Sopenharmony_ci		return -EFAULT;
613362306a36Sopenharmony_ci	if (copy_to_user(optval, &sctp_sk(sk)->initmsg, len))
613462306a36Sopenharmony_ci		return -EFAULT;
613562306a36Sopenharmony_ci	return 0;
613662306a36Sopenharmony_ci}
613762306a36Sopenharmony_ci
613862306a36Sopenharmony_ci
613962306a36Sopenharmony_cistatic int sctp_getsockopt_peer_addrs(struct sock *sk, int len,
614062306a36Sopenharmony_ci				      char __user *optval, int __user *optlen)
614162306a36Sopenharmony_ci{
614262306a36Sopenharmony_ci	struct sctp_association *asoc;
614362306a36Sopenharmony_ci	int cnt = 0;
614462306a36Sopenharmony_ci	struct sctp_getaddrs getaddrs;
614562306a36Sopenharmony_ci	struct sctp_transport *from;
614662306a36Sopenharmony_ci	void __user *to;
614762306a36Sopenharmony_ci	union sctp_addr temp;
614862306a36Sopenharmony_ci	struct sctp_sock *sp = sctp_sk(sk);
614962306a36Sopenharmony_ci	int addrlen;
615062306a36Sopenharmony_ci	size_t space_left;
615162306a36Sopenharmony_ci	int bytes_copied;
615262306a36Sopenharmony_ci
615362306a36Sopenharmony_ci	if (len < sizeof(struct sctp_getaddrs))
615462306a36Sopenharmony_ci		return -EINVAL;
615562306a36Sopenharmony_ci
615662306a36Sopenharmony_ci	if (copy_from_user(&getaddrs, optval, sizeof(struct sctp_getaddrs)))
615762306a36Sopenharmony_ci		return -EFAULT;
615862306a36Sopenharmony_ci
615962306a36Sopenharmony_ci	/* For UDP-style sockets, id specifies the association to query.  */
616062306a36Sopenharmony_ci	asoc = sctp_id2assoc(sk, getaddrs.assoc_id);
616162306a36Sopenharmony_ci	if (!asoc)
616262306a36Sopenharmony_ci		return -EINVAL;
616362306a36Sopenharmony_ci
616462306a36Sopenharmony_ci	to = optval + offsetof(struct sctp_getaddrs, addrs);
616562306a36Sopenharmony_ci	space_left = len - offsetof(struct sctp_getaddrs, addrs);
616662306a36Sopenharmony_ci
616762306a36Sopenharmony_ci	list_for_each_entry(from, &asoc->peer.transport_addr_list,
616862306a36Sopenharmony_ci				transports) {
616962306a36Sopenharmony_ci		memcpy(&temp, &from->ipaddr, sizeof(temp));
617062306a36Sopenharmony_ci		addrlen = sctp_get_pf_specific(sk->sk_family)
617162306a36Sopenharmony_ci			      ->addr_to_user(sp, &temp);
617262306a36Sopenharmony_ci		if (space_left < addrlen)
617362306a36Sopenharmony_ci			return -ENOMEM;
617462306a36Sopenharmony_ci		if (copy_to_user(to, &temp, addrlen))
617562306a36Sopenharmony_ci			return -EFAULT;
617662306a36Sopenharmony_ci		to += addrlen;
617762306a36Sopenharmony_ci		cnt++;
617862306a36Sopenharmony_ci		space_left -= addrlen;
617962306a36Sopenharmony_ci	}
618062306a36Sopenharmony_ci
618162306a36Sopenharmony_ci	if (put_user(cnt, &((struct sctp_getaddrs __user *)optval)->addr_num))
618262306a36Sopenharmony_ci		return -EFAULT;
618362306a36Sopenharmony_ci	bytes_copied = ((char __user *)to) - optval;
618462306a36Sopenharmony_ci	if (put_user(bytes_copied, optlen))
618562306a36Sopenharmony_ci		return -EFAULT;
618662306a36Sopenharmony_ci
618762306a36Sopenharmony_ci	return 0;
618862306a36Sopenharmony_ci}
618962306a36Sopenharmony_ci
619062306a36Sopenharmony_cistatic int sctp_copy_laddrs(struct sock *sk, __u16 port, void *to,
619162306a36Sopenharmony_ci			    size_t space_left, int *bytes_copied)
619262306a36Sopenharmony_ci{
619362306a36Sopenharmony_ci	struct sctp_sockaddr_entry *addr;
619462306a36Sopenharmony_ci	union sctp_addr temp;
619562306a36Sopenharmony_ci	int cnt = 0;
619662306a36Sopenharmony_ci	int addrlen;
619762306a36Sopenharmony_ci	struct net *net = sock_net(sk);
619862306a36Sopenharmony_ci
619962306a36Sopenharmony_ci	rcu_read_lock();
620062306a36Sopenharmony_ci	list_for_each_entry_rcu(addr, &net->sctp.local_addr_list, list) {
620162306a36Sopenharmony_ci		if (!addr->valid)
620262306a36Sopenharmony_ci			continue;
620362306a36Sopenharmony_ci
620462306a36Sopenharmony_ci		if ((PF_INET == sk->sk_family) &&
620562306a36Sopenharmony_ci		    (AF_INET6 == addr->a.sa.sa_family))
620662306a36Sopenharmony_ci			continue;
620762306a36Sopenharmony_ci		if ((PF_INET6 == sk->sk_family) &&
620862306a36Sopenharmony_ci		    inet_v6_ipv6only(sk) &&
620962306a36Sopenharmony_ci		    (AF_INET == addr->a.sa.sa_family))
621062306a36Sopenharmony_ci			continue;
621162306a36Sopenharmony_ci		memcpy(&temp, &addr->a, sizeof(temp));
621262306a36Sopenharmony_ci		if (!temp.v4.sin_port)
621362306a36Sopenharmony_ci			temp.v4.sin_port = htons(port);
621462306a36Sopenharmony_ci
621562306a36Sopenharmony_ci		addrlen = sctp_get_pf_specific(sk->sk_family)
621662306a36Sopenharmony_ci			      ->addr_to_user(sctp_sk(sk), &temp);
621762306a36Sopenharmony_ci
621862306a36Sopenharmony_ci		if (space_left < addrlen) {
621962306a36Sopenharmony_ci			cnt =  -ENOMEM;
622062306a36Sopenharmony_ci			break;
622162306a36Sopenharmony_ci		}
622262306a36Sopenharmony_ci		memcpy(to, &temp, addrlen);
622362306a36Sopenharmony_ci
622462306a36Sopenharmony_ci		to += addrlen;
622562306a36Sopenharmony_ci		cnt++;
622662306a36Sopenharmony_ci		space_left -= addrlen;
622762306a36Sopenharmony_ci		*bytes_copied += addrlen;
622862306a36Sopenharmony_ci	}
622962306a36Sopenharmony_ci	rcu_read_unlock();
623062306a36Sopenharmony_ci
623162306a36Sopenharmony_ci	return cnt;
623262306a36Sopenharmony_ci}
623362306a36Sopenharmony_ci
623462306a36Sopenharmony_ci
623562306a36Sopenharmony_cistatic int sctp_getsockopt_local_addrs(struct sock *sk, int len,
623662306a36Sopenharmony_ci				       char __user *optval, int __user *optlen)
623762306a36Sopenharmony_ci{
623862306a36Sopenharmony_ci	struct sctp_bind_addr *bp;
623962306a36Sopenharmony_ci	struct sctp_association *asoc;
624062306a36Sopenharmony_ci	int cnt = 0;
624162306a36Sopenharmony_ci	struct sctp_getaddrs getaddrs;
624262306a36Sopenharmony_ci	struct sctp_sockaddr_entry *addr;
624362306a36Sopenharmony_ci	void __user *to;
624462306a36Sopenharmony_ci	union sctp_addr temp;
624562306a36Sopenharmony_ci	struct sctp_sock *sp = sctp_sk(sk);
624662306a36Sopenharmony_ci	int addrlen;
624762306a36Sopenharmony_ci	int err = 0;
624862306a36Sopenharmony_ci	size_t space_left;
624962306a36Sopenharmony_ci	int bytes_copied = 0;
625062306a36Sopenharmony_ci	void *addrs;
625162306a36Sopenharmony_ci	void *buf;
625262306a36Sopenharmony_ci
625362306a36Sopenharmony_ci	if (len < sizeof(struct sctp_getaddrs))
625462306a36Sopenharmony_ci		return -EINVAL;
625562306a36Sopenharmony_ci
625662306a36Sopenharmony_ci	if (copy_from_user(&getaddrs, optval, sizeof(struct sctp_getaddrs)))
625762306a36Sopenharmony_ci		return -EFAULT;
625862306a36Sopenharmony_ci
625962306a36Sopenharmony_ci	/*
626062306a36Sopenharmony_ci	 *  For UDP-style sockets, id specifies the association to query.
626162306a36Sopenharmony_ci	 *  If the id field is set to the value '0' then the locally bound
626262306a36Sopenharmony_ci	 *  addresses are returned without regard to any particular
626362306a36Sopenharmony_ci	 *  association.
626462306a36Sopenharmony_ci	 */
626562306a36Sopenharmony_ci	if (0 == getaddrs.assoc_id) {
626662306a36Sopenharmony_ci		bp = &sctp_sk(sk)->ep->base.bind_addr;
626762306a36Sopenharmony_ci	} else {
626862306a36Sopenharmony_ci		asoc = sctp_id2assoc(sk, getaddrs.assoc_id);
626962306a36Sopenharmony_ci		if (!asoc)
627062306a36Sopenharmony_ci			return -EINVAL;
627162306a36Sopenharmony_ci		bp = &asoc->base.bind_addr;
627262306a36Sopenharmony_ci	}
627362306a36Sopenharmony_ci
627462306a36Sopenharmony_ci	to = optval + offsetof(struct sctp_getaddrs, addrs);
627562306a36Sopenharmony_ci	space_left = len - offsetof(struct sctp_getaddrs, addrs);
627662306a36Sopenharmony_ci
627762306a36Sopenharmony_ci	addrs = kmalloc(space_left, GFP_USER | __GFP_NOWARN);
627862306a36Sopenharmony_ci	if (!addrs)
627962306a36Sopenharmony_ci		return -ENOMEM;
628062306a36Sopenharmony_ci
628162306a36Sopenharmony_ci	/* If the endpoint is bound to 0.0.0.0 or ::0, get the valid
628262306a36Sopenharmony_ci	 * addresses from the global local address list.
628362306a36Sopenharmony_ci	 */
628462306a36Sopenharmony_ci	if (sctp_list_single_entry(&bp->address_list)) {
628562306a36Sopenharmony_ci		addr = list_entry(bp->address_list.next,
628662306a36Sopenharmony_ci				  struct sctp_sockaddr_entry, list);
628762306a36Sopenharmony_ci		if (sctp_is_any(sk, &addr->a)) {
628862306a36Sopenharmony_ci			cnt = sctp_copy_laddrs(sk, bp->port, addrs,
628962306a36Sopenharmony_ci						space_left, &bytes_copied);
629062306a36Sopenharmony_ci			if (cnt < 0) {
629162306a36Sopenharmony_ci				err = cnt;
629262306a36Sopenharmony_ci				goto out;
629362306a36Sopenharmony_ci			}
629462306a36Sopenharmony_ci			goto copy_getaddrs;
629562306a36Sopenharmony_ci		}
629662306a36Sopenharmony_ci	}
629762306a36Sopenharmony_ci
629862306a36Sopenharmony_ci	buf = addrs;
629962306a36Sopenharmony_ci	/* Protection on the bound address list is not needed since
630062306a36Sopenharmony_ci	 * in the socket option context we hold a socket lock and
630162306a36Sopenharmony_ci	 * thus the bound address list can't change.
630262306a36Sopenharmony_ci	 */
630362306a36Sopenharmony_ci	list_for_each_entry(addr, &bp->address_list, list) {
630462306a36Sopenharmony_ci		memcpy(&temp, &addr->a, sizeof(temp));
630562306a36Sopenharmony_ci		addrlen = sctp_get_pf_specific(sk->sk_family)
630662306a36Sopenharmony_ci			      ->addr_to_user(sp, &temp);
630762306a36Sopenharmony_ci		if (space_left < addrlen) {
630862306a36Sopenharmony_ci			err =  -ENOMEM; /*fixme: right error?*/
630962306a36Sopenharmony_ci			goto out;
631062306a36Sopenharmony_ci		}
631162306a36Sopenharmony_ci		memcpy(buf, &temp, addrlen);
631262306a36Sopenharmony_ci		buf += addrlen;
631362306a36Sopenharmony_ci		bytes_copied += addrlen;
631462306a36Sopenharmony_ci		cnt++;
631562306a36Sopenharmony_ci		space_left -= addrlen;
631662306a36Sopenharmony_ci	}
631762306a36Sopenharmony_ci
631862306a36Sopenharmony_cicopy_getaddrs:
631962306a36Sopenharmony_ci	if (copy_to_user(to, addrs, bytes_copied)) {
632062306a36Sopenharmony_ci		err = -EFAULT;
632162306a36Sopenharmony_ci		goto out;
632262306a36Sopenharmony_ci	}
632362306a36Sopenharmony_ci	if (put_user(cnt, &((struct sctp_getaddrs __user *)optval)->addr_num)) {
632462306a36Sopenharmony_ci		err = -EFAULT;
632562306a36Sopenharmony_ci		goto out;
632662306a36Sopenharmony_ci	}
632762306a36Sopenharmony_ci	/* XXX: We should have accounted for sizeof(struct sctp_getaddrs) too,
632862306a36Sopenharmony_ci	 * but we can't change it anymore.
632962306a36Sopenharmony_ci	 */
633062306a36Sopenharmony_ci	if (put_user(bytes_copied, optlen))
633162306a36Sopenharmony_ci		err = -EFAULT;
633262306a36Sopenharmony_ciout:
633362306a36Sopenharmony_ci	kfree(addrs);
633462306a36Sopenharmony_ci	return err;
633562306a36Sopenharmony_ci}
633662306a36Sopenharmony_ci
633762306a36Sopenharmony_ci/* 7.1.10 Set Primary Address (SCTP_PRIMARY_ADDR)
633862306a36Sopenharmony_ci *
633962306a36Sopenharmony_ci * Requests that the local SCTP stack use the enclosed peer address as
634062306a36Sopenharmony_ci * the association primary.  The enclosed address must be one of the
634162306a36Sopenharmony_ci * association peer's addresses.
634262306a36Sopenharmony_ci */
634362306a36Sopenharmony_cistatic int sctp_getsockopt_primary_addr(struct sock *sk, int len,
634462306a36Sopenharmony_ci					char __user *optval, int __user *optlen)
634562306a36Sopenharmony_ci{
634662306a36Sopenharmony_ci	struct sctp_prim prim;
634762306a36Sopenharmony_ci	struct sctp_association *asoc;
634862306a36Sopenharmony_ci	struct sctp_sock *sp = sctp_sk(sk);
634962306a36Sopenharmony_ci
635062306a36Sopenharmony_ci	if (len < sizeof(struct sctp_prim))
635162306a36Sopenharmony_ci		return -EINVAL;
635262306a36Sopenharmony_ci
635362306a36Sopenharmony_ci	len = sizeof(struct sctp_prim);
635462306a36Sopenharmony_ci
635562306a36Sopenharmony_ci	if (copy_from_user(&prim, optval, len))
635662306a36Sopenharmony_ci		return -EFAULT;
635762306a36Sopenharmony_ci
635862306a36Sopenharmony_ci	asoc = sctp_id2assoc(sk, prim.ssp_assoc_id);
635962306a36Sopenharmony_ci	if (!asoc)
636062306a36Sopenharmony_ci		return -EINVAL;
636162306a36Sopenharmony_ci
636262306a36Sopenharmony_ci	if (!asoc->peer.primary_path)
636362306a36Sopenharmony_ci		return -ENOTCONN;
636462306a36Sopenharmony_ci
636562306a36Sopenharmony_ci	memcpy(&prim.ssp_addr, &asoc->peer.primary_path->ipaddr,
636662306a36Sopenharmony_ci		asoc->peer.primary_path->af_specific->sockaddr_len);
636762306a36Sopenharmony_ci
636862306a36Sopenharmony_ci	sctp_get_pf_specific(sk->sk_family)->addr_to_user(sp,
636962306a36Sopenharmony_ci			(union sctp_addr *)&prim.ssp_addr);
637062306a36Sopenharmony_ci
637162306a36Sopenharmony_ci	if (put_user(len, optlen))
637262306a36Sopenharmony_ci		return -EFAULT;
637362306a36Sopenharmony_ci	if (copy_to_user(optval, &prim, len))
637462306a36Sopenharmony_ci		return -EFAULT;
637562306a36Sopenharmony_ci
637662306a36Sopenharmony_ci	return 0;
637762306a36Sopenharmony_ci}
637862306a36Sopenharmony_ci
637962306a36Sopenharmony_ci/*
638062306a36Sopenharmony_ci * 7.1.11  Set Adaptation Layer Indicator (SCTP_ADAPTATION_LAYER)
638162306a36Sopenharmony_ci *
638262306a36Sopenharmony_ci * Requests that the local endpoint set the specified Adaptation Layer
638362306a36Sopenharmony_ci * Indication parameter for all future INIT and INIT-ACK exchanges.
638462306a36Sopenharmony_ci */
638562306a36Sopenharmony_cistatic int sctp_getsockopt_adaptation_layer(struct sock *sk, int len,
638662306a36Sopenharmony_ci				  char __user *optval, int __user *optlen)
638762306a36Sopenharmony_ci{
638862306a36Sopenharmony_ci	struct sctp_setadaptation adaptation;
638962306a36Sopenharmony_ci
639062306a36Sopenharmony_ci	if (len < sizeof(struct sctp_setadaptation))
639162306a36Sopenharmony_ci		return -EINVAL;
639262306a36Sopenharmony_ci
639362306a36Sopenharmony_ci	len = sizeof(struct sctp_setadaptation);
639462306a36Sopenharmony_ci
639562306a36Sopenharmony_ci	adaptation.ssb_adaptation_ind = sctp_sk(sk)->adaptation_ind;
639662306a36Sopenharmony_ci
639762306a36Sopenharmony_ci	if (put_user(len, optlen))
639862306a36Sopenharmony_ci		return -EFAULT;
639962306a36Sopenharmony_ci	if (copy_to_user(optval, &adaptation, len))
640062306a36Sopenharmony_ci		return -EFAULT;
640162306a36Sopenharmony_ci
640262306a36Sopenharmony_ci	return 0;
640362306a36Sopenharmony_ci}
640462306a36Sopenharmony_ci
640562306a36Sopenharmony_ci/*
640662306a36Sopenharmony_ci *
640762306a36Sopenharmony_ci * 7.1.14 Set default send parameters (SCTP_DEFAULT_SEND_PARAM)
640862306a36Sopenharmony_ci *
640962306a36Sopenharmony_ci *   Applications that wish to use the sendto() system call may wish to
641062306a36Sopenharmony_ci *   specify a default set of parameters that would normally be supplied
641162306a36Sopenharmony_ci *   through the inclusion of ancillary data.  This socket option allows
641262306a36Sopenharmony_ci *   such an application to set the default sctp_sndrcvinfo structure.
641362306a36Sopenharmony_ci
641462306a36Sopenharmony_ci
641562306a36Sopenharmony_ci *   The application that wishes to use this socket option simply passes
641662306a36Sopenharmony_ci *   in to this call the sctp_sndrcvinfo structure defined in Section
641762306a36Sopenharmony_ci *   5.2.2) The input parameters accepted by this call include
641862306a36Sopenharmony_ci *   sinfo_stream, sinfo_flags, sinfo_ppid, sinfo_context,
641962306a36Sopenharmony_ci *   sinfo_timetolive.  The user must provide the sinfo_assoc_id field in
642062306a36Sopenharmony_ci *   to this call if the caller is using the UDP model.
642162306a36Sopenharmony_ci *
642262306a36Sopenharmony_ci *   For getsockopt, it get the default sctp_sndrcvinfo structure.
642362306a36Sopenharmony_ci */
642462306a36Sopenharmony_cistatic int sctp_getsockopt_default_send_param(struct sock *sk,
642562306a36Sopenharmony_ci					int len, char __user *optval,
642662306a36Sopenharmony_ci					int __user *optlen)
642762306a36Sopenharmony_ci{
642862306a36Sopenharmony_ci	struct sctp_sock *sp = sctp_sk(sk);
642962306a36Sopenharmony_ci	struct sctp_association *asoc;
643062306a36Sopenharmony_ci	struct sctp_sndrcvinfo info;
643162306a36Sopenharmony_ci
643262306a36Sopenharmony_ci	if (len < sizeof(info))
643362306a36Sopenharmony_ci		return -EINVAL;
643462306a36Sopenharmony_ci
643562306a36Sopenharmony_ci	len = sizeof(info);
643662306a36Sopenharmony_ci
643762306a36Sopenharmony_ci	if (copy_from_user(&info, optval, len))
643862306a36Sopenharmony_ci		return -EFAULT;
643962306a36Sopenharmony_ci
644062306a36Sopenharmony_ci	asoc = sctp_id2assoc(sk, info.sinfo_assoc_id);
644162306a36Sopenharmony_ci	if (!asoc && info.sinfo_assoc_id != SCTP_FUTURE_ASSOC &&
644262306a36Sopenharmony_ci	    sctp_style(sk, UDP))
644362306a36Sopenharmony_ci		return -EINVAL;
644462306a36Sopenharmony_ci
644562306a36Sopenharmony_ci	if (asoc) {
644662306a36Sopenharmony_ci		info.sinfo_stream = asoc->default_stream;
644762306a36Sopenharmony_ci		info.sinfo_flags = asoc->default_flags;
644862306a36Sopenharmony_ci		info.sinfo_ppid = asoc->default_ppid;
644962306a36Sopenharmony_ci		info.sinfo_context = asoc->default_context;
645062306a36Sopenharmony_ci		info.sinfo_timetolive = asoc->default_timetolive;
645162306a36Sopenharmony_ci	} else {
645262306a36Sopenharmony_ci		info.sinfo_stream = sp->default_stream;
645362306a36Sopenharmony_ci		info.sinfo_flags = sp->default_flags;
645462306a36Sopenharmony_ci		info.sinfo_ppid = sp->default_ppid;
645562306a36Sopenharmony_ci		info.sinfo_context = sp->default_context;
645662306a36Sopenharmony_ci		info.sinfo_timetolive = sp->default_timetolive;
645762306a36Sopenharmony_ci	}
645862306a36Sopenharmony_ci
645962306a36Sopenharmony_ci	if (put_user(len, optlen))
646062306a36Sopenharmony_ci		return -EFAULT;
646162306a36Sopenharmony_ci	if (copy_to_user(optval, &info, len))
646262306a36Sopenharmony_ci		return -EFAULT;
646362306a36Sopenharmony_ci
646462306a36Sopenharmony_ci	return 0;
646562306a36Sopenharmony_ci}
646662306a36Sopenharmony_ci
646762306a36Sopenharmony_ci/* RFC6458, Section 8.1.31. Set/get Default Send Parameters
646862306a36Sopenharmony_ci * (SCTP_DEFAULT_SNDINFO)
646962306a36Sopenharmony_ci */
647062306a36Sopenharmony_cistatic int sctp_getsockopt_default_sndinfo(struct sock *sk, int len,
647162306a36Sopenharmony_ci					   char __user *optval,
647262306a36Sopenharmony_ci					   int __user *optlen)
647362306a36Sopenharmony_ci{
647462306a36Sopenharmony_ci	struct sctp_sock *sp = sctp_sk(sk);
647562306a36Sopenharmony_ci	struct sctp_association *asoc;
647662306a36Sopenharmony_ci	struct sctp_sndinfo info;
647762306a36Sopenharmony_ci
647862306a36Sopenharmony_ci	if (len < sizeof(info))
647962306a36Sopenharmony_ci		return -EINVAL;
648062306a36Sopenharmony_ci
648162306a36Sopenharmony_ci	len = sizeof(info);
648262306a36Sopenharmony_ci
648362306a36Sopenharmony_ci	if (copy_from_user(&info, optval, len))
648462306a36Sopenharmony_ci		return -EFAULT;
648562306a36Sopenharmony_ci
648662306a36Sopenharmony_ci	asoc = sctp_id2assoc(sk, info.snd_assoc_id);
648762306a36Sopenharmony_ci	if (!asoc && info.snd_assoc_id != SCTP_FUTURE_ASSOC &&
648862306a36Sopenharmony_ci	    sctp_style(sk, UDP))
648962306a36Sopenharmony_ci		return -EINVAL;
649062306a36Sopenharmony_ci
649162306a36Sopenharmony_ci	if (asoc) {
649262306a36Sopenharmony_ci		info.snd_sid = asoc->default_stream;
649362306a36Sopenharmony_ci		info.snd_flags = asoc->default_flags;
649462306a36Sopenharmony_ci		info.snd_ppid = asoc->default_ppid;
649562306a36Sopenharmony_ci		info.snd_context = asoc->default_context;
649662306a36Sopenharmony_ci	} else {
649762306a36Sopenharmony_ci		info.snd_sid = sp->default_stream;
649862306a36Sopenharmony_ci		info.snd_flags = sp->default_flags;
649962306a36Sopenharmony_ci		info.snd_ppid = sp->default_ppid;
650062306a36Sopenharmony_ci		info.snd_context = sp->default_context;
650162306a36Sopenharmony_ci	}
650262306a36Sopenharmony_ci
650362306a36Sopenharmony_ci	if (put_user(len, optlen))
650462306a36Sopenharmony_ci		return -EFAULT;
650562306a36Sopenharmony_ci	if (copy_to_user(optval, &info, len))
650662306a36Sopenharmony_ci		return -EFAULT;
650762306a36Sopenharmony_ci
650862306a36Sopenharmony_ci	return 0;
650962306a36Sopenharmony_ci}
651062306a36Sopenharmony_ci
651162306a36Sopenharmony_ci/*
651262306a36Sopenharmony_ci *
651362306a36Sopenharmony_ci * 7.1.5 SCTP_NODELAY
651462306a36Sopenharmony_ci *
651562306a36Sopenharmony_ci * Turn on/off any Nagle-like algorithm.  This means that packets are
651662306a36Sopenharmony_ci * generally sent as soon as possible and no unnecessary delays are
651762306a36Sopenharmony_ci * introduced, at the cost of more packets in the network.  Expects an
651862306a36Sopenharmony_ci * integer boolean flag.
651962306a36Sopenharmony_ci */
652062306a36Sopenharmony_ci
652162306a36Sopenharmony_cistatic int sctp_getsockopt_nodelay(struct sock *sk, int len,
652262306a36Sopenharmony_ci				   char __user *optval, int __user *optlen)
652362306a36Sopenharmony_ci{
652462306a36Sopenharmony_ci	int val;
652562306a36Sopenharmony_ci
652662306a36Sopenharmony_ci	if (len < sizeof(int))
652762306a36Sopenharmony_ci		return -EINVAL;
652862306a36Sopenharmony_ci
652962306a36Sopenharmony_ci	len = sizeof(int);
653062306a36Sopenharmony_ci	val = (sctp_sk(sk)->nodelay == 1);
653162306a36Sopenharmony_ci	if (put_user(len, optlen))
653262306a36Sopenharmony_ci		return -EFAULT;
653362306a36Sopenharmony_ci	if (copy_to_user(optval, &val, len))
653462306a36Sopenharmony_ci		return -EFAULT;
653562306a36Sopenharmony_ci	return 0;
653662306a36Sopenharmony_ci}
653762306a36Sopenharmony_ci
653862306a36Sopenharmony_ci/*
653962306a36Sopenharmony_ci *
654062306a36Sopenharmony_ci * 7.1.1 SCTP_RTOINFO
654162306a36Sopenharmony_ci *
654262306a36Sopenharmony_ci * The protocol parameters used to initialize and bound retransmission
654362306a36Sopenharmony_ci * timeout (RTO) are tunable. sctp_rtoinfo structure is used to access
654462306a36Sopenharmony_ci * and modify these parameters.
654562306a36Sopenharmony_ci * All parameters are time values, in milliseconds.  A value of 0, when
654662306a36Sopenharmony_ci * modifying the parameters, indicates that the current value should not
654762306a36Sopenharmony_ci * be changed.
654862306a36Sopenharmony_ci *
654962306a36Sopenharmony_ci */
655062306a36Sopenharmony_cistatic int sctp_getsockopt_rtoinfo(struct sock *sk, int len,
655162306a36Sopenharmony_ci				char __user *optval,
655262306a36Sopenharmony_ci				int __user *optlen) {
655362306a36Sopenharmony_ci	struct sctp_rtoinfo rtoinfo;
655462306a36Sopenharmony_ci	struct sctp_association *asoc;
655562306a36Sopenharmony_ci
655662306a36Sopenharmony_ci	if (len < sizeof (struct sctp_rtoinfo))
655762306a36Sopenharmony_ci		return -EINVAL;
655862306a36Sopenharmony_ci
655962306a36Sopenharmony_ci	len = sizeof(struct sctp_rtoinfo);
656062306a36Sopenharmony_ci
656162306a36Sopenharmony_ci	if (copy_from_user(&rtoinfo, optval, len))
656262306a36Sopenharmony_ci		return -EFAULT;
656362306a36Sopenharmony_ci
656462306a36Sopenharmony_ci	asoc = sctp_id2assoc(sk, rtoinfo.srto_assoc_id);
656562306a36Sopenharmony_ci
656662306a36Sopenharmony_ci	if (!asoc && rtoinfo.srto_assoc_id != SCTP_FUTURE_ASSOC &&
656762306a36Sopenharmony_ci	    sctp_style(sk, UDP))
656862306a36Sopenharmony_ci		return -EINVAL;
656962306a36Sopenharmony_ci
657062306a36Sopenharmony_ci	/* Values corresponding to the specific association. */
657162306a36Sopenharmony_ci	if (asoc) {
657262306a36Sopenharmony_ci		rtoinfo.srto_initial = jiffies_to_msecs(asoc->rto_initial);
657362306a36Sopenharmony_ci		rtoinfo.srto_max = jiffies_to_msecs(asoc->rto_max);
657462306a36Sopenharmony_ci		rtoinfo.srto_min = jiffies_to_msecs(asoc->rto_min);
657562306a36Sopenharmony_ci	} else {
657662306a36Sopenharmony_ci		/* Values corresponding to the endpoint. */
657762306a36Sopenharmony_ci		struct sctp_sock *sp = sctp_sk(sk);
657862306a36Sopenharmony_ci
657962306a36Sopenharmony_ci		rtoinfo.srto_initial = sp->rtoinfo.srto_initial;
658062306a36Sopenharmony_ci		rtoinfo.srto_max = sp->rtoinfo.srto_max;
658162306a36Sopenharmony_ci		rtoinfo.srto_min = sp->rtoinfo.srto_min;
658262306a36Sopenharmony_ci	}
658362306a36Sopenharmony_ci
658462306a36Sopenharmony_ci	if (put_user(len, optlen))
658562306a36Sopenharmony_ci		return -EFAULT;
658662306a36Sopenharmony_ci
658762306a36Sopenharmony_ci	if (copy_to_user(optval, &rtoinfo, len))
658862306a36Sopenharmony_ci		return -EFAULT;
658962306a36Sopenharmony_ci
659062306a36Sopenharmony_ci	return 0;
659162306a36Sopenharmony_ci}
659262306a36Sopenharmony_ci
659362306a36Sopenharmony_ci/*
659462306a36Sopenharmony_ci *
659562306a36Sopenharmony_ci * 7.1.2 SCTP_ASSOCINFO
659662306a36Sopenharmony_ci *
659762306a36Sopenharmony_ci * This option is used to tune the maximum retransmission attempts
659862306a36Sopenharmony_ci * of the association.
659962306a36Sopenharmony_ci * Returns an error if the new association retransmission value is
660062306a36Sopenharmony_ci * greater than the sum of the retransmission value  of the peer.
660162306a36Sopenharmony_ci * See [SCTP] for more information.
660262306a36Sopenharmony_ci *
660362306a36Sopenharmony_ci */
660462306a36Sopenharmony_cistatic int sctp_getsockopt_associnfo(struct sock *sk, int len,
660562306a36Sopenharmony_ci				     char __user *optval,
660662306a36Sopenharmony_ci				     int __user *optlen)
660762306a36Sopenharmony_ci{
660862306a36Sopenharmony_ci
660962306a36Sopenharmony_ci	struct sctp_assocparams assocparams;
661062306a36Sopenharmony_ci	struct sctp_association *asoc;
661162306a36Sopenharmony_ci	struct list_head *pos;
661262306a36Sopenharmony_ci	int cnt = 0;
661362306a36Sopenharmony_ci
661462306a36Sopenharmony_ci	if (len < sizeof (struct sctp_assocparams))
661562306a36Sopenharmony_ci		return -EINVAL;
661662306a36Sopenharmony_ci
661762306a36Sopenharmony_ci	len = sizeof(struct sctp_assocparams);
661862306a36Sopenharmony_ci
661962306a36Sopenharmony_ci	if (copy_from_user(&assocparams, optval, len))
662062306a36Sopenharmony_ci		return -EFAULT;
662162306a36Sopenharmony_ci
662262306a36Sopenharmony_ci	asoc = sctp_id2assoc(sk, assocparams.sasoc_assoc_id);
662362306a36Sopenharmony_ci
662462306a36Sopenharmony_ci	if (!asoc && assocparams.sasoc_assoc_id != SCTP_FUTURE_ASSOC &&
662562306a36Sopenharmony_ci	    sctp_style(sk, UDP))
662662306a36Sopenharmony_ci		return -EINVAL;
662762306a36Sopenharmony_ci
662862306a36Sopenharmony_ci	/* Values correspoinding to the specific association */
662962306a36Sopenharmony_ci	if (asoc) {
663062306a36Sopenharmony_ci		assocparams.sasoc_asocmaxrxt = asoc->max_retrans;
663162306a36Sopenharmony_ci		assocparams.sasoc_peer_rwnd = asoc->peer.rwnd;
663262306a36Sopenharmony_ci		assocparams.sasoc_local_rwnd = asoc->a_rwnd;
663362306a36Sopenharmony_ci		assocparams.sasoc_cookie_life = ktime_to_ms(asoc->cookie_life);
663462306a36Sopenharmony_ci
663562306a36Sopenharmony_ci		list_for_each(pos, &asoc->peer.transport_addr_list) {
663662306a36Sopenharmony_ci			cnt++;
663762306a36Sopenharmony_ci		}
663862306a36Sopenharmony_ci
663962306a36Sopenharmony_ci		assocparams.sasoc_number_peer_destinations = cnt;
664062306a36Sopenharmony_ci	} else {
664162306a36Sopenharmony_ci		/* Values corresponding to the endpoint */
664262306a36Sopenharmony_ci		struct sctp_sock *sp = sctp_sk(sk);
664362306a36Sopenharmony_ci
664462306a36Sopenharmony_ci		assocparams.sasoc_asocmaxrxt = sp->assocparams.sasoc_asocmaxrxt;
664562306a36Sopenharmony_ci		assocparams.sasoc_peer_rwnd = sp->assocparams.sasoc_peer_rwnd;
664662306a36Sopenharmony_ci		assocparams.sasoc_local_rwnd = sp->assocparams.sasoc_local_rwnd;
664762306a36Sopenharmony_ci		assocparams.sasoc_cookie_life =
664862306a36Sopenharmony_ci					sp->assocparams.sasoc_cookie_life;
664962306a36Sopenharmony_ci		assocparams.sasoc_number_peer_destinations =
665062306a36Sopenharmony_ci					sp->assocparams.
665162306a36Sopenharmony_ci					sasoc_number_peer_destinations;
665262306a36Sopenharmony_ci	}
665362306a36Sopenharmony_ci
665462306a36Sopenharmony_ci	if (put_user(len, optlen))
665562306a36Sopenharmony_ci		return -EFAULT;
665662306a36Sopenharmony_ci
665762306a36Sopenharmony_ci	if (copy_to_user(optval, &assocparams, len))
665862306a36Sopenharmony_ci		return -EFAULT;
665962306a36Sopenharmony_ci
666062306a36Sopenharmony_ci	return 0;
666162306a36Sopenharmony_ci}
666262306a36Sopenharmony_ci
666362306a36Sopenharmony_ci/*
666462306a36Sopenharmony_ci * 7.1.16 Set/clear IPv4 mapped addresses (SCTP_I_WANT_MAPPED_V4_ADDR)
666562306a36Sopenharmony_ci *
666662306a36Sopenharmony_ci * This socket option is a boolean flag which turns on or off mapped V4
666762306a36Sopenharmony_ci * addresses.  If this option is turned on and the socket is type
666862306a36Sopenharmony_ci * PF_INET6, then IPv4 addresses will be mapped to V6 representation.
666962306a36Sopenharmony_ci * If this option is turned off, then no mapping will be done of V4
667062306a36Sopenharmony_ci * addresses and a user will receive both PF_INET6 and PF_INET type
667162306a36Sopenharmony_ci * addresses on the socket.
667262306a36Sopenharmony_ci */
667362306a36Sopenharmony_cistatic int sctp_getsockopt_mappedv4(struct sock *sk, int len,
667462306a36Sopenharmony_ci				    char __user *optval, int __user *optlen)
667562306a36Sopenharmony_ci{
667662306a36Sopenharmony_ci	int val;
667762306a36Sopenharmony_ci	struct sctp_sock *sp = sctp_sk(sk);
667862306a36Sopenharmony_ci
667962306a36Sopenharmony_ci	if (len < sizeof(int))
668062306a36Sopenharmony_ci		return -EINVAL;
668162306a36Sopenharmony_ci
668262306a36Sopenharmony_ci	len = sizeof(int);
668362306a36Sopenharmony_ci	val = sp->v4mapped;
668462306a36Sopenharmony_ci	if (put_user(len, optlen))
668562306a36Sopenharmony_ci		return -EFAULT;
668662306a36Sopenharmony_ci	if (copy_to_user(optval, &val, len))
668762306a36Sopenharmony_ci		return -EFAULT;
668862306a36Sopenharmony_ci
668962306a36Sopenharmony_ci	return 0;
669062306a36Sopenharmony_ci}
669162306a36Sopenharmony_ci
669262306a36Sopenharmony_ci/*
669362306a36Sopenharmony_ci * 7.1.29.  Set or Get the default context (SCTP_CONTEXT)
669462306a36Sopenharmony_ci * (chapter and verse is quoted at sctp_setsockopt_context())
669562306a36Sopenharmony_ci */
669662306a36Sopenharmony_cistatic int sctp_getsockopt_context(struct sock *sk, int len,
669762306a36Sopenharmony_ci				   char __user *optval, int __user *optlen)
669862306a36Sopenharmony_ci{
669962306a36Sopenharmony_ci	struct sctp_assoc_value params;
670062306a36Sopenharmony_ci	struct sctp_association *asoc;
670162306a36Sopenharmony_ci
670262306a36Sopenharmony_ci	if (len < sizeof(struct sctp_assoc_value))
670362306a36Sopenharmony_ci		return -EINVAL;
670462306a36Sopenharmony_ci
670562306a36Sopenharmony_ci	len = sizeof(struct sctp_assoc_value);
670662306a36Sopenharmony_ci
670762306a36Sopenharmony_ci	if (copy_from_user(&params, optval, len))
670862306a36Sopenharmony_ci		return -EFAULT;
670962306a36Sopenharmony_ci
671062306a36Sopenharmony_ci	asoc = sctp_id2assoc(sk, params.assoc_id);
671162306a36Sopenharmony_ci	if (!asoc && params.assoc_id != SCTP_FUTURE_ASSOC &&
671262306a36Sopenharmony_ci	    sctp_style(sk, UDP))
671362306a36Sopenharmony_ci		return -EINVAL;
671462306a36Sopenharmony_ci
671562306a36Sopenharmony_ci	params.assoc_value = asoc ? asoc->default_rcv_context
671662306a36Sopenharmony_ci				  : sctp_sk(sk)->default_rcv_context;
671762306a36Sopenharmony_ci
671862306a36Sopenharmony_ci	if (put_user(len, optlen))
671962306a36Sopenharmony_ci		return -EFAULT;
672062306a36Sopenharmony_ci	if (copy_to_user(optval, &params, len))
672162306a36Sopenharmony_ci		return -EFAULT;
672262306a36Sopenharmony_ci
672362306a36Sopenharmony_ci	return 0;
672462306a36Sopenharmony_ci}
672562306a36Sopenharmony_ci
672662306a36Sopenharmony_ci/*
672762306a36Sopenharmony_ci * 8.1.16.  Get or Set the Maximum Fragmentation Size (SCTP_MAXSEG)
672862306a36Sopenharmony_ci * This option will get or set the maximum size to put in any outgoing
672962306a36Sopenharmony_ci * SCTP DATA chunk.  If a message is larger than this size it will be
673062306a36Sopenharmony_ci * fragmented by SCTP into the specified size.  Note that the underlying
673162306a36Sopenharmony_ci * SCTP implementation may fragment into smaller sized chunks when the
673262306a36Sopenharmony_ci * PMTU of the underlying association is smaller than the value set by
673362306a36Sopenharmony_ci * the user.  The default value for this option is '0' which indicates
673462306a36Sopenharmony_ci * the user is NOT limiting fragmentation and only the PMTU will effect
673562306a36Sopenharmony_ci * SCTP's choice of DATA chunk size.  Note also that values set larger
673662306a36Sopenharmony_ci * than the maximum size of an IP datagram will effectively let SCTP
673762306a36Sopenharmony_ci * control fragmentation (i.e. the same as setting this option to 0).
673862306a36Sopenharmony_ci *
673962306a36Sopenharmony_ci * The following structure is used to access and modify this parameter:
674062306a36Sopenharmony_ci *
674162306a36Sopenharmony_ci * struct sctp_assoc_value {
674262306a36Sopenharmony_ci *   sctp_assoc_t assoc_id;
674362306a36Sopenharmony_ci *   uint32_t assoc_value;
674462306a36Sopenharmony_ci * };
674562306a36Sopenharmony_ci *
674662306a36Sopenharmony_ci * assoc_id:  This parameter is ignored for one-to-one style sockets.
674762306a36Sopenharmony_ci *    For one-to-many style sockets this parameter indicates which
674862306a36Sopenharmony_ci *    association the user is performing an action upon.  Note that if
674962306a36Sopenharmony_ci *    this field's value is zero then the endpoints default value is
675062306a36Sopenharmony_ci *    changed (effecting future associations only).
675162306a36Sopenharmony_ci * assoc_value:  This parameter specifies the maximum size in bytes.
675262306a36Sopenharmony_ci */
675362306a36Sopenharmony_cistatic int sctp_getsockopt_maxseg(struct sock *sk, int len,
675462306a36Sopenharmony_ci				  char __user *optval, int __user *optlen)
675562306a36Sopenharmony_ci{
675662306a36Sopenharmony_ci	struct sctp_assoc_value params;
675762306a36Sopenharmony_ci	struct sctp_association *asoc;
675862306a36Sopenharmony_ci
675962306a36Sopenharmony_ci	if (len == sizeof(int)) {
676062306a36Sopenharmony_ci		pr_warn_ratelimited(DEPRECATED
676162306a36Sopenharmony_ci				    "%s (pid %d) "
676262306a36Sopenharmony_ci				    "Use of int in maxseg socket option.\n"
676362306a36Sopenharmony_ci				    "Use struct sctp_assoc_value instead\n",
676462306a36Sopenharmony_ci				    current->comm, task_pid_nr(current));
676562306a36Sopenharmony_ci		params.assoc_id = SCTP_FUTURE_ASSOC;
676662306a36Sopenharmony_ci	} else if (len >= sizeof(struct sctp_assoc_value)) {
676762306a36Sopenharmony_ci		len = sizeof(struct sctp_assoc_value);
676862306a36Sopenharmony_ci		if (copy_from_user(&params, optval, len))
676962306a36Sopenharmony_ci			return -EFAULT;
677062306a36Sopenharmony_ci	} else
677162306a36Sopenharmony_ci		return -EINVAL;
677262306a36Sopenharmony_ci
677362306a36Sopenharmony_ci	asoc = sctp_id2assoc(sk, params.assoc_id);
677462306a36Sopenharmony_ci	if (!asoc && params.assoc_id != SCTP_FUTURE_ASSOC &&
677562306a36Sopenharmony_ci	    sctp_style(sk, UDP))
677662306a36Sopenharmony_ci		return -EINVAL;
677762306a36Sopenharmony_ci
677862306a36Sopenharmony_ci	if (asoc)
677962306a36Sopenharmony_ci		params.assoc_value = asoc->frag_point;
678062306a36Sopenharmony_ci	else
678162306a36Sopenharmony_ci		params.assoc_value = sctp_sk(sk)->user_frag;
678262306a36Sopenharmony_ci
678362306a36Sopenharmony_ci	if (put_user(len, optlen))
678462306a36Sopenharmony_ci		return -EFAULT;
678562306a36Sopenharmony_ci	if (len == sizeof(int)) {
678662306a36Sopenharmony_ci		if (copy_to_user(optval, &params.assoc_value, len))
678762306a36Sopenharmony_ci			return -EFAULT;
678862306a36Sopenharmony_ci	} else {
678962306a36Sopenharmony_ci		if (copy_to_user(optval, &params, len))
679062306a36Sopenharmony_ci			return -EFAULT;
679162306a36Sopenharmony_ci	}
679262306a36Sopenharmony_ci
679362306a36Sopenharmony_ci	return 0;
679462306a36Sopenharmony_ci}
679562306a36Sopenharmony_ci
679662306a36Sopenharmony_ci/*
679762306a36Sopenharmony_ci * 7.1.24.  Get or set fragmented interleave (SCTP_FRAGMENT_INTERLEAVE)
679862306a36Sopenharmony_ci * (chapter and verse is quoted at sctp_setsockopt_fragment_interleave())
679962306a36Sopenharmony_ci */
680062306a36Sopenharmony_cistatic int sctp_getsockopt_fragment_interleave(struct sock *sk, int len,
680162306a36Sopenharmony_ci					       char __user *optval, int __user *optlen)
680262306a36Sopenharmony_ci{
680362306a36Sopenharmony_ci	int val;
680462306a36Sopenharmony_ci
680562306a36Sopenharmony_ci	if (len < sizeof(int))
680662306a36Sopenharmony_ci		return -EINVAL;
680762306a36Sopenharmony_ci
680862306a36Sopenharmony_ci	len = sizeof(int);
680962306a36Sopenharmony_ci
681062306a36Sopenharmony_ci	val = sctp_sk(sk)->frag_interleave;
681162306a36Sopenharmony_ci	if (put_user(len, optlen))
681262306a36Sopenharmony_ci		return -EFAULT;
681362306a36Sopenharmony_ci	if (copy_to_user(optval, &val, len))
681462306a36Sopenharmony_ci		return -EFAULT;
681562306a36Sopenharmony_ci
681662306a36Sopenharmony_ci	return 0;
681762306a36Sopenharmony_ci}
681862306a36Sopenharmony_ci
681962306a36Sopenharmony_ci/*
682062306a36Sopenharmony_ci * 7.1.25.  Set or Get the sctp partial delivery point
682162306a36Sopenharmony_ci * (chapter and verse is quoted at sctp_setsockopt_partial_delivery_point())
682262306a36Sopenharmony_ci */
682362306a36Sopenharmony_cistatic int sctp_getsockopt_partial_delivery_point(struct sock *sk, int len,
682462306a36Sopenharmony_ci						  char __user *optval,
682562306a36Sopenharmony_ci						  int __user *optlen)
682662306a36Sopenharmony_ci{
682762306a36Sopenharmony_ci	u32 val;
682862306a36Sopenharmony_ci
682962306a36Sopenharmony_ci	if (len < sizeof(u32))
683062306a36Sopenharmony_ci		return -EINVAL;
683162306a36Sopenharmony_ci
683262306a36Sopenharmony_ci	len = sizeof(u32);
683362306a36Sopenharmony_ci
683462306a36Sopenharmony_ci	val = sctp_sk(sk)->pd_point;
683562306a36Sopenharmony_ci	if (put_user(len, optlen))
683662306a36Sopenharmony_ci		return -EFAULT;
683762306a36Sopenharmony_ci	if (copy_to_user(optval, &val, len))
683862306a36Sopenharmony_ci		return -EFAULT;
683962306a36Sopenharmony_ci
684062306a36Sopenharmony_ci	return 0;
684162306a36Sopenharmony_ci}
684262306a36Sopenharmony_ci
684362306a36Sopenharmony_ci/*
684462306a36Sopenharmony_ci * 7.1.28.  Set or Get the maximum burst (SCTP_MAX_BURST)
684562306a36Sopenharmony_ci * (chapter and verse is quoted at sctp_setsockopt_maxburst())
684662306a36Sopenharmony_ci */
684762306a36Sopenharmony_cistatic int sctp_getsockopt_maxburst(struct sock *sk, int len,
684862306a36Sopenharmony_ci				    char __user *optval,
684962306a36Sopenharmony_ci				    int __user *optlen)
685062306a36Sopenharmony_ci{
685162306a36Sopenharmony_ci	struct sctp_assoc_value params;
685262306a36Sopenharmony_ci	struct sctp_association *asoc;
685362306a36Sopenharmony_ci
685462306a36Sopenharmony_ci	if (len == sizeof(int)) {
685562306a36Sopenharmony_ci		pr_warn_ratelimited(DEPRECATED
685662306a36Sopenharmony_ci				    "%s (pid %d) "
685762306a36Sopenharmony_ci				    "Use of int in max_burst socket option.\n"
685862306a36Sopenharmony_ci				    "Use struct sctp_assoc_value instead\n",
685962306a36Sopenharmony_ci				    current->comm, task_pid_nr(current));
686062306a36Sopenharmony_ci		params.assoc_id = SCTP_FUTURE_ASSOC;
686162306a36Sopenharmony_ci	} else if (len >= sizeof(struct sctp_assoc_value)) {
686262306a36Sopenharmony_ci		len = sizeof(struct sctp_assoc_value);
686362306a36Sopenharmony_ci		if (copy_from_user(&params, optval, len))
686462306a36Sopenharmony_ci			return -EFAULT;
686562306a36Sopenharmony_ci	} else
686662306a36Sopenharmony_ci		return -EINVAL;
686762306a36Sopenharmony_ci
686862306a36Sopenharmony_ci	asoc = sctp_id2assoc(sk, params.assoc_id);
686962306a36Sopenharmony_ci	if (!asoc && params.assoc_id != SCTP_FUTURE_ASSOC &&
687062306a36Sopenharmony_ci	    sctp_style(sk, UDP))
687162306a36Sopenharmony_ci		return -EINVAL;
687262306a36Sopenharmony_ci
687362306a36Sopenharmony_ci	params.assoc_value = asoc ? asoc->max_burst : sctp_sk(sk)->max_burst;
687462306a36Sopenharmony_ci
687562306a36Sopenharmony_ci	if (len == sizeof(int)) {
687662306a36Sopenharmony_ci		if (copy_to_user(optval, &params.assoc_value, len))
687762306a36Sopenharmony_ci			return -EFAULT;
687862306a36Sopenharmony_ci	} else {
687962306a36Sopenharmony_ci		if (copy_to_user(optval, &params, len))
688062306a36Sopenharmony_ci			return -EFAULT;
688162306a36Sopenharmony_ci	}
688262306a36Sopenharmony_ci
688362306a36Sopenharmony_ci	return 0;
688462306a36Sopenharmony_ci
688562306a36Sopenharmony_ci}
688662306a36Sopenharmony_ci
688762306a36Sopenharmony_cistatic int sctp_getsockopt_hmac_ident(struct sock *sk, int len,
688862306a36Sopenharmony_ci				    char __user *optval, int __user *optlen)
688962306a36Sopenharmony_ci{
689062306a36Sopenharmony_ci	struct sctp_endpoint *ep = sctp_sk(sk)->ep;
689162306a36Sopenharmony_ci	struct sctp_hmacalgo  __user *p = (void __user *)optval;
689262306a36Sopenharmony_ci	struct sctp_hmac_algo_param *hmacs;
689362306a36Sopenharmony_ci	__u16 data_len = 0;
689462306a36Sopenharmony_ci	u32 num_idents;
689562306a36Sopenharmony_ci	int i;
689662306a36Sopenharmony_ci
689762306a36Sopenharmony_ci	if (!ep->auth_enable)
689862306a36Sopenharmony_ci		return -EACCES;
689962306a36Sopenharmony_ci
690062306a36Sopenharmony_ci	hmacs = ep->auth_hmacs_list;
690162306a36Sopenharmony_ci	data_len = ntohs(hmacs->param_hdr.length) -
690262306a36Sopenharmony_ci		   sizeof(struct sctp_paramhdr);
690362306a36Sopenharmony_ci
690462306a36Sopenharmony_ci	if (len < sizeof(struct sctp_hmacalgo) + data_len)
690562306a36Sopenharmony_ci		return -EINVAL;
690662306a36Sopenharmony_ci
690762306a36Sopenharmony_ci	len = sizeof(struct sctp_hmacalgo) + data_len;
690862306a36Sopenharmony_ci	num_idents = data_len / sizeof(u16);
690962306a36Sopenharmony_ci
691062306a36Sopenharmony_ci	if (put_user(len, optlen))
691162306a36Sopenharmony_ci		return -EFAULT;
691262306a36Sopenharmony_ci	if (put_user(num_idents, &p->shmac_num_idents))
691362306a36Sopenharmony_ci		return -EFAULT;
691462306a36Sopenharmony_ci	for (i = 0; i < num_idents; i++) {
691562306a36Sopenharmony_ci		__u16 hmacid = ntohs(hmacs->hmac_ids[i]);
691662306a36Sopenharmony_ci
691762306a36Sopenharmony_ci		if (copy_to_user(&p->shmac_idents[i], &hmacid, sizeof(__u16)))
691862306a36Sopenharmony_ci			return -EFAULT;
691962306a36Sopenharmony_ci	}
692062306a36Sopenharmony_ci	return 0;
692162306a36Sopenharmony_ci}
692262306a36Sopenharmony_ci
692362306a36Sopenharmony_cistatic int sctp_getsockopt_active_key(struct sock *sk, int len,
692462306a36Sopenharmony_ci				    char __user *optval, int __user *optlen)
692562306a36Sopenharmony_ci{
692662306a36Sopenharmony_ci	struct sctp_endpoint *ep = sctp_sk(sk)->ep;
692762306a36Sopenharmony_ci	struct sctp_authkeyid val;
692862306a36Sopenharmony_ci	struct sctp_association *asoc;
692962306a36Sopenharmony_ci
693062306a36Sopenharmony_ci	if (len < sizeof(struct sctp_authkeyid))
693162306a36Sopenharmony_ci		return -EINVAL;
693262306a36Sopenharmony_ci
693362306a36Sopenharmony_ci	len = sizeof(struct sctp_authkeyid);
693462306a36Sopenharmony_ci	if (copy_from_user(&val, optval, len))
693562306a36Sopenharmony_ci		return -EFAULT;
693662306a36Sopenharmony_ci
693762306a36Sopenharmony_ci	asoc = sctp_id2assoc(sk, val.scact_assoc_id);
693862306a36Sopenharmony_ci	if (!asoc && val.scact_assoc_id && sctp_style(sk, UDP))
693962306a36Sopenharmony_ci		return -EINVAL;
694062306a36Sopenharmony_ci
694162306a36Sopenharmony_ci	if (asoc) {
694262306a36Sopenharmony_ci		if (!asoc->peer.auth_capable)
694362306a36Sopenharmony_ci			return -EACCES;
694462306a36Sopenharmony_ci		val.scact_keynumber = asoc->active_key_id;
694562306a36Sopenharmony_ci	} else {
694662306a36Sopenharmony_ci		if (!ep->auth_enable)
694762306a36Sopenharmony_ci			return -EACCES;
694862306a36Sopenharmony_ci		val.scact_keynumber = ep->active_key_id;
694962306a36Sopenharmony_ci	}
695062306a36Sopenharmony_ci
695162306a36Sopenharmony_ci	if (put_user(len, optlen))
695262306a36Sopenharmony_ci		return -EFAULT;
695362306a36Sopenharmony_ci	if (copy_to_user(optval, &val, len))
695462306a36Sopenharmony_ci		return -EFAULT;
695562306a36Sopenharmony_ci
695662306a36Sopenharmony_ci	return 0;
695762306a36Sopenharmony_ci}
695862306a36Sopenharmony_ci
695962306a36Sopenharmony_cistatic int sctp_getsockopt_peer_auth_chunks(struct sock *sk, int len,
696062306a36Sopenharmony_ci				    char __user *optval, int __user *optlen)
696162306a36Sopenharmony_ci{
696262306a36Sopenharmony_ci	struct sctp_authchunks __user *p = (void __user *)optval;
696362306a36Sopenharmony_ci	struct sctp_authchunks val;
696462306a36Sopenharmony_ci	struct sctp_association *asoc;
696562306a36Sopenharmony_ci	struct sctp_chunks_param *ch;
696662306a36Sopenharmony_ci	u32    num_chunks = 0;
696762306a36Sopenharmony_ci	char __user *to;
696862306a36Sopenharmony_ci
696962306a36Sopenharmony_ci	if (len < sizeof(struct sctp_authchunks))
697062306a36Sopenharmony_ci		return -EINVAL;
697162306a36Sopenharmony_ci
697262306a36Sopenharmony_ci	if (copy_from_user(&val, optval, sizeof(val)))
697362306a36Sopenharmony_ci		return -EFAULT;
697462306a36Sopenharmony_ci
697562306a36Sopenharmony_ci	to = p->gauth_chunks;
697662306a36Sopenharmony_ci	asoc = sctp_id2assoc(sk, val.gauth_assoc_id);
697762306a36Sopenharmony_ci	if (!asoc)
697862306a36Sopenharmony_ci		return -EINVAL;
697962306a36Sopenharmony_ci
698062306a36Sopenharmony_ci	if (!asoc->peer.auth_capable)
698162306a36Sopenharmony_ci		return -EACCES;
698262306a36Sopenharmony_ci
698362306a36Sopenharmony_ci	ch = asoc->peer.peer_chunks;
698462306a36Sopenharmony_ci	if (!ch)
698562306a36Sopenharmony_ci		goto num;
698662306a36Sopenharmony_ci
698762306a36Sopenharmony_ci	/* See if the user provided enough room for all the data */
698862306a36Sopenharmony_ci	num_chunks = ntohs(ch->param_hdr.length) - sizeof(struct sctp_paramhdr);
698962306a36Sopenharmony_ci	if (len < num_chunks)
699062306a36Sopenharmony_ci		return -EINVAL;
699162306a36Sopenharmony_ci
699262306a36Sopenharmony_ci	if (copy_to_user(to, ch->chunks, num_chunks))
699362306a36Sopenharmony_ci		return -EFAULT;
699462306a36Sopenharmony_cinum:
699562306a36Sopenharmony_ci	len = sizeof(struct sctp_authchunks) + num_chunks;
699662306a36Sopenharmony_ci	if (put_user(len, optlen))
699762306a36Sopenharmony_ci		return -EFAULT;
699862306a36Sopenharmony_ci	if (put_user(num_chunks, &p->gauth_number_of_chunks))
699962306a36Sopenharmony_ci		return -EFAULT;
700062306a36Sopenharmony_ci	return 0;
700162306a36Sopenharmony_ci}
700262306a36Sopenharmony_ci
700362306a36Sopenharmony_cistatic int sctp_getsockopt_local_auth_chunks(struct sock *sk, int len,
700462306a36Sopenharmony_ci				    char __user *optval, int __user *optlen)
700562306a36Sopenharmony_ci{
700662306a36Sopenharmony_ci	struct sctp_endpoint *ep = sctp_sk(sk)->ep;
700762306a36Sopenharmony_ci	struct sctp_authchunks __user *p = (void __user *)optval;
700862306a36Sopenharmony_ci	struct sctp_authchunks val;
700962306a36Sopenharmony_ci	struct sctp_association *asoc;
701062306a36Sopenharmony_ci	struct sctp_chunks_param *ch;
701162306a36Sopenharmony_ci	u32    num_chunks = 0;
701262306a36Sopenharmony_ci	char __user *to;
701362306a36Sopenharmony_ci
701462306a36Sopenharmony_ci	if (len < sizeof(struct sctp_authchunks))
701562306a36Sopenharmony_ci		return -EINVAL;
701662306a36Sopenharmony_ci
701762306a36Sopenharmony_ci	if (copy_from_user(&val, optval, sizeof(val)))
701862306a36Sopenharmony_ci		return -EFAULT;
701962306a36Sopenharmony_ci
702062306a36Sopenharmony_ci	to = p->gauth_chunks;
702162306a36Sopenharmony_ci	asoc = sctp_id2assoc(sk, val.gauth_assoc_id);
702262306a36Sopenharmony_ci	if (!asoc && val.gauth_assoc_id != SCTP_FUTURE_ASSOC &&
702362306a36Sopenharmony_ci	    sctp_style(sk, UDP))
702462306a36Sopenharmony_ci		return -EINVAL;
702562306a36Sopenharmony_ci
702662306a36Sopenharmony_ci	if (asoc) {
702762306a36Sopenharmony_ci		if (!asoc->peer.auth_capable)
702862306a36Sopenharmony_ci			return -EACCES;
702962306a36Sopenharmony_ci		ch = (struct sctp_chunks_param *)asoc->c.auth_chunks;
703062306a36Sopenharmony_ci	} else {
703162306a36Sopenharmony_ci		if (!ep->auth_enable)
703262306a36Sopenharmony_ci			return -EACCES;
703362306a36Sopenharmony_ci		ch = ep->auth_chunk_list;
703462306a36Sopenharmony_ci	}
703562306a36Sopenharmony_ci	if (!ch)
703662306a36Sopenharmony_ci		goto num;
703762306a36Sopenharmony_ci
703862306a36Sopenharmony_ci	num_chunks = ntohs(ch->param_hdr.length) - sizeof(struct sctp_paramhdr);
703962306a36Sopenharmony_ci	if (len < sizeof(struct sctp_authchunks) + num_chunks)
704062306a36Sopenharmony_ci		return -EINVAL;
704162306a36Sopenharmony_ci
704262306a36Sopenharmony_ci	if (copy_to_user(to, ch->chunks, num_chunks))
704362306a36Sopenharmony_ci		return -EFAULT;
704462306a36Sopenharmony_cinum:
704562306a36Sopenharmony_ci	len = sizeof(struct sctp_authchunks) + num_chunks;
704662306a36Sopenharmony_ci	if (put_user(len, optlen))
704762306a36Sopenharmony_ci		return -EFAULT;
704862306a36Sopenharmony_ci	if (put_user(num_chunks, &p->gauth_number_of_chunks))
704962306a36Sopenharmony_ci		return -EFAULT;
705062306a36Sopenharmony_ci
705162306a36Sopenharmony_ci	return 0;
705262306a36Sopenharmony_ci}
705362306a36Sopenharmony_ci
705462306a36Sopenharmony_ci/*
705562306a36Sopenharmony_ci * 8.2.5.  Get the Current Number of Associations (SCTP_GET_ASSOC_NUMBER)
705662306a36Sopenharmony_ci * This option gets the current number of associations that are attached
705762306a36Sopenharmony_ci * to a one-to-many style socket.  The option value is an uint32_t.
705862306a36Sopenharmony_ci */
705962306a36Sopenharmony_cistatic int sctp_getsockopt_assoc_number(struct sock *sk, int len,
706062306a36Sopenharmony_ci				    char __user *optval, int __user *optlen)
706162306a36Sopenharmony_ci{
706262306a36Sopenharmony_ci	struct sctp_sock *sp = sctp_sk(sk);
706362306a36Sopenharmony_ci	struct sctp_association *asoc;
706462306a36Sopenharmony_ci	u32 val = 0;
706562306a36Sopenharmony_ci
706662306a36Sopenharmony_ci	if (sctp_style(sk, TCP))
706762306a36Sopenharmony_ci		return -EOPNOTSUPP;
706862306a36Sopenharmony_ci
706962306a36Sopenharmony_ci	if (len < sizeof(u32))
707062306a36Sopenharmony_ci		return -EINVAL;
707162306a36Sopenharmony_ci
707262306a36Sopenharmony_ci	len = sizeof(u32);
707362306a36Sopenharmony_ci
707462306a36Sopenharmony_ci	list_for_each_entry(asoc, &(sp->ep->asocs), asocs) {
707562306a36Sopenharmony_ci		val++;
707662306a36Sopenharmony_ci	}
707762306a36Sopenharmony_ci
707862306a36Sopenharmony_ci	if (put_user(len, optlen))
707962306a36Sopenharmony_ci		return -EFAULT;
708062306a36Sopenharmony_ci	if (copy_to_user(optval, &val, len))
708162306a36Sopenharmony_ci		return -EFAULT;
708262306a36Sopenharmony_ci
708362306a36Sopenharmony_ci	return 0;
708462306a36Sopenharmony_ci}
708562306a36Sopenharmony_ci
708662306a36Sopenharmony_ci/*
708762306a36Sopenharmony_ci * 8.1.23 SCTP_AUTO_ASCONF
708862306a36Sopenharmony_ci * See the corresponding setsockopt entry as description
708962306a36Sopenharmony_ci */
709062306a36Sopenharmony_cistatic int sctp_getsockopt_auto_asconf(struct sock *sk, int len,
709162306a36Sopenharmony_ci				   char __user *optval, int __user *optlen)
709262306a36Sopenharmony_ci{
709362306a36Sopenharmony_ci	int val = 0;
709462306a36Sopenharmony_ci
709562306a36Sopenharmony_ci	if (len < sizeof(int))
709662306a36Sopenharmony_ci		return -EINVAL;
709762306a36Sopenharmony_ci
709862306a36Sopenharmony_ci	len = sizeof(int);
709962306a36Sopenharmony_ci	if (sctp_sk(sk)->do_auto_asconf && sctp_is_ep_boundall(sk))
710062306a36Sopenharmony_ci		val = 1;
710162306a36Sopenharmony_ci	if (put_user(len, optlen))
710262306a36Sopenharmony_ci		return -EFAULT;
710362306a36Sopenharmony_ci	if (copy_to_user(optval, &val, len))
710462306a36Sopenharmony_ci		return -EFAULT;
710562306a36Sopenharmony_ci	return 0;
710662306a36Sopenharmony_ci}
710762306a36Sopenharmony_ci
710862306a36Sopenharmony_ci/*
710962306a36Sopenharmony_ci * 8.2.6. Get the Current Identifiers of Associations
711062306a36Sopenharmony_ci *        (SCTP_GET_ASSOC_ID_LIST)
711162306a36Sopenharmony_ci *
711262306a36Sopenharmony_ci * This option gets the current list of SCTP association identifiers of
711362306a36Sopenharmony_ci * the SCTP associations handled by a one-to-many style socket.
711462306a36Sopenharmony_ci */
711562306a36Sopenharmony_cistatic int sctp_getsockopt_assoc_ids(struct sock *sk, int len,
711662306a36Sopenharmony_ci				    char __user *optval, int __user *optlen)
711762306a36Sopenharmony_ci{
711862306a36Sopenharmony_ci	struct sctp_sock *sp = sctp_sk(sk);
711962306a36Sopenharmony_ci	struct sctp_association *asoc;
712062306a36Sopenharmony_ci	struct sctp_assoc_ids *ids;
712162306a36Sopenharmony_ci	u32 num = 0;
712262306a36Sopenharmony_ci
712362306a36Sopenharmony_ci	if (sctp_style(sk, TCP))
712462306a36Sopenharmony_ci		return -EOPNOTSUPP;
712562306a36Sopenharmony_ci
712662306a36Sopenharmony_ci	if (len < sizeof(struct sctp_assoc_ids))
712762306a36Sopenharmony_ci		return -EINVAL;
712862306a36Sopenharmony_ci
712962306a36Sopenharmony_ci	list_for_each_entry(asoc, &(sp->ep->asocs), asocs) {
713062306a36Sopenharmony_ci		num++;
713162306a36Sopenharmony_ci	}
713262306a36Sopenharmony_ci
713362306a36Sopenharmony_ci	if (len < sizeof(struct sctp_assoc_ids) + sizeof(sctp_assoc_t) * num)
713462306a36Sopenharmony_ci		return -EINVAL;
713562306a36Sopenharmony_ci
713662306a36Sopenharmony_ci	len = sizeof(struct sctp_assoc_ids) + sizeof(sctp_assoc_t) * num;
713762306a36Sopenharmony_ci
713862306a36Sopenharmony_ci	ids = kmalloc(len, GFP_USER | __GFP_NOWARN);
713962306a36Sopenharmony_ci	if (unlikely(!ids))
714062306a36Sopenharmony_ci		return -ENOMEM;
714162306a36Sopenharmony_ci
714262306a36Sopenharmony_ci	ids->gaids_number_of_ids = num;
714362306a36Sopenharmony_ci	num = 0;
714462306a36Sopenharmony_ci	list_for_each_entry(asoc, &(sp->ep->asocs), asocs) {
714562306a36Sopenharmony_ci		ids->gaids_assoc_id[num++] = asoc->assoc_id;
714662306a36Sopenharmony_ci	}
714762306a36Sopenharmony_ci
714862306a36Sopenharmony_ci	if (put_user(len, optlen) || copy_to_user(optval, ids, len)) {
714962306a36Sopenharmony_ci		kfree(ids);
715062306a36Sopenharmony_ci		return -EFAULT;
715162306a36Sopenharmony_ci	}
715262306a36Sopenharmony_ci
715362306a36Sopenharmony_ci	kfree(ids);
715462306a36Sopenharmony_ci	return 0;
715562306a36Sopenharmony_ci}
715662306a36Sopenharmony_ci
715762306a36Sopenharmony_ci/*
715862306a36Sopenharmony_ci * SCTP_PEER_ADDR_THLDS
715962306a36Sopenharmony_ci *
716062306a36Sopenharmony_ci * This option allows us to fetch the partially failed threshold for one or all
716162306a36Sopenharmony_ci * transports in an association.  See Section 6.1 of:
716262306a36Sopenharmony_ci * http://www.ietf.org/id/draft-nishida-tsvwg-sctp-failover-05.txt
716362306a36Sopenharmony_ci */
716462306a36Sopenharmony_cistatic int sctp_getsockopt_paddr_thresholds(struct sock *sk,
716562306a36Sopenharmony_ci					    char __user *optval, int len,
716662306a36Sopenharmony_ci					    int __user *optlen, bool v2)
716762306a36Sopenharmony_ci{
716862306a36Sopenharmony_ci	struct sctp_paddrthlds_v2 val;
716962306a36Sopenharmony_ci	struct sctp_transport *trans;
717062306a36Sopenharmony_ci	struct sctp_association *asoc;
717162306a36Sopenharmony_ci	int min;
717262306a36Sopenharmony_ci
717362306a36Sopenharmony_ci	min = v2 ? sizeof(val) : sizeof(struct sctp_paddrthlds);
717462306a36Sopenharmony_ci	if (len < min)
717562306a36Sopenharmony_ci		return -EINVAL;
717662306a36Sopenharmony_ci	len = min;
717762306a36Sopenharmony_ci	if (copy_from_user(&val, optval, len))
717862306a36Sopenharmony_ci		return -EFAULT;
717962306a36Sopenharmony_ci
718062306a36Sopenharmony_ci	if (!sctp_is_any(sk, (const union sctp_addr *)&val.spt_address)) {
718162306a36Sopenharmony_ci		trans = sctp_addr_id2transport(sk, &val.spt_address,
718262306a36Sopenharmony_ci					       val.spt_assoc_id);
718362306a36Sopenharmony_ci		if (!trans)
718462306a36Sopenharmony_ci			return -ENOENT;
718562306a36Sopenharmony_ci
718662306a36Sopenharmony_ci		val.spt_pathmaxrxt = trans->pathmaxrxt;
718762306a36Sopenharmony_ci		val.spt_pathpfthld = trans->pf_retrans;
718862306a36Sopenharmony_ci		val.spt_pathcpthld = trans->ps_retrans;
718962306a36Sopenharmony_ci
719062306a36Sopenharmony_ci		goto out;
719162306a36Sopenharmony_ci	}
719262306a36Sopenharmony_ci
719362306a36Sopenharmony_ci	asoc = sctp_id2assoc(sk, val.spt_assoc_id);
719462306a36Sopenharmony_ci	if (!asoc && val.spt_assoc_id != SCTP_FUTURE_ASSOC &&
719562306a36Sopenharmony_ci	    sctp_style(sk, UDP))
719662306a36Sopenharmony_ci		return -EINVAL;
719762306a36Sopenharmony_ci
719862306a36Sopenharmony_ci	if (asoc) {
719962306a36Sopenharmony_ci		val.spt_pathpfthld = asoc->pf_retrans;
720062306a36Sopenharmony_ci		val.spt_pathmaxrxt = asoc->pathmaxrxt;
720162306a36Sopenharmony_ci		val.spt_pathcpthld = asoc->ps_retrans;
720262306a36Sopenharmony_ci	} else {
720362306a36Sopenharmony_ci		struct sctp_sock *sp = sctp_sk(sk);
720462306a36Sopenharmony_ci
720562306a36Sopenharmony_ci		val.spt_pathpfthld = sp->pf_retrans;
720662306a36Sopenharmony_ci		val.spt_pathmaxrxt = sp->pathmaxrxt;
720762306a36Sopenharmony_ci		val.spt_pathcpthld = sp->ps_retrans;
720862306a36Sopenharmony_ci	}
720962306a36Sopenharmony_ci
721062306a36Sopenharmony_ciout:
721162306a36Sopenharmony_ci	if (put_user(len, optlen) || copy_to_user(optval, &val, len))
721262306a36Sopenharmony_ci		return -EFAULT;
721362306a36Sopenharmony_ci
721462306a36Sopenharmony_ci	return 0;
721562306a36Sopenharmony_ci}
721662306a36Sopenharmony_ci
721762306a36Sopenharmony_ci/*
721862306a36Sopenharmony_ci * SCTP_GET_ASSOC_STATS
721962306a36Sopenharmony_ci *
722062306a36Sopenharmony_ci * This option retrieves local per endpoint statistics. It is modeled
722162306a36Sopenharmony_ci * after OpenSolaris' implementation
722262306a36Sopenharmony_ci */
722362306a36Sopenharmony_cistatic int sctp_getsockopt_assoc_stats(struct sock *sk, int len,
722462306a36Sopenharmony_ci				       char __user *optval,
722562306a36Sopenharmony_ci				       int __user *optlen)
722662306a36Sopenharmony_ci{
722762306a36Sopenharmony_ci	struct sctp_assoc_stats sas;
722862306a36Sopenharmony_ci	struct sctp_association *asoc = NULL;
722962306a36Sopenharmony_ci
723062306a36Sopenharmony_ci	/* User must provide at least the assoc id */
723162306a36Sopenharmony_ci	if (len < sizeof(sctp_assoc_t))
723262306a36Sopenharmony_ci		return -EINVAL;
723362306a36Sopenharmony_ci
723462306a36Sopenharmony_ci	/* Allow the struct to grow and fill in as much as possible */
723562306a36Sopenharmony_ci	len = min_t(size_t, len, sizeof(sas));
723662306a36Sopenharmony_ci
723762306a36Sopenharmony_ci	if (copy_from_user(&sas, optval, len))
723862306a36Sopenharmony_ci		return -EFAULT;
723962306a36Sopenharmony_ci
724062306a36Sopenharmony_ci	asoc = sctp_id2assoc(sk, sas.sas_assoc_id);
724162306a36Sopenharmony_ci	if (!asoc)
724262306a36Sopenharmony_ci		return -EINVAL;
724362306a36Sopenharmony_ci
724462306a36Sopenharmony_ci	sas.sas_rtxchunks = asoc->stats.rtxchunks;
724562306a36Sopenharmony_ci	sas.sas_gapcnt = asoc->stats.gapcnt;
724662306a36Sopenharmony_ci	sas.sas_outofseqtsns = asoc->stats.outofseqtsns;
724762306a36Sopenharmony_ci	sas.sas_osacks = asoc->stats.osacks;
724862306a36Sopenharmony_ci	sas.sas_isacks = asoc->stats.isacks;
724962306a36Sopenharmony_ci	sas.sas_octrlchunks = asoc->stats.octrlchunks;
725062306a36Sopenharmony_ci	sas.sas_ictrlchunks = asoc->stats.ictrlchunks;
725162306a36Sopenharmony_ci	sas.sas_oodchunks = asoc->stats.oodchunks;
725262306a36Sopenharmony_ci	sas.sas_iodchunks = asoc->stats.iodchunks;
725362306a36Sopenharmony_ci	sas.sas_ouodchunks = asoc->stats.ouodchunks;
725462306a36Sopenharmony_ci	sas.sas_iuodchunks = asoc->stats.iuodchunks;
725562306a36Sopenharmony_ci	sas.sas_idupchunks = asoc->stats.idupchunks;
725662306a36Sopenharmony_ci	sas.sas_opackets = asoc->stats.opackets;
725762306a36Sopenharmony_ci	sas.sas_ipackets = asoc->stats.ipackets;
725862306a36Sopenharmony_ci
725962306a36Sopenharmony_ci	/* New high max rto observed, will return 0 if not a single
726062306a36Sopenharmony_ci	 * RTO update took place. obs_rto_ipaddr will be bogus
726162306a36Sopenharmony_ci	 * in such a case
726262306a36Sopenharmony_ci	 */
726362306a36Sopenharmony_ci	sas.sas_maxrto = asoc->stats.max_obs_rto;
726462306a36Sopenharmony_ci	memcpy(&sas.sas_obs_rto_ipaddr, &asoc->stats.obs_rto_ipaddr,
726562306a36Sopenharmony_ci		sizeof(struct sockaddr_storage));
726662306a36Sopenharmony_ci
726762306a36Sopenharmony_ci	/* Mark beginning of a new observation period */
726862306a36Sopenharmony_ci	asoc->stats.max_obs_rto = asoc->rto_min;
726962306a36Sopenharmony_ci
727062306a36Sopenharmony_ci	if (put_user(len, optlen))
727162306a36Sopenharmony_ci		return -EFAULT;
727262306a36Sopenharmony_ci
727362306a36Sopenharmony_ci	pr_debug("%s: len:%d, assoc_id:%d\n", __func__, len, sas.sas_assoc_id);
727462306a36Sopenharmony_ci
727562306a36Sopenharmony_ci	if (copy_to_user(optval, &sas, len))
727662306a36Sopenharmony_ci		return -EFAULT;
727762306a36Sopenharmony_ci
727862306a36Sopenharmony_ci	return 0;
727962306a36Sopenharmony_ci}
728062306a36Sopenharmony_ci
728162306a36Sopenharmony_cistatic int sctp_getsockopt_recvrcvinfo(struct sock *sk,	int len,
728262306a36Sopenharmony_ci				       char __user *optval,
728362306a36Sopenharmony_ci				       int __user *optlen)
728462306a36Sopenharmony_ci{
728562306a36Sopenharmony_ci	int val = 0;
728662306a36Sopenharmony_ci
728762306a36Sopenharmony_ci	if (len < sizeof(int))
728862306a36Sopenharmony_ci		return -EINVAL;
728962306a36Sopenharmony_ci
729062306a36Sopenharmony_ci	len = sizeof(int);
729162306a36Sopenharmony_ci	if (sctp_sk(sk)->recvrcvinfo)
729262306a36Sopenharmony_ci		val = 1;
729362306a36Sopenharmony_ci	if (put_user(len, optlen))
729462306a36Sopenharmony_ci		return -EFAULT;
729562306a36Sopenharmony_ci	if (copy_to_user(optval, &val, len))
729662306a36Sopenharmony_ci		return -EFAULT;
729762306a36Sopenharmony_ci
729862306a36Sopenharmony_ci	return 0;
729962306a36Sopenharmony_ci}
730062306a36Sopenharmony_ci
730162306a36Sopenharmony_cistatic int sctp_getsockopt_recvnxtinfo(struct sock *sk,	int len,
730262306a36Sopenharmony_ci				       char __user *optval,
730362306a36Sopenharmony_ci				       int __user *optlen)
730462306a36Sopenharmony_ci{
730562306a36Sopenharmony_ci	int val = 0;
730662306a36Sopenharmony_ci
730762306a36Sopenharmony_ci	if (len < sizeof(int))
730862306a36Sopenharmony_ci		return -EINVAL;
730962306a36Sopenharmony_ci
731062306a36Sopenharmony_ci	len = sizeof(int);
731162306a36Sopenharmony_ci	if (sctp_sk(sk)->recvnxtinfo)
731262306a36Sopenharmony_ci		val = 1;
731362306a36Sopenharmony_ci	if (put_user(len, optlen))
731462306a36Sopenharmony_ci		return -EFAULT;
731562306a36Sopenharmony_ci	if (copy_to_user(optval, &val, len))
731662306a36Sopenharmony_ci		return -EFAULT;
731762306a36Sopenharmony_ci
731862306a36Sopenharmony_ci	return 0;
731962306a36Sopenharmony_ci}
732062306a36Sopenharmony_ci
732162306a36Sopenharmony_cistatic int sctp_getsockopt_pr_supported(struct sock *sk, int len,
732262306a36Sopenharmony_ci					char __user *optval,
732362306a36Sopenharmony_ci					int __user *optlen)
732462306a36Sopenharmony_ci{
732562306a36Sopenharmony_ci	struct sctp_assoc_value params;
732662306a36Sopenharmony_ci	struct sctp_association *asoc;
732762306a36Sopenharmony_ci	int retval = -EFAULT;
732862306a36Sopenharmony_ci
732962306a36Sopenharmony_ci	if (len < sizeof(params)) {
733062306a36Sopenharmony_ci		retval = -EINVAL;
733162306a36Sopenharmony_ci		goto out;
733262306a36Sopenharmony_ci	}
733362306a36Sopenharmony_ci
733462306a36Sopenharmony_ci	len = sizeof(params);
733562306a36Sopenharmony_ci	if (copy_from_user(&params, optval, len))
733662306a36Sopenharmony_ci		goto out;
733762306a36Sopenharmony_ci
733862306a36Sopenharmony_ci	asoc = sctp_id2assoc(sk, params.assoc_id);
733962306a36Sopenharmony_ci	if (!asoc && params.assoc_id != SCTP_FUTURE_ASSOC &&
734062306a36Sopenharmony_ci	    sctp_style(sk, UDP)) {
734162306a36Sopenharmony_ci		retval = -EINVAL;
734262306a36Sopenharmony_ci		goto out;
734362306a36Sopenharmony_ci	}
734462306a36Sopenharmony_ci
734562306a36Sopenharmony_ci	params.assoc_value = asoc ? asoc->peer.prsctp_capable
734662306a36Sopenharmony_ci				  : sctp_sk(sk)->ep->prsctp_enable;
734762306a36Sopenharmony_ci
734862306a36Sopenharmony_ci	if (put_user(len, optlen))
734962306a36Sopenharmony_ci		goto out;
735062306a36Sopenharmony_ci
735162306a36Sopenharmony_ci	if (copy_to_user(optval, &params, len))
735262306a36Sopenharmony_ci		goto out;
735362306a36Sopenharmony_ci
735462306a36Sopenharmony_ci	retval = 0;
735562306a36Sopenharmony_ci
735662306a36Sopenharmony_ciout:
735762306a36Sopenharmony_ci	return retval;
735862306a36Sopenharmony_ci}
735962306a36Sopenharmony_ci
736062306a36Sopenharmony_cistatic int sctp_getsockopt_default_prinfo(struct sock *sk, int len,
736162306a36Sopenharmony_ci					  char __user *optval,
736262306a36Sopenharmony_ci					  int __user *optlen)
736362306a36Sopenharmony_ci{
736462306a36Sopenharmony_ci	struct sctp_default_prinfo info;
736562306a36Sopenharmony_ci	struct sctp_association *asoc;
736662306a36Sopenharmony_ci	int retval = -EFAULT;
736762306a36Sopenharmony_ci
736862306a36Sopenharmony_ci	if (len < sizeof(info)) {
736962306a36Sopenharmony_ci		retval = -EINVAL;
737062306a36Sopenharmony_ci		goto out;
737162306a36Sopenharmony_ci	}
737262306a36Sopenharmony_ci
737362306a36Sopenharmony_ci	len = sizeof(info);
737462306a36Sopenharmony_ci	if (copy_from_user(&info, optval, len))
737562306a36Sopenharmony_ci		goto out;
737662306a36Sopenharmony_ci
737762306a36Sopenharmony_ci	asoc = sctp_id2assoc(sk, info.pr_assoc_id);
737862306a36Sopenharmony_ci	if (!asoc && info.pr_assoc_id != SCTP_FUTURE_ASSOC &&
737962306a36Sopenharmony_ci	    sctp_style(sk, UDP)) {
738062306a36Sopenharmony_ci		retval = -EINVAL;
738162306a36Sopenharmony_ci		goto out;
738262306a36Sopenharmony_ci	}
738362306a36Sopenharmony_ci
738462306a36Sopenharmony_ci	if (asoc) {
738562306a36Sopenharmony_ci		info.pr_policy = SCTP_PR_POLICY(asoc->default_flags);
738662306a36Sopenharmony_ci		info.pr_value = asoc->default_timetolive;
738762306a36Sopenharmony_ci	} else {
738862306a36Sopenharmony_ci		struct sctp_sock *sp = sctp_sk(sk);
738962306a36Sopenharmony_ci
739062306a36Sopenharmony_ci		info.pr_policy = SCTP_PR_POLICY(sp->default_flags);
739162306a36Sopenharmony_ci		info.pr_value = sp->default_timetolive;
739262306a36Sopenharmony_ci	}
739362306a36Sopenharmony_ci
739462306a36Sopenharmony_ci	if (put_user(len, optlen))
739562306a36Sopenharmony_ci		goto out;
739662306a36Sopenharmony_ci
739762306a36Sopenharmony_ci	if (copy_to_user(optval, &info, len))
739862306a36Sopenharmony_ci		goto out;
739962306a36Sopenharmony_ci
740062306a36Sopenharmony_ci	retval = 0;
740162306a36Sopenharmony_ci
740262306a36Sopenharmony_ciout:
740362306a36Sopenharmony_ci	return retval;
740462306a36Sopenharmony_ci}
740562306a36Sopenharmony_ci
740662306a36Sopenharmony_cistatic int sctp_getsockopt_pr_assocstatus(struct sock *sk, int len,
740762306a36Sopenharmony_ci					  char __user *optval,
740862306a36Sopenharmony_ci					  int __user *optlen)
740962306a36Sopenharmony_ci{
741062306a36Sopenharmony_ci	struct sctp_prstatus params;
741162306a36Sopenharmony_ci	struct sctp_association *asoc;
741262306a36Sopenharmony_ci	int policy;
741362306a36Sopenharmony_ci	int retval = -EINVAL;
741462306a36Sopenharmony_ci
741562306a36Sopenharmony_ci	if (len < sizeof(params))
741662306a36Sopenharmony_ci		goto out;
741762306a36Sopenharmony_ci
741862306a36Sopenharmony_ci	len = sizeof(params);
741962306a36Sopenharmony_ci	if (copy_from_user(&params, optval, len)) {
742062306a36Sopenharmony_ci		retval = -EFAULT;
742162306a36Sopenharmony_ci		goto out;
742262306a36Sopenharmony_ci	}
742362306a36Sopenharmony_ci
742462306a36Sopenharmony_ci	policy = params.sprstat_policy;
742562306a36Sopenharmony_ci	if (!policy || (policy & ~(SCTP_PR_SCTP_MASK | SCTP_PR_SCTP_ALL)) ||
742662306a36Sopenharmony_ci	    ((policy & SCTP_PR_SCTP_ALL) && (policy & SCTP_PR_SCTP_MASK)))
742762306a36Sopenharmony_ci		goto out;
742862306a36Sopenharmony_ci
742962306a36Sopenharmony_ci	asoc = sctp_id2assoc(sk, params.sprstat_assoc_id);
743062306a36Sopenharmony_ci	if (!asoc)
743162306a36Sopenharmony_ci		goto out;
743262306a36Sopenharmony_ci
743362306a36Sopenharmony_ci	if (policy == SCTP_PR_SCTP_ALL) {
743462306a36Sopenharmony_ci		params.sprstat_abandoned_unsent = 0;
743562306a36Sopenharmony_ci		params.sprstat_abandoned_sent = 0;
743662306a36Sopenharmony_ci		for (policy = 0; policy <= SCTP_PR_INDEX(MAX); policy++) {
743762306a36Sopenharmony_ci			params.sprstat_abandoned_unsent +=
743862306a36Sopenharmony_ci				asoc->abandoned_unsent[policy];
743962306a36Sopenharmony_ci			params.sprstat_abandoned_sent +=
744062306a36Sopenharmony_ci				asoc->abandoned_sent[policy];
744162306a36Sopenharmony_ci		}
744262306a36Sopenharmony_ci	} else {
744362306a36Sopenharmony_ci		params.sprstat_abandoned_unsent =
744462306a36Sopenharmony_ci			asoc->abandoned_unsent[__SCTP_PR_INDEX(policy)];
744562306a36Sopenharmony_ci		params.sprstat_abandoned_sent =
744662306a36Sopenharmony_ci			asoc->abandoned_sent[__SCTP_PR_INDEX(policy)];
744762306a36Sopenharmony_ci	}
744862306a36Sopenharmony_ci
744962306a36Sopenharmony_ci	if (put_user(len, optlen)) {
745062306a36Sopenharmony_ci		retval = -EFAULT;
745162306a36Sopenharmony_ci		goto out;
745262306a36Sopenharmony_ci	}
745362306a36Sopenharmony_ci
745462306a36Sopenharmony_ci	if (copy_to_user(optval, &params, len)) {
745562306a36Sopenharmony_ci		retval = -EFAULT;
745662306a36Sopenharmony_ci		goto out;
745762306a36Sopenharmony_ci	}
745862306a36Sopenharmony_ci
745962306a36Sopenharmony_ci	retval = 0;
746062306a36Sopenharmony_ci
746162306a36Sopenharmony_ciout:
746262306a36Sopenharmony_ci	return retval;
746362306a36Sopenharmony_ci}
746462306a36Sopenharmony_ci
746562306a36Sopenharmony_cistatic int sctp_getsockopt_pr_streamstatus(struct sock *sk, int len,
746662306a36Sopenharmony_ci					   char __user *optval,
746762306a36Sopenharmony_ci					   int __user *optlen)
746862306a36Sopenharmony_ci{
746962306a36Sopenharmony_ci	struct sctp_stream_out_ext *streamoute;
747062306a36Sopenharmony_ci	struct sctp_association *asoc;
747162306a36Sopenharmony_ci	struct sctp_prstatus params;
747262306a36Sopenharmony_ci	int retval = -EINVAL;
747362306a36Sopenharmony_ci	int policy;
747462306a36Sopenharmony_ci
747562306a36Sopenharmony_ci	if (len < sizeof(params))
747662306a36Sopenharmony_ci		goto out;
747762306a36Sopenharmony_ci
747862306a36Sopenharmony_ci	len = sizeof(params);
747962306a36Sopenharmony_ci	if (copy_from_user(&params, optval, len)) {
748062306a36Sopenharmony_ci		retval = -EFAULT;
748162306a36Sopenharmony_ci		goto out;
748262306a36Sopenharmony_ci	}
748362306a36Sopenharmony_ci
748462306a36Sopenharmony_ci	policy = params.sprstat_policy;
748562306a36Sopenharmony_ci	if (!policy || (policy & ~(SCTP_PR_SCTP_MASK | SCTP_PR_SCTP_ALL)) ||
748662306a36Sopenharmony_ci	    ((policy & SCTP_PR_SCTP_ALL) && (policy & SCTP_PR_SCTP_MASK)))
748762306a36Sopenharmony_ci		goto out;
748862306a36Sopenharmony_ci
748962306a36Sopenharmony_ci	asoc = sctp_id2assoc(sk, params.sprstat_assoc_id);
749062306a36Sopenharmony_ci	if (!asoc || params.sprstat_sid >= asoc->stream.outcnt)
749162306a36Sopenharmony_ci		goto out;
749262306a36Sopenharmony_ci
749362306a36Sopenharmony_ci	streamoute = SCTP_SO(&asoc->stream, params.sprstat_sid)->ext;
749462306a36Sopenharmony_ci	if (!streamoute) {
749562306a36Sopenharmony_ci		/* Not allocated yet, means all stats are 0 */
749662306a36Sopenharmony_ci		params.sprstat_abandoned_unsent = 0;
749762306a36Sopenharmony_ci		params.sprstat_abandoned_sent = 0;
749862306a36Sopenharmony_ci		retval = 0;
749962306a36Sopenharmony_ci		goto out;
750062306a36Sopenharmony_ci	}
750162306a36Sopenharmony_ci
750262306a36Sopenharmony_ci	if (policy == SCTP_PR_SCTP_ALL) {
750362306a36Sopenharmony_ci		params.sprstat_abandoned_unsent = 0;
750462306a36Sopenharmony_ci		params.sprstat_abandoned_sent = 0;
750562306a36Sopenharmony_ci		for (policy = 0; policy <= SCTP_PR_INDEX(MAX); policy++) {
750662306a36Sopenharmony_ci			params.sprstat_abandoned_unsent +=
750762306a36Sopenharmony_ci				streamoute->abandoned_unsent[policy];
750862306a36Sopenharmony_ci			params.sprstat_abandoned_sent +=
750962306a36Sopenharmony_ci				streamoute->abandoned_sent[policy];
751062306a36Sopenharmony_ci		}
751162306a36Sopenharmony_ci	} else {
751262306a36Sopenharmony_ci		params.sprstat_abandoned_unsent =
751362306a36Sopenharmony_ci			streamoute->abandoned_unsent[__SCTP_PR_INDEX(policy)];
751462306a36Sopenharmony_ci		params.sprstat_abandoned_sent =
751562306a36Sopenharmony_ci			streamoute->abandoned_sent[__SCTP_PR_INDEX(policy)];
751662306a36Sopenharmony_ci	}
751762306a36Sopenharmony_ci
751862306a36Sopenharmony_ci	if (put_user(len, optlen) || copy_to_user(optval, &params, len)) {
751962306a36Sopenharmony_ci		retval = -EFAULT;
752062306a36Sopenharmony_ci		goto out;
752162306a36Sopenharmony_ci	}
752262306a36Sopenharmony_ci
752362306a36Sopenharmony_ci	retval = 0;
752462306a36Sopenharmony_ci
752562306a36Sopenharmony_ciout:
752662306a36Sopenharmony_ci	return retval;
752762306a36Sopenharmony_ci}
752862306a36Sopenharmony_ci
752962306a36Sopenharmony_cistatic int sctp_getsockopt_reconfig_supported(struct sock *sk, int len,
753062306a36Sopenharmony_ci					      char __user *optval,
753162306a36Sopenharmony_ci					      int __user *optlen)
753262306a36Sopenharmony_ci{
753362306a36Sopenharmony_ci	struct sctp_assoc_value params;
753462306a36Sopenharmony_ci	struct sctp_association *asoc;
753562306a36Sopenharmony_ci	int retval = -EFAULT;
753662306a36Sopenharmony_ci
753762306a36Sopenharmony_ci	if (len < sizeof(params)) {
753862306a36Sopenharmony_ci		retval = -EINVAL;
753962306a36Sopenharmony_ci		goto out;
754062306a36Sopenharmony_ci	}
754162306a36Sopenharmony_ci
754262306a36Sopenharmony_ci	len = sizeof(params);
754362306a36Sopenharmony_ci	if (copy_from_user(&params, optval, len))
754462306a36Sopenharmony_ci		goto out;
754562306a36Sopenharmony_ci
754662306a36Sopenharmony_ci	asoc = sctp_id2assoc(sk, params.assoc_id);
754762306a36Sopenharmony_ci	if (!asoc && params.assoc_id != SCTP_FUTURE_ASSOC &&
754862306a36Sopenharmony_ci	    sctp_style(sk, UDP)) {
754962306a36Sopenharmony_ci		retval = -EINVAL;
755062306a36Sopenharmony_ci		goto out;
755162306a36Sopenharmony_ci	}
755262306a36Sopenharmony_ci
755362306a36Sopenharmony_ci	params.assoc_value = asoc ? asoc->peer.reconf_capable
755462306a36Sopenharmony_ci				  : sctp_sk(sk)->ep->reconf_enable;
755562306a36Sopenharmony_ci
755662306a36Sopenharmony_ci	if (put_user(len, optlen))
755762306a36Sopenharmony_ci		goto out;
755862306a36Sopenharmony_ci
755962306a36Sopenharmony_ci	if (copy_to_user(optval, &params, len))
756062306a36Sopenharmony_ci		goto out;
756162306a36Sopenharmony_ci
756262306a36Sopenharmony_ci	retval = 0;
756362306a36Sopenharmony_ci
756462306a36Sopenharmony_ciout:
756562306a36Sopenharmony_ci	return retval;
756662306a36Sopenharmony_ci}
756762306a36Sopenharmony_ci
756862306a36Sopenharmony_cistatic int sctp_getsockopt_enable_strreset(struct sock *sk, int len,
756962306a36Sopenharmony_ci					   char __user *optval,
757062306a36Sopenharmony_ci					   int __user *optlen)
757162306a36Sopenharmony_ci{
757262306a36Sopenharmony_ci	struct sctp_assoc_value params;
757362306a36Sopenharmony_ci	struct sctp_association *asoc;
757462306a36Sopenharmony_ci	int retval = -EFAULT;
757562306a36Sopenharmony_ci
757662306a36Sopenharmony_ci	if (len < sizeof(params)) {
757762306a36Sopenharmony_ci		retval = -EINVAL;
757862306a36Sopenharmony_ci		goto out;
757962306a36Sopenharmony_ci	}
758062306a36Sopenharmony_ci
758162306a36Sopenharmony_ci	len = sizeof(params);
758262306a36Sopenharmony_ci	if (copy_from_user(&params, optval, len))
758362306a36Sopenharmony_ci		goto out;
758462306a36Sopenharmony_ci
758562306a36Sopenharmony_ci	asoc = sctp_id2assoc(sk, params.assoc_id);
758662306a36Sopenharmony_ci	if (!asoc && params.assoc_id != SCTP_FUTURE_ASSOC &&
758762306a36Sopenharmony_ci	    sctp_style(sk, UDP)) {
758862306a36Sopenharmony_ci		retval = -EINVAL;
758962306a36Sopenharmony_ci		goto out;
759062306a36Sopenharmony_ci	}
759162306a36Sopenharmony_ci
759262306a36Sopenharmony_ci	params.assoc_value = asoc ? asoc->strreset_enable
759362306a36Sopenharmony_ci				  : sctp_sk(sk)->ep->strreset_enable;
759462306a36Sopenharmony_ci
759562306a36Sopenharmony_ci	if (put_user(len, optlen))
759662306a36Sopenharmony_ci		goto out;
759762306a36Sopenharmony_ci
759862306a36Sopenharmony_ci	if (copy_to_user(optval, &params, len))
759962306a36Sopenharmony_ci		goto out;
760062306a36Sopenharmony_ci
760162306a36Sopenharmony_ci	retval = 0;
760262306a36Sopenharmony_ci
760362306a36Sopenharmony_ciout:
760462306a36Sopenharmony_ci	return retval;
760562306a36Sopenharmony_ci}
760662306a36Sopenharmony_ci
760762306a36Sopenharmony_cistatic int sctp_getsockopt_scheduler(struct sock *sk, int len,
760862306a36Sopenharmony_ci				     char __user *optval,
760962306a36Sopenharmony_ci				     int __user *optlen)
761062306a36Sopenharmony_ci{
761162306a36Sopenharmony_ci	struct sctp_assoc_value params;
761262306a36Sopenharmony_ci	struct sctp_association *asoc;
761362306a36Sopenharmony_ci	int retval = -EFAULT;
761462306a36Sopenharmony_ci
761562306a36Sopenharmony_ci	if (len < sizeof(params)) {
761662306a36Sopenharmony_ci		retval = -EINVAL;
761762306a36Sopenharmony_ci		goto out;
761862306a36Sopenharmony_ci	}
761962306a36Sopenharmony_ci
762062306a36Sopenharmony_ci	len = sizeof(params);
762162306a36Sopenharmony_ci	if (copy_from_user(&params, optval, len))
762262306a36Sopenharmony_ci		goto out;
762362306a36Sopenharmony_ci
762462306a36Sopenharmony_ci	asoc = sctp_id2assoc(sk, params.assoc_id);
762562306a36Sopenharmony_ci	if (!asoc && params.assoc_id != SCTP_FUTURE_ASSOC &&
762662306a36Sopenharmony_ci	    sctp_style(sk, UDP)) {
762762306a36Sopenharmony_ci		retval = -EINVAL;
762862306a36Sopenharmony_ci		goto out;
762962306a36Sopenharmony_ci	}
763062306a36Sopenharmony_ci
763162306a36Sopenharmony_ci	params.assoc_value = asoc ? sctp_sched_get_sched(asoc)
763262306a36Sopenharmony_ci				  : sctp_sk(sk)->default_ss;
763362306a36Sopenharmony_ci
763462306a36Sopenharmony_ci	if (put_user(len, optlen))
763562306a36Sopenharmony_ci		goto out;
763662306a36Sopenharmony_ci
763762306a36Sopenharmony_ci	if (copy_to_user(optval, &params, len))
763862306a36Sopenharmony_ci		goto out;
763962306a36Sopenharmony_ci
764062306a36Sopenharmony_ci	retval = 0;
764162306a36Sopenharmony_ci
764262306a36Sopenharmony_ciout:
764362306a36Sopenharmony_ci	return retval;
764462306a36Sopenharmony_ci}
764562306a36Sopenharmony_ci
764662306a36Sopenharmony_cistatic int sctp_getsockopt_scheduler_value(struct sock *sk, int len,
764762306a36Sopenharmony_ci					   char __user *optval,
764862306a36Sopenharmony_ci					   int __user *optlen)
764962306a36Sopenharmony_ci{
765062306a36Sopenharmony_ci	struct sctp_stream_value params;
765162306a36Sopenharmony_ci	struct sctp_association *asoc;
765262306a36Sopenharmony_ci	int retval = -EFAULT;
765362306a36Sopenharmony_ci
765462306a36Sopenharmony_ci	if (len < sizeof(params)) {
765562306a36Sopenharmony_ci		retval = -EINVAL;
765662306a36Sopenharmony_ci		goto out;
765762306a36Sopenharmony_ci	}
765862306a36Sopenharmony_ci
765962306a36Sopenharmony_ci	len = sizeof(params);
766062306a36Sopenharmony_ci	if (copy_from_user(&params, optval, len))
766162306a36Sopenharmony_ci		goto out;
766262306a36Sopenharmony_ci
766362306a36Sopenharmony_ci	asoc = sctp_id2assoc(sk, params.assoc_id);
766462306a36Sopenharmony_ci	if (!asoc) {
766562306a36Sopenharmony_ci		retval = -EINVAL;
766662306a36Sopenharmony_ci		goto out;
766762306a36Sopenharmony_ci	}
766862306a36Sopenharmony_ci
766962306a36Sopenharmony_ci	retval = sctp_sched_get_value(asoc, params.stream_id,
767062306a36Sopenharmony_ci				      &params.stream_value);
767162306a36Sopenharmony_ci	if (retval)
767262306a36Sopenharmony_ci		goto out;
767362306a36Sopenharmony_ci
767462306a36Sopenharmony_ci	if (put_user(len, optlen)) {
767562306a36Sopenharmony_ci		retval = -EFAULT;
767662306a36Sopenharmony_ci		goto out;
767762306a36Sopenharmony_ci	}
767862306a36Sopenharmony_ci
767962306a36Sopenharmony_ci	if (copy_to_user(optval, &params, len)) {
768062306a36Sopenharmony_ci		retval = -EFAULT;
768162306a36Sopenharmony_ci		goto out;
768262306a36Sopenharmony_ci	}
768362306a36Sopenharmony_ci
768462306a36Sopenharmony_ciout:
768562306a36Sopenharmony_ci	return retval;
768662306a36Sopenharmony_ci}
768762306a36Sopenharmony_ci
768862306a36Sopenharmony_cistatic int sctp_getsockopt_interleaving_supported(struct sock *sk, int len,
768962306a36Sopenharmony_ci						  char __user *optval,
769062306a36Sopenharmony_ci						  int __user *optlen)
769162306a36Sopenharmony_ci{
769262306a36Sopenharmony_ci	struct sctp_assoc_value params;
769362306a36Sopenharmony_ci	struct sctp_association *asoc;
769462306a36Sopenharmony_ci	int retval = -EFAULT;
769562306a36Sopenharmony_ci
769662306a36Sopenharmony_ci	if (len < sizeof(params)) {
769762306a36Sopenharmony_ci		retval = -EINVAL;
769862306a36Sopenharmony_ci		goto out;
769962306a36Sopenharmony_ci	}
770062306a36Sopenharmony_ci
770162306a36Sopenharmony_ci	len = sizeof(params);
770262306a36Sopenharmony_ci	if (copy_from_user(&params, optval, len))
770362306a36Sopenharmony_ci		goto out;
770462306a36Sopenharmony_ci
770562306a36Sopenharmony_ci	asoc = sctp_id2assoc(sk, params.assoc_id);
770662306a36Sopenharmony_ci	if (!asoc && params.assoc_id != SCTP_FUTURE_ASSOC &&
770762306a36Sopenharmony_ci	    sctp_style(sk, UDP)) {
770862306a36Sopenharmony_ci		retval = -EINVAL;
770962306a36Sopenharmony_ci		goto out;
771062306a36Sopenharmony_ci	}
771162306a36Sopenharmony_ci
771262306a36Sopenharmony_ci	params.assoc_value = asoc ? asoc->peer.intl_capable
771362306a36Sopenharmony_ci				  : sctp_sk(sk)->ep->intl_enable;
771462306a36Sopenharmony_ci
771562306a36Sopenharmony_ci	if (put_user(len, optlen))
771662306a36Sopenharmony_ci		goto out;
771762306a36Sopenharmony_ci
771862306a36Sopenharmony_ci	if (copy_to_user(optval, &params, len))
771962306a36Sopenharmony_ci		goto out;
772062306a36Sopenharmony_ci
772162306a36Sopenharmony_ci	retval = 0;
772262306a36Sopenharmony_ci
772362306a36Sopenharmony_ciout:
772462306a36Sopenharmony_ci	return retval;
772562306a36Sopenharmony_ci}
772662306a36Sopenharmony_ci
772762306a36Sopenharmony_cistatic int sctp_getsockopt_reuse_port(struct sock *sk, int len,
772862306a36Sopenharmony_ci				      char __user *optval,
772962306a36Sopenharmony_ci				      int __user *optlen)
773062306a36Sopenharmony_ci{
773162306a36Sopenharmony_ci	int val;
773262306a36Sopenharmony_ci
773362306a36Sopenharmony_ci	if (len < sizeof(int))
773462306a36Sopenharmony_ci		return -EINVAL;
773562306a36Sopenharmony_ci
773662306a36Sopenharmony_ci	len = sizeof(int);
773762306a36Sopenharmony_ci	val = sctp_sk(sk)->reuse;
773862306a36Sopenharmony_ci	if (put_user(len, optlen))
773962306a36Sopenharmony_ci		return -EFAULT;
774062306a36Sopenharmony_ci
774162306a36Sopenharmony_ci	if (copy_to_user(optval, &val, len))
774262306a36Sopenharmony_ci		return -EFAULT;
774362306a36Sopenharmony_ci
774462306a36Sopenharmony_ci	return 0;
774562306a36Sopenharmony_ci}
774662306a36Sopenharmony_ci
774762306a36Sopenharmony_cistatic int sctp_getsockopt_event(struct sock *sk, int len, char __user *optval,
774862306a36Sopenharmony_ci				 int __user *optlen)
774962306a36Sopenharmony_ci{
775062306a36Sopenharmony_ci	struct sctp_association *asoc;
775162306a36Sopenharmony_ci	struct sctp_event param;
775262306a36Sopenharmony_ci	__u16 subscribe;
775362306a36Sopenharmony_ci
775462306a36Sopenharmony_ci	if (len < sizeof(param))
775562306a36Sopenharmony_ci		return -EINVAL;
775662306a36Sopenharmony_ci
775762306a36Sopenharmony_ci	len = sizeof(param);
775862306a36Sopenharmony_ci	if (copy_from_user(&param, optval, len))
775962306a36Sopenharmony_ci		return -EFAULT;
776062306a36Sopenharmony_ci
776162306a36Sopenharmony_ci	if (param.se_type < SCTP_SN_TYPE_BASE ||
776262306a36Sopenharmony_ci	    param.se_type > SCTP_SN_TYPE_MAX)
776362306a36Sopenharmony_ci		return -EINVAL;
776462306a36Sopenharmony_ci
776562306a36Sopenharmony_ci	asoc = sctp_id2assoc(sk, param.se_assoc_id);
776662306a36Sopenharmony_ci	if (!asoc && param.se_assoc_id != SCTP_FUTURE_ASSOC &&
776762306a36Sopenharmony_ci	    sctp_style(sk, UDP))
776862306a36Sopenharmony_ci		return -EINVAL;
776962306a36Sopenharmony_ci
777062306a36Sopenharmony_ci	subscribe = asoc ? asoc->subscribe : sctp_sk(sk)->subscribe;
777162306a36Sopenharmony_ci	param.se_on = sctp_ulpevent_type_enabled(subscribe, param.se_type);
777262306a36Sopenharmony_ci
777362306a36Sopenharmony_ci	if (put_user(len, optlen))
777462306a36Sopenharmony_ci		return -EFAULT;
777562306a36Sopenharmony_ci
777662306a36Sopenharmony_ci	if (copy_to_user(optval, &param, len))
777762306a36Sopenharmony_ci		return -EFAULT;
777862306a36Sopenharmony_ci
777962306a36Sopenharmony_ci	return 0;
778062306a36Sopenharmony_ci}
778162306a36Sopenharmony_ci
778262306a36Sopenharmony_cistatic int sctp_getsockopt_asconf_supported(struct sock *sk, int len,
778362306a36Sopenharmony_ci					    char __user *optval,
778462306a36Sopenharmony_ci					    int __user *optlen)
778562306a36Sopenharmony_ci{
778662306a36Sopenharmony_ci	struct sctp_assoc_value params;
778762306a36Sopenharmony_ci	struct sctp_association *asoc;
778862306a36Sopenharmony_ci	int retval = -EFAULT;
778962306a36Sopenharmony_ci
779062306a36Sopenharmony_ci	if (len < sizeof(params)) {
779162306a36Sopenharmony_ci		retval = -EINVAL;
779262306a36Sopenharmony_ci		goto out;
779362306a36Sopenharmony_ci	}
779462306a36Sopenharmony_ci
779562306a36Sopenharmony_ci	len = sizeof(params);
779662306a36Sopenharmony_ci	if (copy_from_user(&params, optval, len))
779762306a36Sopenharmony_ci		goto out;
779862306a36Sopenharmony_ci
779962306a36Sopenharmony_ci	asoc = sctp_id2assoc(sk, params.assoc_id);
780062306a36Sopenharmony_ci	if (!asoc && params.assoc_id != SCTP_FUTURE_ASSOC &&
780162306a36Sopenharmony_ci	    sctp_style(sk, UDP)) {
780262306a36Sopenharmony_ci		retval = -EINVAL;
780362306a36Sopenharmony_ci		goto out;
780462306a36Sopenharmony_ci	}
780562306a36Sopenharmony_ci
780662306a36Sopenharmony_ci	params.assoc_value = asoc ? asoc->peer.asconf_capable
780762306a36Sopenharmony_ci				  : sctp_sk(sk)->ep->asconf_enable;
780862306a36Sopenharmony_ci
780962306a36Sopenharmony_ci	if (put_user(len, optlen))
781062306a36Sopenharmony_ci		goto out;
781162306a36Sopenharmony_ci
781262306a36Sopenharmony_ci	if (copy_to_user(optval, &params, len))
781362306a36Sopenharmony_ci		goto out;
781462306a36Sopenharmony_ci
781562306a36Sopenharmony_ci	retval = 0;
781662306a36Sopenharmony_ci
781762306a36Sopenharmony_ciout:
781862306a36Sopenharmony_ci	return retval;
781962306a36Sopenharmony_ci}
782062306a36Sopenharmony_ci
782162306a36Sopenharmony_cistatic int sctp_getsockopt_auth_supported(struct sock *sk, int len,
782262306a36Sopenharmony_ci					  char __user *optval,
782362306a36Sopenharmony_ci					  int __user *optlen)
782462306a36Sopenharmony_ci{
782562306a36Sopenharmony_ci	struct sctp_assoc_value params;
782662306a36Sopenharmony_ci	struct sctp_association *asoc;
782762306a36Sopenharmony_ci	int retval = -EFAULT;
782862306a36Sopenharmony_ci
782962306a36Sopenharmony_ci	if (len < sizeof(params)) {
783062306a36Sopenharmony_ci		retval = -EINVAL;
783162306a36Sopenharmony_ci		goto out;
783262306a36Sopenharmony_ci	}
783362306a36Sopenharmony_ci
783462306a36Sopenharmony_ci	len = sizeof(params);
783562306a36Sopenharmony_ci	if (copy_from_user(&params, optval, len))
783662306a36Sopenharmony_ci		goto out;
783762306a36Sopenharmony_ci
783862306a36Sopenharmony_ci	asoc = sctp_id2assoc(sk, params.assoc_id);
783962306a36Sopenharmony_ci	if (!asoc && params.assoc_id != SCTP_FUTURE_ASSOC &&
784062306a36Sopenharmony_ci	    sctp_style(sk, UDP)) {
784162306a36Sopenharmony_ci		retval = -EINVAL;
784262306a36Sopenharmony_ci		goto out;
784362306a36Sopenharmony_ci	}
784462306a36Sopenharmony_ci
784562306a36Sopenharmony_ci	params.assoc_value = asoc ? asoc->peer.auth_capable
784662306a36Sopenharmony_ci				  : sctp_sk(sk)->ep->auth_enable;
784762306a36Sopenharmony_ci
784862306a36Sopenharmony_ci	if (put_user(len, optlen))
784962306a36Sopenharmony_ci		goto out;
785062306a36Sopenharmony_ci
785162306a36Sopenharmony_ci	if (copy_to_user(optval, &params, len))
785262306a36Sopenharmony_ci		goto out;
785362306a36Sopenharmony_ci
785462306a36Sopenharmony_ci	retval = 0;
785562306a36Sopenharmony_ci
785662306a36Sopenharmony_ciout:
785762306a36Sopenharmony_ci	return retval;
785862306a36Sopenharmony_ci}
785962306a36Sopenharmony_ci
786062306a36Sopenharmony_cistatic int sctp_getsockopt_ecn_supported(struct sock *sk, int len,
786162306a36Sopenharmony_ci					 char __user *optval,
786262306a36Sopenharmony_ci					 int __user *optlen)
786362306a36Sopenharmony_ci{
786462306a36Sopenharmony_ci	struct sctp_assoc_value params;
786562306a36Sopenharmony_ci	struct sctp_association *asoc;
786662306a36Sopenharmony_ci	int retval = -EFAULT;
786762306a36Sopenharmony_ci
786862306a36Sopenharmony_ci	if (len < sizeof(params)) {
786962306a36Sopenharmony_ci		retval = -EINVAL;
787062306a36Sopenharmony_ci		goto out;
787162306a36Sopenharmony_ci	}
787262306a36Sopenharmony_ci
787362306a36Sopenharmony_ci	len = sizeof(params);
787462306a36Sopenharmony_ci	if (copy_from_user(&params, optval, len))
787562306a36Sopenharmony_ci		goto out;
787662306a36Sopenharmony_ci
787762306a36Sopenharmony_ci	asoc = sctp_id2assoc(sk, params.assoc_id);
787862306a36Sopenharmony_ci	if (!asoc && params.assoc_id != SCTP_FUTURE_ASSOC &&
787962306a36Sopenharmony_ci	    sctp_style(sk, UDP)) {
788062306a36Sopenharmony_ci		retval = -EINVAL;
788162306a36Sopenharmony_ci		goto out;
788262306a36Sopenharmony_ci	}
788362306a36Sopenharmony_ci
788462306a36Sopenharmony_ci	params.assoc_value = asoc ? asoc->peer.ecn_capable
788562306a36Sopenharmony_ci				  : sctp_sk(sk)->ep->ecn_enable;
788662306a36Sopenharmony_ci
788762306a36Sopenharmony_ci	if (put_user(len, optlen))
788862306a36Sopenharmony_ci		goto out;
788962306a36Sopenharmony_ci
789062306a36Sopenharmony_ci	if (copy_to_user(optval, &params, len))
789162306a36Sopenharmony_ci		goto out;
789262306a36Sopenharmony_ci
789362306a36Sopenharmony_ci	retval = 0;
789462306a36Sopenharmony_ci
789562306a36Sopenharmony_ciout:
789662306a36Sopenharmony_ci	return retval;
789762306a36Sopenharmony_ci}
789862306a36Sopenharmony_ci
789962306a36Sopenharmony_cistatic int sctp_getsockopt_pf_expose(struct sock *sk, int len,
790062306a36Sopenharmony_ci				     char __user *optval,
790162306a36Sopenharmony_ci				     int __user *optlen)
790262306a36Sopenharmony_ci{
790362306a36Sopenharmony_ci	struct sctp_assoc_value params;
790462306a36Sopenharmony_ci	struct sctp_association *asoc;
790562306a36Sopenharmony_ci	int retval = -EFAULT;
790662306a36Sopenharmony_ci
790762306a36Sopenharmony_ci	if (len < sizeof(params)) {
790862306a36Sopenharmony_ci		retval = -EINVAL;
790962306a36Sopenharmony_ci		goto out;
791062306a36Sopenharmony_ci	}
791162306a36Sopenharmony_ci
791262306a36Sopenharmony_ci	len = sizeof(params);
791362306a36Sopenharmony_ci	if (copy_from_user(&params, optval, len))
791462306a36Sopenharmony_ci		goto out;
791562306a36Sopenharmony_ci
791662306a36Sopenharmony_ci	asoc = sctp_id2assoc(sk, params.assoc_id);
791762306a36Sopenharmony_ci	if (!asoc && params.assoc_id != SCTP_FUTURE_ASSOC &&
791862306a36Sopenharmony_ci	    sctp_style(sk, UDP)) {
791962306a36Sopenharmony_ci		retval = -EINVAL;
792062306a36Sopenharmony_ci		goto out;
792162306a36Sopenharmony_ci	}
792262306a36Sopenharmony_ci
792362306a36Sopenharmony_ci	params.assoc_value = asoc ? asoc->pf_expose
792462306a36Sopenharmony_ci				  : sctp_sk(sk)->pf_expose;
792562306a36Sopenharmony_ci
792662306a36Sopenharmony_ci	if (put_user(len, optlen))
792762306a36Sopenharmony_ci		goto out;
792862306a36Sopenharmony_ci
792962306a36Sopenharmony_ci	if (copy_to_user(optval, &params, len))
793062306a36Sopenharmony_ci		goto out;
793162306a36Sopenharmony_ci
793262306a36Sopenharmony_ci	retval = 0;
793362306a36Sopenharmony_ci
793462306a36Sopenharmony_ciout:
793562306a36Sopenharmony_ci	return retval;
793662306a36Sopenharmony_ci}
793762306a36Sopenharmony_ci
793862306a36Sopenharmony_cistatic int sctp_getsockopt_encap_port(struct sock *sk, int len,
793962306a36Sopenharmony_ci				      char __user *optval, int __user *optlen)
794062306a36Sopenharmony_ci{
794162306a36Sopenharmony_ci	struct sctp_association *asoc;
794262306a36Sopenharmony_ci	struct sctp_udpencaps encap;
794362306a36Sopenharmony_ci	struct sctp_transport *t;
794462306a36Sopenharmony_ci	__be16 encap_port;
794562306a36Sopenharmony_ci
794662306a36Sopenharmony_ci	if (len < sizeof(encap))
794762306a36Sopenharmony_ci		return -EINVAL;
794862306a36Sopenharmony_ci
794962306a36Sopenharmony_ci	len = sizeof(encap);
795062306a36Sopenharmony_ci	if (copy_from_user(&encap, optval, len))
795162306a36Sopenharmony_ci		return -EFAULT;
795262306a36Sopenharmony_ci
795362306a36Sopenharmony_ci	/* If an address other than INADDR_ANY is specified, and
795462306a36Sopenharmony_ci	 * no transport is found, then the request is invalid.
795562306a36Sopenharmony_ci	 */
795662306a36Sopenharmony_ci	if (!sctp_is_any(sk, (union sctp_addr *)&encap.sue_address)) {
795762306a36Sopenharmony_ci		t = sctp_addr_id2transport(sk, &encap.sue_address,
795862306a36Sopenharmony_ci					   encap.sue_assoc_id);
795962306a36Sopenharmony_ci		if (!t) {
796062306a36Sopenharmony_ci			pr_debug("%s: failed no transport\n", __func__);
796162306a36Sopenharmony_ci			return -EINVAL;
796262306a36Sopenharmony_ci		}
796362306a36Sopenharmony_ci
796462306a36Sopenharmony_ci		encap_port = t->encap_port;
796562306a36Sopenharmony_ci		goto out;
796662306a36Sopenharmony_ci	}
796762306a36Sopenharmony_ci
796862306a36Sopenharmony_ci	/* Get association, if assoc_id != SCTP_FUTURE_ASSOC and the
796962306a36Sopenharmony_ci	 * socket is a one to many style socket, and an association
797062306a36Sopenharmony_ci	 * was not found, then the id was invalid.
797162306a36Sopenharmony_ci	 */
797262306a36Sopenharmony_ci	asoc = sctp_id2assoc(sk, encap.sue_assoc_id);
797362306a36Sopenharmony_ci	if (!asoc && encap.sue_assoc_id != SCTP_FUTURE_ASSOC &&
797462306a36Sopenharmony_ci	    sctp_style(sk, UDP)) {
797562306a36Sopenharmony_ci		pr_debug("%s: failed no association\n", __func__);
797662306a36Sopenharmony_ci		return -EINVAL;
797762306a36Sopenharmony_ci	}
797862306a36Sopenharmony_ci
797962306a36Sopenharmony_ci	if (asoc) {
798062306a36Sopenharmony_ci		encap_port = asoc->encap_port;
798162306a36Sopenharmony_ci		goto out;
798262306a36Sopenharmony_ci	}
798362306a36Sopenharmony_ci
798462306a36Sopenharmony_ci	encap_port = sctp_sk(sk)->encap_port;
798562306a36Sopenharmony_ci
798662306a36Sopenharmony_ciout:
798762306a36Sopenharmony_ci	encap.sue_port = (__force uint16_t)encap_port;
798862306a36Sopenharmony_ci	if (copy_to_user(optval, &encap, len))
798962306a36Sopenharmony_ci		return -EFAULT;
799062306a36Sopenharmony_ci
799162306a36Sopenharmony_ci	if (put_user(len, optlen))
799262306a36Sopenharmony_ci		return -EFAULT;
799362306a36Sopenharmony_ci
799462306a36Sopenharmony_ci	return 0;
799562306a36Sopenharmony_ci}
799662306a36Sopenharmony_ci
799762306a36Sopenharmony_cistatic int sctp_getsockopt_probe_interval(struct sock *sk, int len,
799862306a36Sopenharmony_ci					  char __user *optval,
799962306a36Sopenharmony_ci					  int __user *optlen)
800062306a36Sopenharmony_ci{
800162306a36Sopenharmony_ci	struct sctp_probeinterval params;
800262306a36Sopenharmony_ci	struct sctp_association *asoc;
800362306a36Sopenharmony_ci	struct sctp_transport *t;
800462306a36Sopenharmony_ci	__u32 probe_interval;
800562306a36Sopenharmony_ci
800662306a36Sopenharmony_ci	if (len < sizeof(params))
800762306a36Sopenharmony_ci		return -EINVAL;
800862306a36Sopenharmony_ci
800962306a36Sopenharmony_ci	len = sizeof(params);
801062306a36Sopenharmony_ci	if (copy_from_user(&params, optval, len))
801162306a36Sopenharmony_ci		return -EFAULT;
801262306a36Sopenharmony_ci
801362306a36Sopenharmony_ci	/* If an address other than INADDR_ANY is specified, and
801462306a36Sopenharmony_ci	 * no transport is found, then the request is invalid.
801562306a36Sopenharmony_ci	 */
801662306a36Sopenharmony_ci	if (!sctp_is_any(sk, (union sctp_addr *)&params.spi_address)) {
801762306a36Sopenharmony_ci		t = sctp_addr_id2transport(sk, &params.spi_address,
801862306a36Sopenharmony_ci					   params.spi_assoc_id);
801962306a36Sopenharmony_ci		if (!t) {
802062306a36Sopenharmony_ci			pr_debug("%s: failed no transport\n", __func__);
802162306a36Sopenharmony_ci			return -EINVAL;
802262306a36Sopenharmony_ci		}
802362306a36Sopenharmony_ci
802462306a36Sopenharmony_ci		probe_interval = jiffies_to_msecs(t->probe_interval);
802562306a36Sopenharmony_ci		goto out;
802662306a36Sopenharmony_ci	}
802762306a36Sopenharmony_ci
802862306a36Sopenharmony_ci	/* Get association, if assoc_id != SCTP_FUTURE_ASSOC and the
802962306a36Sopenharmony_ci	 * socket is a one to many style socket, and an association
803062306a36Sopenharmony_ci	 * was not found, then the id was invalid.
803162306a36Sopenharmony_ci	 */
803262306a36Sopenharmony_ci	asoc = sctp_id2assoc(sk, params.spi_assoc_id);
803362306a36Sopenharmony_ci	if (!asoc && params.spi_assoc_id != SCTP_FUTURE_ASSOC &&
803462306a36Sopenharmony_ci	    sctp_style(sk, UDP)) {
803562306a36Sopenharmony_ci		pr_debug("%s: failed no association\n", __func__);
803662306a36Sopenharmony_ci		return -EINVAL;
803762306a36Sopenharmony_ci	}
803862306a36Sopenharmony_ci
803962306a36Sopenharmony_ci	if (asoc) {
804062306a36Sopenharmony_ci		probe_interval = jiffies_to_msecs(asoc->probe_interval);
804162306a36Sopenharmony_ci		goto out;
804262306a36Sopenharmony_ci	}
804362306a36Sopenharmony_ci
804462306a36Sopenharmony_ci	probe_interval = sctp_sk(sk)->probe_interval;
804562306a36Sopenharmony_ci
804662306a36Sopenharmony_ciout:
804762306a36Sopenharmony_ci	params.spi_interval = probe_interval;
804862306a36Sopenharmony_ci	if (copy_to_user(optval, &params, len))
804962306a36Sopenharmony_ci		return -EFAULT;
805062306a36Sopenharmony_ci
805162306a36Sopenharmony_ci	if (put_user(len, optlen))
805262306a36Sopenharmony_ci		return -EFAULT;
805362306a36Sopenharmony_ci
805462306a36Sopenharmony_ci	return 0;
805562306a36Sopenharmony_ci}
805662306a36Sopenharmony_ci
805762306a36Sopenharmony_cistatic int sctp_getsockopt(struct sock *sk, int level, int optname,
805862306a36Sopenharmony_ci			   char __user *optval, int __user *optlen)
805962306a36Sopenharmony_ci{
806062306a36Sopenharmony_ci	int retval = 0;
806162306a36Sopenharmony_ci	int len;
806262306a36Sopenharmony_ci
806362306a36Sopenharmony_ci	pr_debug("%s: sk:%p, optname:%d\n", __func__, sk, optname);
806462306a36Sopenharmony_ci
806562306a36Sopenharmony_ci	/* I can hardly begin to describe how wrong this is.  This is
806662306a36Sopenharmony_ci	 * so broken as to be worse than useless.  The API draft
806762306a36Sopenharmony_ci	 * REALLY is NOT helpful here...  I am not convinced that the
806862306a36Sopenharmony_ci	 * semantics of getsockopt() with a level OTHER THAN SOL_SCTP
806962306a36Sopenharmony_ci	 * are at all well-founded.
807062306a36Sopenharmony_ci	 */
807162306a36Sopenharmony_ci	if (level != SOL_SCTP) {
807262306a36Sopenharmony_ci		struct sctp_af *af = sctp_sk(sk)->pf->af;
807362306a36Sopenharmony_ci
807462306a36Sopenharmony_ci		retval = af->getsockopt(sk, level, optname, optval, optlen);
807562306a36Sopenharmony_ci		return retval;
807662306a36Sopenharmony_ci	}
807762306a36Sopenharmony_ci
807862306a36Sopenharmony_ci	if (get_user(len, optlen))
807962306a36Sopenharmony_ci		return -EFAULT;
808062306a36Sopenharmony_ci
808162306a36Sopenharmony_ci	if (len < 0)
808262306a36Sopenharmony_ci		return -EINVAL;
808362306a36Sopenharmony_ci
808462306a36Sopenharmony_ci	lock_sock(sk);
808562306a36Sopenharmony_ci
808662306a36Sopenharmony_ci	switch (optname) {
808762306a36Sopenharmony_ci	case SCTP_STATUS:
808862306a36Sopenharmony_ci		retval = sctp_getsockopt_sctp_status(sk, len, optval, optlen);
808962306a36Sopenharmony_ci		break;
809062306a36Sopenharmony_ci	case SCTP_DISABLE_FRAGMENTS:
809162306a36Sopenharmony_ci		retval = sctp_getsockopt_disable_fragments(sk, len, optval,
809262306a36Sopenharmony_ci							   optlen);
809362306a36Sopenharmony_ci		break;
809462306a36Sopenharmony_ci	case SCTP_EVENTS:
809562306a36Sopenharmony_ci		retval = sctp_getsockopt_events(sk, len, optval, optlen);
809662306a36Sopenharmony_ci		break;
809762306a36Sopenharmony_ci	case SCTP_AUTOCLOSE:
809862306a36Sopenharmony_ci		retval = sctp_getsockopt_autoclose(sk, len, optval, optlen);
809962306a36Sopenharmony_ci		break;
810062306a36Sopenharmony_ci	case SCTP_SOCKOPT_PEELOFF:
810162306a36Sopenharmony_ci		retval = sctp_getsockopt_peeloff(sk, len, optval, optlen);
810262306a36Sopenharmony_ci		break;
810362306a36Sopenharmony_ci	case SCTP_SOCKOPT_PEELOFF_FLAGS:
810462306a36Sopenharmony_ci		retval = sctp_getsockopt_peeloff_flags(sk, len, optval, optlen);
810562306a36Sopenharmony_ci		break;
810662306a36Sopenharmony_ci	case SCTP_PEER_ADDR_PARAMS:
810762306a36Sopenharmony_ci		retval = sctp_getsockopt_peer_addr_params(sk, len, optval,
810862306a36Sopenharmony_ci							  optlen);
810962306a36Sopenharmony_ci		break;
811062306a36Sopenharmony_ci	case SCTP_DELAYED_SACK:
811162306a36Sopenharmony_ci		retval = sctp_getsockopt_delayed_ack(sk, len, optval,
811262306a36Sopenharmony_ci							  optlen);
811362306a36Sopenharmony_ci		break;
811462306a36Sopenharmony_ci	case SCTP_INITMSG:
811562306a36Sopenharmony_ci		retval = sctp_getsockopt_initmsg(sk, len, optval, optlen);
811662306a36Sopenharmony_ci		break;
811762306a36Sopenharmony_ci	case SCTP_GET_PEER_ADDRS:
811862306a36Sopenharmony_ci		retval = sctp_getsockopt_peer_addrs(sk, len, optval,
811962306a36Sopenharmony_ci						    optlen);
812062306a36Sopenharmony_ci		break;
812162306a36Sopenharmony_ci	case SCTP_GET_LOCAL_ADDRS:
812262306a36Sopenharmony_ci		retval = sctp_getsockopt_local_addrs(sk, len, optval,
812362306a36Sopenharmony_ci						     optlen);
812462306a36Sopenharmony_ci		break;
812562306a36Sopenharmony_ci	case SCTP_SOCKOPT_CONNECTX3:
812662306a36Sopenharmony_ci		retval = sctp_getsockopt_connectx3(sk, len, optval, optlen);
812762306a36Sopenharmony_ci		break;
812862306a36Sopenharmony_ci	case SCTP_DEFAULT_SEND_PARAM:
812962306a36Sopenharmony_ci		retval = sctp_getsockopt_default_send_param(sk, len,
813062306a36Sopenharmony_ci							    optval, optlen);
813162306a36Sopenharmony_ci		break;
813262306a36Sopenharmony_ci	case SCTP_DEFAULT_SNDINFO:
813362306a36Sopenharmony_ci		retval = sctp_getsockopt_default_sndinfo(sk, len,
813462306a36Sopenharmony_ci							 optval, optlen);
813562306a36Sopenharmony_ci		break;
813662306a36Sopenharmony_ci	case SCTP_PRIMARY_ADDR:
813762306a36Sopenharmony_ci		retval = sctp_getsockopt_primary_addr(sk, len, optval, optlen);
813862306a36Sopenharmony_ci		break;
813962306a36Sopenharmony_ci	case SCTP_NODELAY:
814062306a36Sopenharmony_ci		retval = sctp_getsockopt_nodelay(sk, len, optval, optlen);
814162306a36Sopenharmony_ci		break;
814262306a36Sopenharmony_ci	case SCTP_RTOINFO:
814362306a36Sopenharmony_ci		retval = sctp_getsockopt_rtoinfo(sk, len, optval, optlen);
814462306a36Sopenharmony_ci		break;
814562306a36Sopenharmony_ci	case SCTP_ASSOCINFO:
814662306a36Sopenharmony_ci		retval = sctp_getsockopt_associnfo(sk, len, optval, optlen);
814762306a36Sopenharmony_ci		break;
814862306a36Sopenharmony_ci	case SCTP_I_WANT_MAPPED_V4_ADDR:
814962306a36Sopenharmony_ci		retval = sctp_getsockopt_mappedv4(sk, len, optval, optlen);
815062306a36Sopenharmony_ci		break;
815162306a36Sopenharmony_ci	case SCTP_MAXSEG:
815262306a36Sopenharmony_ci		retval = sctp_getsockopt_maxseg(sk, len, optval, optlen);
815362306a36Sopenharmony_ci		break;
815462306a36Sopenharmony_ci	case SCTP_GET_PEER_ADDR_INFO:
815562306a36Sopenharmony_ci		retval = sctp_getsockopt_peer_addr_info(sk, len, optval,
815662306a36Sopenharmony_ci							optlen);
815762306a36Sopenharmony_ci		break;
815862306a36Sopenharmony_ci	case SCTP_ADAPTATION_LAYER:
815962306a36Sopenharmony_ci		retval = sctp_getsockopt_adaptation_layer(sk, len, optval,
816062306a36Sopenharmony_ci							optlen);
816162306a36Sopenharmony_ci		break;
816262306a36Sopenharmony_ci	case SCTP_CONTEXT:
816362306a36Sopenharmony_ci		retval = sctp_getsockopt_context(sk, len, optval, optlen);
816462306a36Sopenharmony_ci		break;
816562306a36Sopenharmony_ci	case SCTP_FRAGMENT_INTERLEAVE:
816662306a36Sopenharmony_ci		retval = sctp_getsockopt_fragment_interleave(sk, len, optval,
816762306a36Sopenharmony_ci							     optlen);
816862306a36Sopenharmony_ci		break;
816962306a36Sopenharmony_ci	case SCTP_PARTIAL_DELIVERY_POINT:
817062306a36Sopenharmony_ci		retval = sctp_getsockopt_partial_delivery_point(sk, len, optval,
817162306a36Sopenharmony_ci								optlen);
817262306a36Sopenharmony_ci		break;
817362306a36Sopenharmony_ci	case SCTP_MAX_BURST:
817462306a36Sopenharmony_ci		retval = sctp_getsockopt_maxburst(sk, len, optval, optlen);
817562306a36Sopenharmony_ci		break;
817662306a36Sopenharmony_ci	case SCTP_AUTH_KEY:
817762306a36Sopenharmony_ci	case SCTP_AUTH_CHUNK:
817862306a36Sopenharmony_ci	case SCTP_AUTH_DELETE_KEY:
817962306a36Sopenharmony_ci	case SCTP_AUTH_DEACTIVATE_KEY:
818062306a36Sopenharmony_ci		retval = -EOPNOTSUPP;
818162306a36Sopenharmony_ci		break;
818262306a36Sopenharmony_ci	case SCTP_HMAC_IDENT:
818362306a36Sopenharmony_ci		retval = sctp_getsockopt_hmac_ident(sk, len, optval, optlen);
818462306a36Sopenharmony_ci		break;
818562306a36Sopenharmony_ci	case SCTP_AUTH_ACTIVE_KEY:
818662306a36Sopenharmony_ci		retval = sctp_getsockopt_active_key(sk, len, optval, optlen);
818762306a36Sopenharmony_ci		break;
818862306a36Sopenharmony_ci	case SCTP_PEER_AUTH_CHUNKS:
818962306a36Sopenharmony_ci		retval = sctp_getsockopt_peer_auth_chunks(sk, len, optval,
819062306a36Sopenharmony_ci							optlen);
819162306a36Sopenharmony_ci		break;
819262306a36Sopenharmony_ci	case SCTP_LOCAL_AUTH_CHUNKS:
819362306a36Sopenharmony_ci		retval = sctp_getsockopt_local_auth_chunks(sk, len, optval,
819462306a36Sopenharmony_ci							optlen);
819562306a36Sopenharmony_ci		break;
819662306a36Sopenharmony_ci	case SCTP_GET_ASSOC_NUMBER:
819762306a36Sopenharmony_ci		retval = sctp_getsockopt_assoc_number(sk, len, optval, optlen);
819862306a36Sopenharmony_ci		break;
819962306a36Sopenharmony_ci	case SCTP_GET_ASSOC_ID_LIST:
820062306a36Sopenharmony_ci		retval = sctp_getsockopt_assoc_ids(sk, len, optval, optlen);
820162306a36Sopenharmony_ci		break;
820262306a36Sopenharmony_ci	case SCTP_AUTO_ASCONF:
820362306a36Sopenharmony_ci		retval = sctp_getsockopt_auto_asconf(sk, len, optval, optlen);
820462306a36Sopenharmony_ci		break;
820562306a36Sopenharmony_ci	case SCTP_PEER_ADDR_THLDS:
820662306a36Sopenharmony_ci		retval = sctp_getsockopt_paddr_thresholds(sk, optval, len,
820762306a36Sopenharmony_ci							  optlen, false);
820862306a36Sopenharmony_ci		break;
820962306a36Sopenharmony_ci	case SCTP_PEER_ADDR_THLDS_V2:
821062306a36Sopenharmony_ci		retval = sctp_getsockopt_paddr_thresholds(sk, optval, len,
821162306a36Sopenharmony_ci							  optlen, true);
821262306a36Sopenharmony_ci		break;
821362306a36Sopenharmony_ci	case SCTP_GET_ASSOC_STATS:
821462306a36Sopenharmony_ci		retval = sctp_getsockopt_assoc_stats(sk, len, optval, optlen);
821562306a36Sopenharmony_ci		break;
821662306a36Sopenharmony_ci	case SCTP_RECVRCVINFO:
821762306a36Sopenharmony_ci		retval = sctp_getsockopt_recvrcvinfo(sk, len, optval, optlen);
821862306a36Sopenharmony_ci		break;
821962306a36Sopenharmony_ci	case SCTP_RECVNXTINFO:
822062306a36Sopenharmony_ci		retval = sctp_getsockopt_recvnxtinfo(sk, len, optval, optlen);
822162306a36Sopenharmony_ci		break;
822262306a36Sopenharmony_ci	case SCTP_PR_SUPPORTED:
822362306a36Sopenharmony_ci		retval = sctp_getsockopt_pr_supported(sk, len, optval, optlen);
822462306a36Sopenharmony_ci		break;
822562306a36Sopenharmony_ci	case SCTP_DEFAULT_PRINFO:
822662306a36Sopenharmony_ci		retval = sctp_getsockopt_default_prinfo(sk, len, optval,
822762306a36Sopenharmony_ci							optlen);
822862306a36Sopenharmony_ci		break;
822962306a36Sopenharmony_ci	case SCTP_PR_ASSOC_STATUS:
823062306a36Sopenharmony_ci		retval = sctp_getsockopt_pr_assocstatus(sk, len, optval,
823162306a36Sopenharmony_ci							optlen);
823262306a36Sopenharmony_ci		break;
823362306a36Sopenharmony_ci	case SCTP_PR_STREAM_STATUS:
823462306a36Sopenharmony_ci		retval = sctp_getsockopt_pr_streamstatus(sk, len, optval,
823562306a36Sopenharmony_ci							 optlen);
823662306a36Sopenharmony_ci		break;
823762306a36Sopenharmony_ci	case SCTP_RECONFIG_SUPPORTED:
823862306a36Sopenharmony_ci		retval = sctp_getsockopt_reconfig_supported(sk, len, optval,
823962306a36Sopenharmony_ci							    optlen);
824062306a36Sopenharmony_ci		break;
824162306a36Sopenharmony_ci	case SCTP_ENABLE_STREAM_RESET:
824262306a36Sopenharmony_ci		retval = sctp_getsockopt_enable_strreset(sk, len, optval,
824362306a36Sopenharmony_ci							 optlen);
824462306a36Sopenharmony_ci		break;
824562306a36Sopenharmony_ci	case SCTP_STREAM_SCHEDULER:
824662306a36Sopenharmony_ci		retval = sctp_getsockopt_scheduler(sk, len, optval,
824762306a36Sopenharmony_ci						   optlen);
824862306a36Sopenharmony_ci		break;
824962306a36Sopenharmony_ci	case SCTP_STREAM_SCHEDULER_VALUE:
825062306a36Sopenharmony_ci		retval = sctp_getsockopt_scheduler_value(sk, len, optval,
825162306a36Sopenharmony_ci							 optlen);
825262306a36Sopenharmony_ci		break;
825362306a36Sopenharmony_ci	case SCTP_INTERLEAVING_SUPPORTED:
825462306a36Sopenharmony_ci		retval = sctp_getsockopt_interleaving_supported(sk, len, optval,
825562306a36Sopenharmony_ci								optlen);
825662306a36Sopenharmony_ci		break;
825762306a36Sopenharmony_ci	case SCTP_REUSE_PORT:
825862306a36Sopenharmony_ci		retval = sctp_getsockopt_reuse_port(sk, len, optval, optlen);
825962306a36Sopenharmony_ci		break;
826062306a36Sopenharmony_ci	case SCTP_EVENT:
826162306a36Sopenharmony_ci		retval = sctp_getsockopt_event(sk, len, optval, optlen);
826262306a36Sopenharmony_ci		break;
826362306a36Sopenharmony_ci	case SCTP_ASCONF_SUPPORTED:
826462306a36Sopenharmony_ci		retval = sctp_getsockopt_asconf_supported(sk, len, optval,
826562306a36Sopenharmony_ci							  optlen);
826662306a36Sopenharmony_ci		break;
826762306a36Sopenharmony_ci	case SCTP_AUTH_SUPPORTED:
826862306a36Sopenharmony_ci		retval = sctp_getsockopt_auth_supported(sk, len, optval,
826962306a36Sopenharmony_ci							optlen);
827062306a36Sopenharmony_ci		break;
827162306a36Sopenharmony_ci	case SCTP_ECN_SUPPORTED:
827262306a36Sopenharmony_ci		retval = sctp_getsockopt_ecn_supported(sk, len, optval, optlen);
827362306a36Sopenharmony_ci		break;
827462306a36Sopenharmony_ci	case SCTP_EXPOSE_POTENTIALLY_FAILED_STATE:
827562306a36Sopenharmony_ci		retval = sctp_getsockopt_pf_expose(sk, len, optval, optlen);
827662306a36Sopenharmony_ci		break;
827762306a36Sopenharmony_ci	case SCTP_REMOTE_UDP_ENCAPS_PORT:
827862306a36Sopenharmony_ci		retval = sctp_getsockopt_encap_port(sk, len, optval, optlen);
827962306a36Sopenharmony_ci		break;
828062306a36Sopenharmony_ci	case SCTP_PLPMTUD_PROBE_INTERVAL:
828162306a36Sopenharmony_ci		retval = sctp_getsockopt_probe_interval(sk, len, optval, optlen);
828262306a36Sopenharmony_ci		break;
828362306a36Sopenharmony_ci	default:
828462306a36Sopenharmony_ci		retval = -ENOPROTOOPT;
828562306a36Sopenharmony_ci		break;
828662306a36Sopenharmony_ci	}
828762306a36Sopenharmony_ci
828862306a36Sopenharmony_ci	release_sock(sk);
828962306a36Sopenharmony_ci	return retval;
829062306a36Sopenharmony_ci}
829162306a36Sopenharmony_ci
829262306a36Sopenharmony_cistatic bool sctp_bpf_bypass_getsockopt(int level, int optname)
829362306a36Sopenharmony_ci{
829462306a36Sopenharmony_ci	if (level == SOL_SCTP) {
829562306a36Sopenharmony_ci		switch (optname) {
829662306a36Sopenharmony_ci		case SCTP_SOCKOPT_PEELOFF:
829762306a36Sopenharmony_ci		case SCTP_SOCKOPT_PEELOFF_FLAGS:
829862306a36Sopenharmony_ci		case SCTP_SOCKOPT_CONNECTX3:
829962306a36Sopenharmony_ci			return true;
830062306a36Sopenharmony_ci		default:
830162306a36Sopenharmony_ci			return false;
830262306a36Sopenharmony_ci		}
830362306a36Sopenharmony_ci	}
830462306a36Sopenharmony_ci
830562306a36Sopenharmony_ci	return false;
830662306a36Sopenharmony_ci}
830762306a36Sopenharmony_ci
830862306a36Sopenharmony_cistatic int sctp_hash(struct sock *sk)
830962306a36Sopenharmony_ci{
831062306a36Sopenharmony_ci	/* STUB */
831162306a36Sopenharmony_ci	return 0;
831262306a36Sopenharmony_ci}
831362306a36Sopenharmony_ci
831462306a36Sopenharmony_cistatic void sctp_unhash(struct sock *sk)
831562306a36Sopenharmony_ci{
831662306a36Sopenharmony_ci	/* STUB */
831762306a36Sopenharmony_ci}
831862306a36Sopenharmony_ci
831962306a36Sopenharmony_ci/* Check if port is acceptable.  Possibly find first available port.
832062306a36Sopenharmony_ci *
832162306a36Sopenharmony_ci * The port hash table (contained in the 'global' SCTP protocol storage
832262306a36Sopenharmony_ci * returned by struct sctp_protocol *sctp_get_protocol()). The hash
832362306a36Sopenharmony_ci * table is an array of 4096 lists (sctp_bind_hashbucket). Each
832462306a36Sopenharmony_ci * list (the list number is the port number hashed out, so as you
832562306a36Sopenharmony_ci * would expect from a hash function, all the ports in a given list have
832662306a36Sopenharmony_ci * such a number that hashes out to the same list number; you were
832762306a36Sopenharmony_ci * expecting that, right?); so each list has a set of ports, with a
832862306a36Sopenharmony_ci * link to the socket (struct sock) that uses it, the port number and
832962306a36Sopenharmony_ci * a fastreuse flag (FIXME: NPI ipg).
833062306a36Sopenharmony_ci */
833162306a36Sopenharmony_cistatic struct sctp_bind_bucket *sctp_bucket_create(
833262306a36Sopenharmony_ci	struct sctp_bind_hashbucket *head, struct net *, unsigned short snum);
833362306a36Sopenharmony_ci
833462306a36Sopenharmony_cistatic int sctp_get_port_local(struct sock *sk, union sctp_addr *addr)
833562306a36Sopenharmony_ci{
833662306a36Sopenharmony_ci	struct sctp_sock *sp = sctp_sk(sk);
833762306a36Sopenharmony_ci	bool reuse = (sk->sk_reuse || sp->reuse);
833862306a36Sopenharmony_ci	struct sctp_bind_hashbucket *head; /* hash list */
833962306a36Sopenharmony_ci	struct net *net = sock_net(sk);
834062306a36Sopenharmony_ci	kuid_t uid = sock_i_uid(sk);
834162306a36Sopenharmony_ci	struct sctp_bind_bucket *pp;
834262306a36Sopenharmony_ci	unsigned short snum;
834362306a36Sopenharmony_ci	int ret;
834462306a36Sopenharmony_ci
834562306a36Sopenharmony_ci	snum = ntohs(addr->v4.sin_port);
834662306a36Sopenharmony_ci
834762306a36Sopenharmony_ci	pr_debug("%s: begins, snum:%d\n", __func__, snum);
834862306a36Sopenharmony_ci
834962306a36Sopenharmony_ci	if (snum == 0) {
835062306a36Sopenharmony_ci		/* Search for an available port. */
835162306a36Sopenharmony_ci		int low, high, remaining, index;
835262306a36Sopenharmony_ci		unsigned int rover;
835362306a36Sopenharmony_ci
835462306a36Sopenharmony_ci		inet_sk_get_local_port_range(sk, &low, &high);
835562306a36Sopenharmony_ci		remaining = (high - low) + 1;
835662306a36Sopenharmony_ci		rover = get_random_u32_below(remaining) + low;
835762306a36Sopenharmony_ci
835862306a36Sopenharmony_ci		do {
835962306a36Sopenharmony_ci			rover++;
836062306a36Sopenharmony_ci			if ((rover < low) || (rover > high))
836162306a36Sopenharmony_ci				rover = low;
836262306a36Sopenharmony_ci			if (inet_is_local_reserved_port(net, rover))
836362306a36Sopenharmony_ci				continue;
836462306a36Sopenharmony_ci			index = sctp_phashfn(net, rover);
836562306a36Sopenharmony_ci			head = &sctp_port_hashtable[index];
836662306a36Sopenharmony_ci			spin_lock_bh(&head->lock);
836762306a36Sopenharmony_ci			sctp_for_each_hentry(pp, &head->chain)
836862306a36Sopenharmony_ci				if ((pp->port == rover) &&
836962306a36Sopenharmony_ci				    net_eq(net, pp->net))
837062306a36Sopenharmony_ci					goto next;
837162306a36Sopenharmony_ci			break;
837262306a36Sopenharmony_ci		next:
837362306a36Sopenharmony_ci			spin_unlock_bh(&head->lock);
837462306a36Sopenharmony_ci			cond_resched();
837562306a36Sopenharmony_ci		} while (--remaining > 0);
837662306a36Sopenharmony_ci
837762306a36Sopenharmony_ci		/* Exhausted local port range during search? */
837862306a36Sopenharmony_ci		ret = 1;
837962306a36Sopenharmony_ci		if (remaining <= 0)
838062306a36Sopenharmony_ci			return ret;
838162306a36Sopenharmony_ci
838262306a36Sopenharmony_ci		/* OK, here is the one we will use.  HEAD (the port
838362306a36Sopenharmony_ci		 * hash table list entry) is non-NULL and we hold it's
838462306a36Sopenharmony_ci		 * mutex.
838562306a36Sopenharmony_ci		 */
838662306a36Sopenharmony_ci		snum = rover;
838762306a36Sopenharmony_ci	} else {
838862306a36Sopenharmony_ci		/* We are given an specific port number; we verify
838962306a36Sopenharmony_ci		 * that it is not being used. If it is used, we will
839062306a36Sopenharmony_ci		 * exahust the search in the hash list corresponding
839162306a36Sopenharmony_ci		 * to the port number (snum) - we detect that with the
839262306a36Sopenharmony_ci		 * port iterator, pp being NULL.
839362306a36Sopenharmony_ci		 */
839462306a36Sopenharmony_ci		head = &sctp_port_hashtable[sctp_phashfn(net, snum)];
839562306a36Sopenharmony_ci		spin_lock_bh(&head->lock);
839662306a36Sopenharmony_ci		sctp_for_each_hentry(pp, &head->chain) {
839762306a36Sopenharmony_ci			if ((pp->port == snum) && net_eq(pp->net, net))
839862306a36Sopenharmony_ci				goto pp_found;
839962306a36Sopenharmony_ci		}
840062306a36Sopenharmony_ci	}
840162306a36Sopenharmony_ci	pp = NULL;
840262306a36Sopenharmony_ci	goto pp_not_found;
840362306a36Sopenharmony_cipp_found:
840462306a36Sopenharmony_ci	if (!hlist_empty(&pp->owner)) {
840562306a36Sopenharmony_ci		/* We had a port hash table hit - there is an
840662306a36Sopenharmony_ci		 * available port (pp != NULL) and it is being
840762306a36Sopenharmony_ci		 * used by other socket (pp->owner not empty); that other
840862306a36Sopenharmony_ci		 * socket is going to be sk2.
840962306a36Sopenharmony_ci		 */
841062306a36Sopenharmony_ci		struct sock *sk2;
841162306a36Sopenharmony_ci
841262306a36Sopenharmony_ci		pr_debug("%s: found a possible match\n", __func__);
841362306a36Sopenharmony_ci
841462306a36Sopenharmony_ci		if ((pp->fastreuse && reuse &&
841562306a36Sopenharmony_ci		     sk->sk_state != SCTP_SS_LISTENING) ||
841662306a36Sopenharmony_ci		    (pp->fastreuseport && sk->sk_reuseport &&
841762306a36Sopenharmony_ci		     uid_eq(pp->fastuid, uid)))
841862306a36Sopenharmony_ci			goto success;
841962306a36Sopenharmony_ci
842062306a36Sopenharmony_ci		/* Run through the list of sockets bound to the port
842162306a36Sopenharmony_ci		 * (pp->port) [via the pointers bind_next and
842262306a36Sopenharmony_ci		 * bind_pprev in the struct sock *sk2 (pp->sk)]. On each one,
842362306a36Sopenharmony_ci		 * we get the endpoint they describe and run through
842462306a36Sopenharmony_ci		 * the endpoint's list of IP (v4 or v6) addresses,
842562306a36Sopenharmony_ci		 * comparing each of the addresses with the address of
842662306a36Sopenharmony_ci		 * the socket sk. If we find a match, then that means
842762306a36Sopenharmony_ci		 * that this port/socket (sk) combination are already
842862306a36Sopenharmony_ci		 * in an endpoint.
842962306a36Sopenharmony_ci		 */
843062306a36Sopenharmony_ci		sk_for_each_bound(sk2, &pp->owner) {
843162306a36Sopenharmony_ci			int bound_dev_if2 = READ_ONCE(sk2->sk_bound_dev_if);
843262306a36Sopenharmony_ci			struct sctp_sock *sp2 = sctp_sk(sk2);
843362306a36Sopenharmony_ci			struct sctp_endpoint *ep2 = sp2->ep;
843462306a36Sopenharmony_ci
843562306a36Sopenharmony_ci			if (sk == sk2 ||
843662306a36Sopenharmony_ci			    (reuse && (sk2->sk_reuse || sp2->reuse) &&
843762306a36Sopenharmony_ci			     sk2->sk_state != SCTP_SS_LISTENING) ||
843862306a36Sopenharmony_ci			    (sk->sk_reuseport && sk2->sk_reuseport &&
843962306a36Sopenharmony_ci			     uid_eq(uid, sock_i_uid(sk2))))
844062306a36Sopenharmony_ci				continue;
844162306a36Sopenharmony_ci
844262306a36Sopenharmony_ci			if ((!sk->sk_bound_dev_if || !bound_dev_if2 ||
844362306a36Sopenharmony_ci			     sk->sk_bound_dev_if == bound_dev_if2) &&
844462306a36Sopenharmony_ci			    sctp_bind_addr_conflict(&ep2->base.bind_addr,
844562306a36Sopenharmony_ci						    addr, sp2, sp)) {
844662306a36Sopenharmony_ci				ret = 1;
844762306a36Sopenharmony_ci				goto fail_unlock;
844862306a36Sopenharmony_ci			}
844962306a36Sopenharmony_ci		}
845062306a36Sopenharmony_ci
845162306a36Sopenharmony_ci		pr_debug("%s: found a match\n", __func__);
845262306a36Sopenharmony_ci	}
845362306a36Sopenharmony_cipp_not_found:
845462306a36Sopenharmony_ci	/* If there was a hash table miss, create a new port.  */
845562306a36Sopenharmony_ci	ret = 1;
845662306a36Sopenharmony_ci	if (!pp && !(pp = sctp_bucket_create(head, net, snum)))
845762306a36Sopenharmony_ci		goto fail_unlock;
845862306a36Sopenharmony_ci
845962306a36Sopenharmony_ci	/* In either case (hit or miss), make sure fastreuse is 1 only
846062306a36Sopenharmony_ci	 * if sk->sk_reuse is too (that is, if the caller requested
846162306a36Sopenharmony_ci	 * SO_REUSEADDR on this socket -sk-).
846262306a36Sopenharmony_ci	 */
846362306a36Sopenharmony_ci	if (hlist_empty(&pp->owner)) {
846462306a36Sopenharmony_ci		if (reuse && sk->sk_state != SCTP_SS_LISTENING)
846562306a36Sopenharmony_ci			pp->fastreuse = 1;
846662306a36Sopenharmony_ci		else
846762306a36Sopenharmony_ci			pp->fastreuse = 0;
846862306a36Sopenharmony_ci
846962306a36Sopenharmony_ci		if (sk->sk_reuseport) {
847062306a36Sopenharmony_ci			pp->fastreuseport = 1;
847162306a36Sopenharmony_ci			pp->fastuid = uid;
847262306a36Sopenharmony_ci		} else {
847362306a36Sopenharmony_ci			pp->fastreuseport = 0;
847462306a36Sopenharmony_ci		}
847562306a36Sopenharmony_ci	} else {
847662306a36Sopenharmony_ci		if (pp->fastreuse &&
847762306a36Sopenharmony_ci		    (!reuse || sk->sk_state == SCTP_SS_LISTENING))
847862306a36Sopenharmony_ci			pp->fastreuse = 0;
847962306a36Sopenharmony_ci
848062306a36Sopenharmony_ci		if (pp->fastreuseport &&
848162306a36Sopenharmony_ci		    (!sk->sk_reuseport || !uid_eq(pp->fastuid, uid)))
848262306a36Sopenharmony_ci			pp->fastreuseport = 0;
848362306a36Sopenharmony_ci	}
848462306a36Sopenharmony_ci
848562306a36Sopenharmony_ci	/* We are set, so fill up all the data in the hash table
848662306a36Sopenharmony_ci	 * entry, tie the socket list information with the rest of the
848762306a36Sopenharmony_ci	 * sockets FIXME: Blurry, NPI (ipg).
848862306a36Sopenharmony_ci	 */
848962306a36Sopenharmony_cisuccess:
849062306a36Sopenharmony_ci	if (!sp->bind_hash) {
849162306a36Sopenharmony_ci		inet_sk(sk)->inet_num = snum;
849262306a36Sopenharmony_ci		sk_add_bind_node(sk, &pp->owner);
849362306a36Sopenharmony_ci		sp->bind_hash = pp;
849462306a36Sopenharmony_ci	}
849562306a36Sopenharmony_ci	ret = 0;
849662306a36Sopenharmony_ci
849762306a36Sopenharmony_cifail_unlock:
849862306a36Sopenharmony_ci	spin_unlock_bh(&head->lock);
849962306a36Sopenharmony_ci	return ret;
850062306a36Sopenharmony_ci}
850162306a36Sopenharmony_ci
850262306a36Sopenharmony_ci/* Assign a 'snum' port to the socket.  If snum == 0, an ephemeral
850362306a36Sopenharmony_ci * port is requested.
850462306a36Sopenharmony_ci */
850562306a36Sopenharmony_cistatic int sctp_get_port(struct sock *sk, unsigned short snum)
850662306a36Sopenharmony_ci{
850762306a36Sopenharmony_ci	union sctp_addr addr;
850862306a36Sopenharmony_ci	struct sctp_af *af = sctp_sk(sk)->pf->af;
850962306a36Sopenharmony_ci
851062306a36Sopenharmony_ci	/* Set up a dummy address struct from the sk. */
851162306a36Sopenharmony_ci	af->from_sk(&addr, sk);
851262306a36Sopenharmony_ci	addr.v4.sin_port = htons(snum);
851362306a36Sopenharmony_ci
851462306a36Sopenharmony_ci	/* Note: sk->sk_num gets filled in if ephemeral port request. */
851562306a36Sopenharmony_ci	return sctp_get_port_local(sk, &addr);
851662306a36Sopenharmony_ci}
851762306a36Sopenharmony_ci
851862306a36Sopenharmony_ci/*
851962306a36Sopenharmony_ci *  Move a socket to LISTENING state.
852062306a36Sopenharmony_ci */
852162306a36Sopenharmony_cistatic int sctp_listen_start(struct sock *sk, int backlog)
852262306a36Sopenharmony_ci{
852362306a36Sopenharmony_ci	struct sctp_sock *sp = sctp_sk(sk);
852462306a36Sopenharmony_ci	struct sctp_endpoint *ep = sp->ep;
852562306a36Sopenharmony_ci	struct crypto_shash *tfm = NULL;
852662306a36Sopenharmony_ci	char alg[32];
852762306a36Sopenharmony_ci
852862306a36Sopenharmony_ci	/* Allocate HMAC for generating cookie. */
852962306a36Sopenharmony_ci	if (!sp->hmac && sp->sctp_hmac_alg) {
853062306a36Sopenharmony_ci		sprintf(alg, "hmac(%s)", sp->sctp_hmac_alg);
853162306a36Sopenharmony_ci		tfm = crypto_alloc_shash(alg, 0, 0);
853262306a36Sopenharmony_ci		if (IS_ERR(tfm)) {
853362306a36Sopenharmony_ci			net_info_ratelimited("failed to load transform for %s: %ld\n",
853462306a36Sopenharmony_ci					     sp->sctp_hmac_alg, PTR_ERR(tfm));
853562306a36Sopenharmony_ci			return -ENOSYS;
853662306a36Sopenharmony_ci		}
853762306a36Sopenharmony_ci		sctp_sk(sk)->hmac = tfm;
853862306a36Sopenharmony_ci	}
853962306a36Sopenharmony_ci
854062306a36Sopenharmony_ci	/*
854162306a36Sopenharmony_ci	 * If a bind() or sctp_bindx() is not called prior to a listen()
854262306a36Sopenharmony_ci	 * call that allows new associations to be accepted, the system
854362306a36Sopenharmony_ci	 * picks an ephemeral port and will choose an address set equivalent
854462306a36Sopenharmony_ci	 * to binding with a wildcard address.
854562306a36Sopenharmony_ci	 *
854662306a36Sopenharmony_ci	 * This is not currently spelled out in the SCTP sockets
854762306a36Sopenharmony_ci	 * extensions draft, but follows the practice as seen in TCP
854862306a36Sopenharmony_ci	 * sockets.
854962306a36Sopenharmony_ci	 *
855062306a36Sopenharmony_ci	 */
855162306a36Sopenharmony_ci	inet_sk_set_state(sk, SCTP_SS_LISTENING);
855262306a36Sopenharmony_ci	if (!ep->base.bind_addr.port) {
855362306a36Sopenharmony_ci		if (sctp_autobind(sk))
855462306a36Sopenharmony_ci			return -EAGAIN;
855562306a36Sopenharmony_ci	} else {
855662306a36Sopenharmony_ci		if (sctp_get_port(sk, inet_sk(sk)->inet_num)) {
855762306a36Sopenharmony_ci			inet_sk_set_state(sk, SCTP_SS_CLOSED);
855862306a36Sopenharmony_ci			return -EADDRINUSE;
855962306a36Sopenharmony_ci		}
856062306a36Sopenharmony_ci	}
856162306a36Sopenharmony_ci
856262306a36Sopenharmony_ci	WRITE_ONCE(sk->sk_max_ack_backlog, backlog);
856362306a36Sopenharmony_ci	return sctp_hash_endpoint(ep);
856462306a36Sopenharmony_ci}
856562306a36Sopenharmony_ci
856662306a36Sopenharmony_ci/*
856762306a36Sopenharmony_ci * 4.1.3 / 5.1.3 listen()
856862306a36Sopenharmony_ci *
856962306a36Sopenharmony_ci *   By default, new associations are not accepted for UDP style sockets.
857062306a36Sopenharmony_ci *   An application uses listen() to mark a socket as being able to
857162306a36Sopenharmony_ci *   accept new associations.
857262306a36Sopenharmony_ci *
857362306a36Sopenharmony_ci *   On TCP style sockets, applications use listen() to ready the SCTP
857462306a36Sopenharmony_ci *   endpoint for accepting inbound associations.
857562306a36Sopenharmony_ci *
857662306a36Sopenharmony_ci *   On both types of endpoints a backlog of '0' disables listening.
857762306a36Sopenharmony_ci *
857862306a36Sopenharmony_ci *  Move a socket to LISTENING state.
857962306a36Sopenharmony_ci */
858062306a36Sopenharmony_ciint sctp_inet_listen(struct socket *sock, int backlog)
858162306a36Sopenharmony_ci{
858262306a36Sopenharmony_ci	struct sock *sk = sock->sk;
858362306a36Sopenharmony_ci	struct sctp_endpoint *ep = sctp_sk(sk)->ep;
858462306a36Sopenharmony_ci	int err = -EINVAL;
858562306a36Sopenharmony_ci
858662306a36Sopenharmony_ci	if (unlikely(backlog < 0))
858762306a36Sopenharmony_ci		return err;
858862306a36Sopenharmony_ci
858962306a36Sopenharmony_ci	lock_sock(sk);
859062306a36Sopenharmony_ci
859162306a36Sopenharmony_ci	/* Peeled-off sockets are not allowed to listen().  */
859262306a36Sopenharmony_ci	if (sctp_style(sk, UDP_HIGH_BANDWIDTH))
859362306a36Sopenharmony_ci		goto out;
859462306a36Sopenharmony_ci
859562306a36Sopenharmony_ci	if (sock->state != SS_UNCONNECTED)
859662306a36Sopenharmony_ci		goto out;
859762306a36Sopenharmony_ci
859862306a36Sopenharmony_ci	if (!sctp_sstate(sk, LISTENING) && !sctp_sstate(sk, CLOSED))
859962306a36Sopenharmony_ci		goto out;
860062306a36Sopenharmony_ci
860162306a36Sopenharmony_ci	/* If backlog is zero, disable listening. */
860262306a36Sopenharmony_ci	if (!backlog) {
860362306a36Sopenharmony_ci		if (sctp_sstate(sk, CLOSED))
860462306a36Sopenharmony_ci			goto out;
860562306a36Sopenharmony_ci
860662306a36Sopenharmony_ci		err = 0;
860762306a36Sopenharmony_ci		sctp_unhash_endpoint(ep);
860862306a36Sopenharmony_ci		sk->sk_state = SCTP_SS_CLOSED;
860962306a36Sopenharmony_ci		if (sk->sk_reuse || sctp_sk(sk)->reuse)
861062306a36Sopenharmony_ci			sctp_sk(sk)->bind_hash->fastreuse = 1;
861162306a36Sopenharmony_ci		goto out;
861262306a36Sopenharmony_ci	}
861362306a36Sopenharmony_ci
861462306a36Sopenharmony_ci	/* If we are already listening, just update the backlog */
861562306a36Sopenharmony_ci	if (sctp_sstate(sk, LISTENING))
861662306a36Sopenharmony_ci		WRITE_ONCE(sk->sk_max_ack_backlog, backlog);
861762306a36Sopenharmony_ci	else {
861862306a36Sopenharmony_ci		err = sctp_listen_start(sk, backlog);
861962306a36Sopenharmony_ci		if (err)
862062306a36Sopenharmony_ci			goto out;
862162306a36Sopenharmony_ci	}
862262306a36Sopenharmony_ci
862362306a36Sopenharmony_ci	err = 0;
862462306a36Sopenharmony_ciout:
862562306a36Sopenharmony_ci	release_sock(sk);
862662306a36Sopenharmony_ci	return err;
862762306a36Sopenharmony_ci}
862862306a36Sopenharmony_ci
862962306a36Sopenharmony_ci/*
863062306a36Sopenharmony_ci * This function is done by modeling the current datagram_poll() and the
863162306a36Sopenharmony_ci * tcp_poll().  Note that, based on these implementations, we don't
863262306a36Sopenharmony_ci * lock the socket in this function, even though it seems that,
863362306a36Sopenharmony_ci * ideally, locking or some other mechanisms can be used to ensure
863462306a36Sopenharmony_ci * the integrity of the counters (sndbuf and wmem_alloc) used
863562306a36Sopenharmony_ci * in this place.  We assume that we don't need locks either until proven
863662306a36Sopenharmony_ci * otherwise.
863762306a36Sopenharmony_ci *
863862306a36Sopenharmony_ci * Another thing to note is that we include the Async I/O support
863962306a36Sopenharmony_ci * here, again, by modeling the current TCP/UDP code.  We don't have
864062306a36Sopenharmony_ci * a good way to test with it yet.
864162306a36Sopenharmony_ci */
864262306a36Sopenharmony_ci__poll_t sctp_poll(struct file *file, struct socket *sock, poll_table *wait)
864362306a36Sopenharmony_ci{
864462306a36Sopenharmony_ci	struct sock *sk = sock->sk;
864562306a36Sopenharmony_ci	struct sctp_sock *sp = sctp_sk(sk);
864662306a36Sopenharmony_ci	__poll_t mask;
864762306a36Sopenharmony_ci
864862306a36Sopenharmony_ci	poll_wait(file, sk_sleep(sk), wait);
864962306a36Sopenharmony_ci
865062306a36Sopenharmony_ci	sock_rps_record_flow(sk);
865162306a36Sopenharmony_ci
865262306a36Sopenharmony_ci	/* A TCP-style listening socket becomes readable when the accept queue
865362306a36Sopenharmony_ci	 * is not empty.
865462306a36Sopenharmony_ci	 */
865562306a36Sopenharmony_ci	if (sctp_style(sk, TCP) && sctp_sstate(sk, LISTENING))
865662306a36Sopenharmony_ci		return (!list_empty(&sp->ep->asocs)) ?
865762306a36Sopenharmony_ci			(EPOLLIN | EPOLLRDNORM) : 0;
865862306a36Sopenharmony_ci
865962306a36Sopenharmony_ci	mask = 0;
866062306a36Sopenharmony_ci
866162306a36Sopenharmony_ci	/* Is there any exceptional events?  */
866262306a36Sopenharmony_ci	if (sk->sk_err || !skb_queue_empty_lockless(&sk->sk_error_queue))
866362306a36Sopenharmony_ci		mask |= EPOLLERR |
866462306a36Sopenharmony_ci			(sock_flag(sk, SOCK_SELECT_ERR_QUEUE) ? EPOLLPRI : 0);
866562306a36Sopenharmony_ci	if (sk->sk_shutdown & RCV_SHUTDOWN)
866662306a36Sopenharmony_ci		mask |= EPOLLRDHUP | EPOLLIN | EPOLLRDNORM;
866762306a36Sopenharmony_ci	if (sk->sk_shutdown == SHUTDOWN_MASK)
866862306a36Sopenharmony_ci		mask |= EPOLLHUP;
866962306a36Sopenharmony_ci
867062306a36Sopenharmony_ci	/* Is it readable?  Reconsider this code with TCP-style support.  */
867162306a36Sopenharmony_ci	if (!skb_queue_empty_lockless(&sk->sk_receive_queue))
867262306a36Sopenharmony_ci		mask |= EPOLLIN | EPOLLRDNORM;
867362306a36Sopenharmony_ci
867462306a36Sopenharmony_ci	/* The association is either gone or not ready.  */
867562306a36Sopenharmony_ci	if (!sctp_style(sk, UDP) && sctp_sstate(sk, CLOSED))
867662306a36Sopenharmony_ci		return mask;
867762306a36Sopenharmony_ci
867862306a36Sopenharmony_ci	/* Is it writable?  */
867962306a36Sopenharmony_ci	if (sctp_writeable(sk)) {
868062306a36Sopenharmony_ci		mask |= EPOLLOUT | EPOLLWRNORM;
868162306a36Sopenharmony_ci	} else {
868262306a36Sopenharmony_ci		sk_set_bit(SOCKWQ_ASYNC_NOSPACE, sk);
868362306a36Sopenharmony_ci		/*
868462306a36Sopenharmony_ci		 * Since the socket is not locked, the buffer
868562306a36Sopenharmony_ci		 * might be made available after the writeable check and
868662306a36Sopenharmony_ci		 * before the bit is set.  This could cause a lost I/O
868762306a36Sopenharmony_ci		 * signal.  tcp_poll() has a race breaker for this race
868862306a36Sopenharmony_ci		 * condition.  Based on their implementation, we put
868962306a36Sopenharmony_ci		 * in the following code to cover it as well.
869062306a36Sopenharmony_ci		 */
869162306a36Sopenharmony_ci		if (sctp_writeable(sk))
869262306a36Sopenharmony_ci			mask |= EPOLLOUT | EPOLLWRNORM;
869362306a36Sopenharmony_ci	}
869462306a36Sopenharmony_ci	return mask;
869562306a36Sopenharmony_ci}
869662306a36Sopenharmony_ci
869762306a36Sopenharmony_ci/********************************************************************
869862306a36Sopenharmony_ci * 2nd Level Abstractions
869962306a36Sopenharmony_ci ********************************************************************/
870062306a36Sopenharmony_ci
870162306a36Sopenharmony_cistatic struct sctp_bind_bucket *sctp_bucket_create(
870262306a36Sopenharmony_ci	struct sctp_bind_hashbucket *head, struct net *net, unsigned short snum)
870362306a36Sopenharmony_ci{
870462306a36Sopenharmony_ci	struct sctp_bind_bucket *pp;
870562306a36Sopenharmony_ci
870662306a36Sopenharmony_ci	pp = kmem_cache_alloc(sctp_bucket_cachep, GFP_ATOMIC);
870762306a36Sopenharmony_ci	if (pp) {
870862306a36Sopenharmony_ci		SCTP_DBG_OBJCNT_INC(bind_bucket);
870962306a36Sopenharmony_ci		pp->port = snum;
871062306a36Sopenharmony_ci		pp->fastreuse = 0;
871162306a36Sopenharmony_ci		INIT_HLIST_HEAD(&pp->owner);
871262306a36Sopenharmony_ci		pp->net = net;
871362306a36Sopenharmony_ci		hlist_add_head(&pp->node, &head->chain);
871462306a36Sopenharmony_ci	}
871562306a36Sopenharmony_ci	return pp;
871662306a36Sopenharmony_ci}
871762306a36Sopenharmony_ci
871862306a36Sopenharmony_ci/* Caller must hold hashbucket lock for this tb with local BH disabled */
871962306a36Sopenharmony_cistatic void sctp_bucket_destroy(struct sctp_bind_bucket *pp)
872062306a36Sopenharmony_ci{
872162306a36Sopenharmony_ci	if (pp && hlist_empty(&pp->owner)) {
872262306a36Sopenharmony_ci		__hlist_del(&pp->node);
872362306a36Sopenharmony_ci		kmem_cache_free(sctp_bucket_cachep, pp);
872462306a36Sopenharmony_ci		SCTP_DBG_OBJCNT_DEC(bind_bucket);
872562306a36Sopenharmony_ci	}
872662306a36Sopenharmony_ci}
872762306a36Sopenharmony_ci
872862306a36Sopenharmony_ci/* Release this socket's reference to a local port.  */
872962306a36Sopenharmony_cistatic inline void __sctp_put_port(struct sock *sk)
873062306a36Sopenharmony_ci{
873162306a36Sopenharmony_ci	struct sctp_bind_hashbucket *head =
873262306a36Sopenharmony_ci		&sctp_port_hashtable[sctp_phashfn(sock_net(sk),
873362306a36Sopenharmony_ci						  inet_sk(sk)->inet_num)];
873462306a36Sopenharmony_ci	struct sctp_bind_bucket *pp;
873562306a36Sopenharmony_ci
873662306a36Sopenharmony_ci	spin_lock(&head->lock);
873762306a36Sopenharmony_ci	pp = sctp_sk(sk)->bind_hash;
873862306a36Sopenharmony_ci	__sk_del_bind_node(sk);
873962306a36Sopenharmony_ci	sctp_sk(sk)->bind_hash = NULL;
874062306a36Sopenharmony_ci	inet_sk(sk)->inet_num = 0;
874162306a36Sopenharmony_ci	sctp_bucket_destroy(pp);
874262306a36Sopenharmony_ci	spin_unlock(&head->lock);
874362306a36Sopenharmony_ci}
874462306a36Sopenharmony_ci
874562306a36Sopenharmony_civoid sctp_put_port(struct sock *sk)
874662306a36Sopenharmony_ci{
874762306a36Sopenharmony_ci	local_bh_disable();
874862306a36Sopenharmony_ci	__sctp_put_port(sk);
874962306a36Sopenharmony_ci	local_bh_enable();
875062306a36Sopenharmony_ci}
875162306a36Sopenharmony_ci
875262306a36Sopenharmony_ci/*
875362306a36Sopenharmony_ci * The system picks an ephemeral port and choose an address set equivalent
875462306a36Sopenharmony_ci * to binding with a wildcard address.
875562306a36Sopenharmony_ci * One of those addresses will be the primary address for the association.
875662306a36Sopenharmony_ci * This automatically enables the multihoming capability of SCTP.
875762306a36Sopenharmony_ci */
875862306a36Sopenharmony_cistatic int sctp_autobind(struct sock *sk)
875962306a36Sopenharmony_ci{
876062306a36Sopenharmony_ci	union sctp_addr autoaddr;
876162306a36Sopenharmony_ci	struct sctp_af *af;
876262306a36Sopenharmony_ci	__be16 port;
876362306a36Sopenharmony_ci
876462306a36Sopenharmony_ci	/* Initialize a local sockaddr structure to INADDR_ANY. */
876562306a36Sopenharmony_ci	af = sctp_sk(sk)->pf->af;
876662306a36Sopenharmony_ci
876762306a36Sopenharmony_ci	port = htons(inet_sk(sk)->inet_num);
876862306a36Sopenharmony_ci	af->inaddr_any(&autoaddr, port);
876962306a36Sopenharmony_ci
877062306a36Sopenharmony_ci	return sctp_do_bind(sk, &autoaddr, af->sockaddr_len);
877162306a36Sopenharmony_ci}
877262306a36Sopenharmony_ci
877362306a36Sopenharmony_ci/* Parse out IPPROTO_SCTP CMSG headers.  Perform only minimal validation.
877462306a36Sopenharmony_ci *
877562306a36Sopenharmony_ci * From RFC 2292
877662306a36Sopenharmony_ci * 4.2 The cmsghdr Structure *
877762306a36Sopenharmony_ci *
877862306a36Sopenharmony_ci * When ancillary data is sent or received, any number of ancillary data
877962306a36Sopenharmony_ci * objects can be specified by the msg_control and msg_controllen members of
878062306a36Sopenharmony_ci * the msghdr structure, because each object is preceded by
878162306a36Sopenharmony_ci * a cmsghdr structure defining the object's length (the cmsg_len member).
878262306a36Sopenharmony_ci * Historically Berkeley-derived implementations have passed only one object
878362306a36Sopenharmony_ci * at a time, but this API allows multiple objects to be
878462306a36Sopenharmony_ci * passed in a single call to sendmsg() or recvmsg(). The following example
878562306a36Sopenharmony_ci * shows two ancillary data objects in a control buffer.
878662306a36Sopenharmony_ci *
878762306a36Sopenharmony_ci *   |<--------------------------- msg_controllen -------------------------->|
878862306a36Sopenharmony_ci *   |                                                                       |
878962306a36Sopenharmony_ci *
879062306a36Sopenharmony_ci *   |<----- ancillary data object ----->|<----- ancillary data object ----->|
879162306a36Sopenharmony_ci *
879262306a36Sopenharmony_ci *   |<---------- CMSG_SPACE() --------->|<---------- CMSG_SPACE() --------->|
879362306a36Sopenharmony_ci *   |                                   |                                   |
879462306a36Sopenharmony_ci *
879562306a36Sopenharmony_ci *   |<---------- cmsg_len ---------->|  |<--------- cmsg_len ----------->|  |
879662306a36Sopenharmony_ci *
879762306a36Sopenharmony_ci *   |<--------- CMSG_LEN() --------->|  |<-------- CMSG_LEN() ---------->|  |
879862306a36Sopenharmony_ci *   |                                |  |                                |  |
879962306a36Sopenharmony_ci *
880062306a36Sopenharmony_ci *   +-----+-----+-----+--+-----------+--+-----+-----+-----+--+-----------+--+
880162306a36Sopenharmony_ci *   |cmsg_|cmsg_|cmsg_|XX|           |XX|cmsg_|cmsg_|cmsg_|XX|           |XX|
880262306a36Sopenharmony_ci *
880362306a36Sopenharmony_ci *   |len  |level|type |XX|cmsg_data[]|XX|len  |level|type |XX|cmsg_data[]|XX|
880462306a36Sopenharmony_ci *
880562306a36Sopenharmony_ci *   +-----+-----+-----+--+-----------+--+-----+-----+-----+--+-----------+--+
880662306a36Sopenharmony_ci *    ^
880762306a36Sopenharmony_ci *    |
880862306a36Sopenharmony_ci *
880962306a36Sopenharmony_ci * msg_control
881062306a36Sopenharmony_ci * points here
881162306a36Sopenharmony_ci */
881262306a36Sopenharmony_cistatic int sctp_msghdr_parse(const struct msghdr *msg, struct sctp_cmsgs *cmsgs)
881362306a36Sopenharmony_ci{
881462306a36Sopenharmony_ci	struct msghdr *my_msg = (struct msghdr *)msg;
881562306a36Sopenharmony_ci	struct cmsghdr *cmsg;
881662306a36Sopenharmony_ci
881762306a36Sopenharmony_ci	for_each_cmsghdr(cmsg, my_msg) {
881862306a36Sopenharmony_ci		if (!CMSG_OK(my_msg, cmsg))
881962306a36Sopenharmony_ci			return -EINVAL;
882062306a36Sopenharmony_ci
882162306a36Sopenharmony_ci		/* Should we parse this header or ignore?  */
882262306a36Sopenharmony_ci		if (cmsg->cmsg_level != IPPROTO_SCTP)
882362306a36Sopenharmony_ci			continue;
882462306a36Sopenharmony_ci
882562306a36Sopenharmony_ci		/* Strictly check lengths following example in SCM code.  */
882662306a36Sopenharmony_ci		switch (cmsg->cmsg_type) {
882762306a36Sopenharmony_ci		case SCTP_INIT:
882862306a36Sopenharmony_ci			/* SCTP Socket API Extension
882962306a36Sopenharmony_ci			 * 5.3.1 SCTP Initiation Structure (SCTP_INIT)
883062306a36Sopenharmony_ci			 *
883162306a36Sopenharmony_ci			 * This cmsghdr structure provides information for
883262306a36Sopenharmony_ci			 * initializing new SCTP associations with sendmsg().
883362306a36Sopenharmony_ci			 * The SCTP_INITMSG socket option uses this same data
883462306a36Sopenharmony_ci			 * structure.  This structure is not used for
883562306a36Sopenharmony_ci			 * recvmsg().
883662306a36Sopenharmony_ci			 *
883762306a36Sopenharmony_ci			 * cmsg_level    cmsg_type      cmsg_data[]
883862306a36Sopenharmony_ci			 * ------------  ------------   ----------------------
883962306a36Sopenharmony_ci			 * IPPROTO_SCTP  SCTP_INIT      struct sctp_initmsg
884062306a36Sopenharmony_ci			 */
884162306a36Sopenharmony_ci			if (cmsg->cmsg_len != CMSG_LEN(sizeof(struct sctp_initmsg)))
884262306a36Sopenharmony_ci				return -EINVAL;
884362306a36Sopenharmony_ci
884462306a36Sopenharmony_ci			cmsgs->init = CMSG_DATA(cmsg);
884562306a36Sopenharmony_ci			break;
884662306a36Sopenharmony_ci
884762306a36Sopenharmony_ci		case SCTP_SNDRCV:
884862306a36Sopenharmony_ci			/* SCTP Socket API Extension
884962306a36Sopenharmony_ci			 * 5.3.2 SCTP Header Information Structure(SCTP_SNDRCV)
885062306a36Sopenharmony_ci			 *
885162306a36Sopenharmony_ci			 * This cmsghdr structure specifies SCTP options for
885262306a36Sopenharmony_ci			 * sendmsg() and describes SCTP header information
885362306a36Sopenharmony_ci			 * about a received message through recvmsg().
885462306a36Sopenharmony_ci			 *
885562306a36Sopenharmony_ci			 * cmsg_level    cmsg_type      cmsg_data[]
885662306a36Sopenharmony_ci			 * ------------  ------------   ----------------------
885762306a36Sopenharmony_ci			 * IPPROTO_SCTP  SCTP_SNDRCV    struct sctp_sndrcvinfo
885862306a36Sopenharmony_ci			 */
885962306a36Sopenharmony_ci			if (cmsg->cmsg_len != CMSG_LEN(sizeof(struct sctp_sndrcvinfo)))
886062306a36Sopenharmony_ci				return -EINVAL;
886162306a36Sopenharmony_ci
886262306a36Sopenharmony_ci			cmsgs->srinfo = CMSG_DATA(cmsg);
886362306a36Sopenharmony_ci
886462306a36Sopenharmony_ci			if (cmsgs->srinfo->sinfo_flags &
886562306a36Sopenharmony_ci			    ~(SCTP_UNORDERED | SCTP_ADDR_OVER |
886662306a36Sopenharmony_ci			      SCTP_SACK_IMMEDIATELY | SCTP_SENDALL |
886762306a36Sopenharmony_ci			      SCTP_PR_SCTP_MASK | SCTP_ABORT | SCTP_EOF))
886862306a36Sopenharmony_ci				return -EINVAL;
886962306a36Sopenharmony_ci			break;
887062306a36Sopenharmony_ci
887162306a36Sopenharmony_ci		case SCTP_SNDINFO:
887262306a36Sopenharmony_ci			/* SCTP Socket API Extension
887362306a36Sopenharmony_ci			 * 5.3.4 SCTP Send Information Structure (SCTP_SNDINFO)
887462306a36Sopenharmony_ci			 *
887562306a36Sopenharmony_ci			 * This cmsghdr structure specifies SCTP options for
887662306a36Sopenharmony_ci			 * sendmsg(). This structure and SCTP_RCVINFO replaces
887762306a36Sopenharmony_ci			 * SCTP_SNDRCV which has been deprecated.
887862306a36Sopenharmony_ci			 *
887962306a36Sopenharmony_ci			 * cmsg_level    cmsg_type      cmsg_data[]
888062306a36Sopenharmony_ci			 * ------------  ------------   ---------------------
888162306a36Sopenharmony_ci			 * IPPROTO_SCTP  SCTP_SNDINFO    struct sctp_sndinfo
888262306a36Sopenharmony_ci			 */
888362306a36Sopenharmony_ci			if (cmsg->cmsg_len != CMSG_LEN(sizeof(struct sctp_sndinfo)))
888462306a36Sopenharmony_ci				return -EINVAL;
888562306a36Sopenharmony_ci
888662306a36Sopenharmony_ci			cmsgs->sinfo = CMSG_DATA(cmsg);
888762306a36Sopenharmony_ci
888862306a36Sopenharmony_ci			if (cmsgs->sinfo->snd_flags &
888962306a36Sopenharmony_ci			    ~(SCTP_UNORDERED | SCTP_ADDR_OVER |
889062306a36Sopenharmony_ci			      SCTP_SACK_IMMEDIATELY | SCTP_SENDALL |
889162306a36Sopenharmony_ci			      SCTP_PR_SCTP_MASK | SCTP_ABORT | SCTP_EOF))
889262306a36Sopenharmony_ci				return -EINVAL;
889362306a36Sopenharmony_ci			break;
889462306a36Sopenharmony_ci		case SCTP_PRINFO:
889562306a36Sopenharmony_ci			/* SCTP Socket API Extension
889662306a36Sopenharmony_ci			 * 5.3.7 SCTP PR-SCTP Information Structure (SCTP_PRINFO)
889762306a36Sopenharmony_ci			 *
889862306a36Sopenharmony_ci			 * This cmsghdr structure specifies SCTP options for sendmsg().
889962306a36Sopenharmony_ci			 *
890062306a36Sopenharmony_ci			 * cmsg_level    cmsg_type      cmsg_data[]
890162306a36Sopenharmony_ci			 * ------------  ------------   ---------------------
890262306a36Sopenharmony_ci			 * IPPROTO_SCTP  SCTP_PRINFO    struct sctp_prinfo
890362306a36Sopenharmony_ci			 */
890462306a36Sopenharmony_ci			if (cmsg->cmsg_len != CMSG_LEN(sizeof(struct sctp_prinfo)))
890562306a36Sopenharmony_ci				return -EINVAL;
890662306a36Sopenharmony_ci
890762306a36Sopenharmony_ci			cmsgs->prinfo = CMSG_DATA(cmsg);
890862306a36Sopenharmony_ci			if (cmsgs->prinfo->pr_policy & ~SCTP_PR_SCTP_MASK)
890962306a36Sopenharmony_ci				return -EINVAL;
891062306a36Sopenharmony_ci
891162306a36Sopenharmony_ci			if (cmsgs->prinfo->pr_policy == SCTP_PR_SCTP_NONE)
891262306a36Sopenharmony_ci				cmsgs->prinfo->pr_value = 0;
891362306a36Sopenharmony_ci			break;
891462306a36Sopenharmony_ci		case SCTP_AUTHINFO:
891562306a36Sopenharmony_ci			/* SCTP Socket API Extension
891662306a36Sopenharmony_ci			 * 5.3.8 SCTP AUTH Information Structure (SCTP_AUTHINFO)
891762306a36Sopenharmony_ci			 *
891862306a36Sopenharmony_ci			 * This cmsghdr structure specifies SCTP options for sendmsg().
891962306a36Sopenharmony_ci			 *
892062306a36Sopenharmony_ci			 * cmsg_level    cmsg_type      cmsg_data[]
892162306a36Sopenharmony_ci			 * ------------  ------------   ---------------------
892262306a36Sopenharmony_ci			 * IPPROTO_SCTP  SCTP_AUTHINFO  struct sctp_authinfo
892362306a36Sopenharmony_ci			 */
892462306a36Sopenharmony_ci			if (cmsg->cmsg_len != CMSG_LEN(sizeof(struct sctp_authinfo)))
892562306a36Sopenharmony_ci				return -EINVAL;
892662306a36Sopenharmony_ci
892762306a36Sopenharmony_ci			cmsgs->authinfo = CMSG_DATA(cmsg);
892862306a36Sopenharmony_ci			break;
892962306a36Sopenharmony_ci		case SCTP_DSTADDRV4:
893062306a36Sopenharmony_ci		case SCTP_DSTADDRV6:
893162306a36Sopenharmony_ci			/* SCTP Socket API Extension
893262306a36Sopenharmony_ci			 * 5.3.9/10 SCTP Destination IPv4/6 Address Structure (SCTP_DSTADDRV4/6)
893362306a36Sopenharmony_ci			 *
893462306a36Sopenharmony_ci			 * This cmsghdr structure specifies SCTP options for sendmsg().
893562306a36Sopenharmony_ci			 *
893662306a36Sopenharmony_ci			 * cmsg_level    cmsg_type         cmsg_data[]
893762306a36Sopenharmony_ci			 * ------------  ------------   ---------------------
893862306a36Sopenharmony_ci			 * IPPROTO_SCTP  SCTP_DSTADDRV4 struct in_addr
893962306a36Sopenharmony_ci			 * ------------  ------------   ---------------------
894062306a36Sopenharmony_ci			 * IPPROTO_SCTP  SCTP_DSTADDRV6 struct in6_addr
894162306a36Sopenharmony_ci			 */
894262306a36Sopenharmony_ci			cmsgs->addrs_msg = my_msg;
894362306a36Sopenharmony_ci			break;
894462306a36Sopenharmony_ci		default:
894562306a36Sopenharmony_ci			return -EINVAL;
894662306a36Sopenharmony_ci		}
894762306a36Sopenharmony_ci	}
894862306a36Sopenharmony_ci
894962306a36Sopenharmony_ci	return 0;
895062306a36Sopenharmony_ci}
895162306a36Sopenharmony_ci
895262306a36Sopenharmony_ci/*
895362306a36Sopenharmony_ci * Wait for a packet..
895462306a36Sopenharmony_ci * Note: This function is the same function as in core/datagram.c
895562306a36Sopenharmony_ci * with a few modifications to make lksctp work.
895662306a36Sopenharmony_ci */
895762306a36Sopenharmony_cistatic int sctp_wait_for_packet(struct sock *sk, int *err, long *timeo_p)
895862306a36Sopenharmony_ci{
895962306a36Sopenharmony_ci	int error;
896062306a36Sopenharmony_ci	DEFINE_WAIT(wait);
896162306a36Sopenharmony_ci
896262306a36Sopenharmony_ci	prepare_to_wait_exclusive(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE);
896362306a36Sopenharmony_ci
896462306a36Sopenharmony_ci	/* Socket errors? */
896562306a36Sopenharmony_ci	error = sock_error(sk);
896662306a36Sopenharmony_ci	if (error)
896762306a36Sopenharmony_ci		goto out;
896862306a36Sopenharmony_ci
896962306a36Sopenharmony_ci	if (!skb_queue_empty(&sk->sk_receive_queue))
897062306a36Sopenharmony_ci		goto ready;
897162306a36Sopenharmony_ci
897262306a36Sopenharmony_ci	/* Socket shut down?  */
897362306a36Sopenharmony_ci	if (sk->sk_shutdown & RCV_SHUTDOWN)
897462306a36Sopenharmony_ci		goto out;
897562306a36Sopenharmony_ci
897662306a36Sopenharmony_ci	/* Sequenced packets can come disconnected.  If so we report the
897762306a36Sopenharmony_ci	 * problem.
897862306a36Sopenharmony_ci	 */
897962306a36Sopenharmony_ci	error = -ENOTCONN;
898062306a36Sopenharmony_ci
898162306a36Sopenharmony_ci	/* Is there a good reason to think that we may receive some data?  */
898262306a36Sopenharmony_ci	if (list_empty(&sctp_sk(sk)->ep->asocs) && !sctp_sstate(sk, LISTENING))
898362306a36Sopenharmony_ci		goto out;
898462306a36Sopenharmony_ci
898562306a36Sopenharmony_ci	/* Handle signals.  */
898662306a36Sopenharmony_ci	if (signal_pending(current))
898762306a36Sopenharmony_ci		goto interrupted;
898862306a36Sopenharmony_ci
898962306a36Sopenharmony_ci	/* Let another process have a go.  Since we are going to sleep
899062306a36Sopenharmony_ci	 * anyway.  Note: This may cause odd behaviors if the message
899162306a36Sopenharmony_ci	 * does not fit in the user's buffer, but this seems to be the
899262306a36Sopenharmony_ci	 * only way to honor MSG_DONTWAIT realistically.
899362306a36Sopenharmony_ci	 */
899462306a36Sopenharmony_ci	release_sock(sk);
899562306a36Sopenharmony_ci	*timeo_p = schedule_timeout(*timeo_p);
899662306a36Sopenharmony_ci	lock_sock(sk);
899762306a36Sopenharmony_ci
899862306a36Sopenharmony_ciready:
899962306a36Sopenharmony_ci	finish_wait(sk_sleep(sk), &wait);
900062306a36Sopenharmony_ci	return 0;
900162306a36Sopenharmony_ci
900262306a36Sopenharmony_ciinterrupted:
900362306a36Sopenharmony_ci	error = sock_intr_errno(*timeo_p);
900462306a36Sopenharmony_ci
900562306a36Sopenharmony_ciout:
900662306a36Sopenharmony_ci	finish_wait(sk_sleep(sk), &wait);
900762306a36Sopenharmony_ci	*err = error;
900862306a36Sopenharmony_ci	return error;
900962306a36Sopenharmony_ci}
901062306a36Sopenharmony_ci
901162306a36Sopenharmony_ci/* Receive a datagram.
901262306a36Sopenharmony_ci * Note: This is pretty much the same routine as in core/datagram.c
901362306a36Sopenharmony_ci * with a few changes to make lksctp work.
901462306a36Sopenharmony_ci */
901562306a36Sopenharmony_cistruct sk_buff *sctp_skb_recv_datagram(struct sock *sk, int flags, int *err)
901662306a36Sopenharmony_ci{
901762306a36Sopenharmony_ci	int error;
901862306a36Sopenharmony_ci	struct sk_buff *skb;
901962306a36Sopenharmony_ci	long timeo;
902062306a36Sopenharmony_ci
902162306a36Sopenharmony_ci	timeo = sock_rcvtimeo(sk, flags & MSG_DONTWAIT);
902262306a36Sopenharmony_ci
902362306a36Sopenharmony_ci	pr_debug("%s: timeo:%ld, max:%ld\n", __func__, timeo,
902462306a36Sopenharmony_ci		 MAX_SCHEDULE_TIMEOUT);
902562306a36Sopenharmony_ci
902662306a36Sopenharmony_ci	do {
902762306a36Sopenharmony_ci		/* Again only user level code calls this function,
902862306a36Sopenharmony_ci		 * so nothing interrupt level
902962306a36Sopenharmony_ci		 * will suddenly eat the receive_queue.
903062306a36Sopenharmony_ci		 *
903162306a36Sopenharmony_ci		 *  Look at current nfs client by the way...
903262306a36Sopenharmony_ci		 *  However, this function was correct in any case. 8)
903362306a36Sopenharmony_ci		 */
903462306a36Sopenharmony_ci		if (flags & MSG_PEEK) {
903562306a36Sopenharmony_ci			skb = skb_peek(&sk->sk_receive_queue);
903662306a36Sopenharmony_ci			if (skb)
903762306a36Sopenharmony_ci				refcount_inc(&skb->users);
903862306a36Sopenharmony_ci		} else {
903962306a36Sopenharmony_ci			skb = __skb_dequeue(&sk->sk_receive_queue);
904062306a36Sopenharmony_ci		}
904162306a36Sopenharmony_ci
904262306a36Sopenharmony_ci		if (skb)
904362306a36Sopenharmony_ci			return skb;
904462306a36Sopenharmony_ci
904562306a36Sopenharmony_ci		/* Caller is allowed not to check sk->sk_err before calling. */
904662306a36Sopenharmony_ci		error = sock_error(sk);
904762306a36Sopenharmony_ci		if (error)
904862306a36Sopenharmony_ci			goto no_packet;
904962306a36Sopenharmony_ci
905062306a36Sopenharmony_ci		if (sk->sk_shutdown & RCV_SHUTDOWN)
905162306a36Sopenharmony_ci			break;
905262306a36Sopenharmony_ci
905362306a36Sopenharmony_ci
905462306a36Sopenharmony_ci		/* User doesn't want to wait.  */
905562306a36Sopenharmony_ci		error = -EAGAIN;
905662306a36Sopenharmony_ci		if (!timeo)
905762306a36Sopenharmony_ci			goto no_packet;
905862306a36Sopenharmony_ci	} while (sctp_wait_for_packet(sk, err, &timeo) == 0);
905962306a36Sopenharmony_ci
906062306a36Sopenharmony_ci	return NULL;
906162306a36Sopenharmony_ci
906262306a36Sopenharmony_cino_packet:
906362306a36Sopenharmony_ci	*err = error;
906462306a36Sopenharmony_ci	return NULL;
906562306a36Sopenharmony_ci}
906662306a36Sopenharmony_ci
906762306a36Sopenharmony_ci/* If sndbuf has changed, wake up per association sndbuf waiters.  */
906862306a36Sopenharmony_cistatic void __sctp_write_space(struct sctp_association *asoc)
906962306a36Sopenharmony_ci{
907062306a36Sopenharmony_ci	struct sock *sk = asoc->base.sk;
907162306a36Sopenharmony_ci
907262306a36Sopenharmony_ci	if (sctp_wspace(asoc) <= 0)
907362306a36Sopenharmony_ci		return;
907462306a36Sopenharmony_ci
907562306a36Sopenharmony_ci	if (waitqueue_active(&asoc->wait))
907662306a36Sopenharmony_ci		wake_up_interruptible(&asoc->wait);
907762306a36Sopenharmony_ci
907862306a36Sopenharmony_ci	if (sctp_writeable(sk)) {
907962306a36Sopenharmony_ci		struct socket_wq *wq;
908062306a36Sopenharmony_ci
908162306a36Sopenharmony_ci		rcu_read_lock();
908262306a36Sopenharmony_ci		wq = rcu_dereference(sk->sk_wq);
908362306a36Sopenharmony_ci		if (wq) {
908462306a36Sopenharmony_ci			if (waitqueue_active(&wq->wait))
908562306a36Sopenharmony_ci				wake_up_interruptible(&wq->wait);
908662306a36Sopenharmony_ci
908762306a36Sopenharmony_ci			/* Note that we try to include the Async I/O support
908862306a36Sopenharmony_ci			 * here by modeling from the current TCP/UDP code.
908962306a36Sopenharmony_ci			 * We have not tested with it yet.
909062306a36Sopenharmony_ci			 */
909162306a36Sopenharmony_ci			if (!(sk->sk_shutdown & SEND_SHUTDOWN))
909262306a36Sopenharmony_ci				sock_wake_async(wq, SOCK_WAKE_SPACE, POLL_OUT);
909362306a36Sopenharmony_ci		}
909462306a36Sopenharmony_ci		rcu_read_unlock();
909562306a36Sopenharmony_ci	}
909662306a36Sopenharmony_ci}
909762306a36Sopenharmony_ci
909862306a36Sopenharmony_cistatic void sctp_wake_up_waiters(struct sock *sk,
909962306a36Sopenharmony_ci				 struct sctp_association *asoc)
910062306a36Sopenharmony_ci{
910162306a36Sopenharmony_ci	struct sctp_association *tmp = asoc;
910262306a36Sopenharmony_ci
910362306a36Sopenharmony_ci	/* We do accounting for the sndbuf space per association,
910462306a36Sopenharmony_ci	 * so we only need to wake our own association.
910562306a36Sopenharmony_ci	 */
910662306a36Sopenharmony_ci	if (asoc->ep->sndbuf_policy)
910762306a36Sopenharmony_ci		return __sctp_write_space(asoc);
910862306a36Sopenharmony_ci
910962306a36Sopenharmony_ci	/* If association goes down and is just flushing its
911062306a36Sopenharmony_ci	 * outq, then just normally notify others.
911162306a36Sopenharmony_ci	 */
911262306a36Sopenharmony_ci	if (asoc->base.dead)
911362306a36Sopenharmony_ci		return sctp_write_space(sk);
911462306a36Sopenharmony_ci
911562306a36Sopenharmony_ci	/* Accounting for the sndbuf space is per socket, so we
911662306a36Sopenharmony_ci	 * need to wake up others, try to be fair and in case of
911762306a36Sopenharmony_ci	 * other associations, let them have a go first instead
911862306a36Sopenharmony_ci	 * of just doing a sctp_write_space() call.
911962306a36Sopenharmony_ci	 *
912062306a36Sopenharmony_ci	 * Note that we reach sctp_wake_up_waiters() only when
912162306a36Sopenharmony_ci	 * associations free up queued chunks, thus we are under
912262306a36Sopenharmony_ci	 * lock and the list of associations on a socket is
912362306a36Sopenharmony_ci	 * guaranteed not to change.
912462306a36Sopenharmony_ci	 */
912562306a36Sopenharmony_ci	for (tmp = list_next_entry(tmp, asocs); 1;
912662306a36Sopenharmony_ci	     tmp = list_next_entry(tmp, asocs)) {
912762306a36Sopenharmony_ci		/* Manually skip the head element. */
912862306a36Sopenharmony_ci		if (&tmp->asocs == &((sctp_sk(sk))->ep->asocs))
912962306a36Sopenharmony_ci			continue;
913062306a36Sopenharmony_ci		/* Wake up association. */
913162306a36Sopenharmony_ci		__sctp_write_space(tmp);
913262306a36Sopenharmony_ci		/* We've reached the end. */
913362306a36Sopenharmony_ci		if (tmp == asoc)
913462306a36Sopenharmony_ci			break;
913562306a36Sopenharmony_ci	}
913662306a36Sopenharmony_ci}
913762306a36Sopenharmony_ci
913862306a36Sopenharmony_ci/* Do accounting for the sndbuf space.
913962306a36Sopenharmony_ci * Decrement the used sndbuf space of the corresponding association by the
914062306a36Sopenharmony_ci * data size which was just transmitted(freed).
914162306a36Sopenharmony_ci */
914262306a36Sopenharmony_cistatic void sctp_wfree(struct sk_buff *skb)
914362306a36Sopenharmony_ci{
914462306a36Sopenharmony_ci	struct sctp_chunk *chunk = skb_shinfo(skb)->destructor_arg;
914562306a36Sopenharmony_ci	struct sctp_association *asoc = chunk->asoc;
914662306a36Sopenharmony_ci	struct sock *sk = asoc->base.sk;
914762306a36Sopenharmony_ci
914862306a36Sopenharmony_ci	sk_mem_uncharge(sk, skb->truesize);
914962306a36Sopenharmony_ci	sk_wmem_queued_add(sk, -(skb->truesize + sizeof(struct sctp_chunk)));
915062306a36Sopenharmony_ci	asoc->sndbuf_used -= skb->truesize + sizeof(struct sctp_chunk);
915162306a36Sopenharmony_ci	WARN_ON(refcount_sub_and_test(sizeof(struct sctp_chunk),
915262306a36Sopenharmony_ci				      &sk->sk_wmem_alloc));
915362306a36Sopenharmony_ci
915462306a36Sopenharmony_ci	if (chunk->shkey) {
915562306a36Sopenharmony_ci		struct sctp_shared_key *shkey = chunk->shkey;
915662306a36Sopenharmony_ci
915762306a36Sopenharmony_ci		/* refcnt == 2 and !list_empty mean after this release, it's
915862306a36Sopenharmony_ci		 * not being used anywhere, and it's time to notify userland
915962306a36Sopenharmony_ci		 * that this shkey can be freed if it's been deactivated.
916062306a36Sopenharmony_ci		 */
916162306a36Sopenharmony_ci		if (shkey->deactivated && !list_empty(&shkey->key_list) &&
916262306a36Sopenharmony_ci		    refcount_read(&shkey->refcnt) == 2) {
916362306a36Sopenharmony_ci			struct sctp_ulpevent *ev;
916462306a36Sopenharmony_ci
916562306a36Sopenharmony_ci			ev = sctp_ulpevent_make_authkey(asoc, shkey->key_id,
916662306a36Sopenharmony_ci							SCTP_AUTH_FREE_KEY,
916762306a36Sopenharmony_ci							GFP_KERNEL);
916862306a36Sopenharmony_ci			if (ev)
916962306a36Sopenharmony_ci				asoc->stream.si->enqueue_event(&asoc->ulpq, ev);
917062306a36Sopenharmony_ci		}
917162306a36Sopenharmony_ci		sctp_auth_shkey_release(chunk->shkey);
917262306a36Sopenharmony_ci	}
917362306a36Sopenharmony_ci
917462306a36Sopenharmony_ci	sock_wfree(skb);
917562306a36Sopenharmony_ci	sctp_wake_up_waiters(sk, asoc);
917662306a36Sopenharmony_ci
917762306a36Sopenharmony_ci	sctp_association_put(asoc);
917862306a36Sopenharmony_ci}
917962306a36Sopenharmony_ci
918062306a36Sopenharmony_ci/* Do accounting for the receive space on the socket.
918162306a36Sopenharmony_ci * Accounting for the association is done in ulpevent.c
918262306a36Sopenharmony_ci * We set this as a destructor for the cloned data skbs so that
918362306a36Sopenharmony_ci * accounting is done at the correct time.
918462306a36Sopenharmony_ci */
918562306a36Sopenharmony_civoid sctp_sock_rfree(struct sk_buff *skb)
918662306a36Sopenharmony_ci{
918762306a36Sopenharmony_ci	struct sock *sk = skb->sk;
918862306a36Sopenharmony_ci	struct sctp_ulpevent *event = sctp_skb2event(skb);
918962306a36Sopenharmony_ci
919062306a36Sopenharmony_ci	atomic_sub(event->rmem_len, &sk->sk_rmem_alloc);
919162306a36Sopenharmony_ci
919262306a36Sopenharmony_ci	/*
919362306a36Sopenharmony_ci	 * Mimic the behavior of sock_rfree
919462306a36Sopenharmony_ci	 */
919562306a36Sopenharmony_ci	sk_mem_uncharge(sk, event->rmem_len);
919662306a36Sopenharmony_ci}
919762306a36Sopenharmony_ci
919862306a36Sopenharmony_ci
919962306a36Sopenharmony_ci/* Helper function to wait for space in the sndbuf.  */
920062306a36Sopenharmony_cistatic int sctp_wait_for_sndbuf(struct sctp_association *asoc, long *timeo_p,
920162306a36Sopenharmony_ci				size_t msg_len)
920262306a36Sopenharmony_ci{
920362306a36Sopenharmony_ci	struct sock *sk = asoc->base.sk;
920462306a36Sopenharmony_ci	long current_timeo = *timeo_p;
920562306a36Sopenharmony_ci	DEFINE_WAIT(wait);
920662306a36Sopenharmony_ci	int err = 0;
920762306a36Sopenharmony_ci
920862306a36Sopenharmony_ci	pr_debug("%s: asoc:%p, timeo:%ld, msg_len:%zu\n", __func__, asoc,
920962306a36Sopenharmony_ci		 *timeo_p, msg_len);
921062306a36Sopenharmony_ci
921162306a36Sopenharmony_ci	/* Increment the association's refcnt.  */
921262306a36Sopenharmony_ci	sctp_association_hold(asoc);
921362306a36Sopenharmony_ci
921462306a36Sopenharmony_ci	/* Wait on the association specific sndbuf space. */
921562306a36Sopenharmony_ci	for (;;) {
921662306a36Sopenharmony_ci		prepare_to_wait_exclusive(&asoc->wait, &wait,
921762306a36Sopenharmony_ci					  TASK_INTERRUPTIBLE);
921862306a36Sopenharmony_ci		if (asoc->base.dead)
921962306a36Sopenharmony_ci			goto do_dead;
922062306a36Sopenharmony_ci		if (!*timeo_p)
922162306a36Sopenharmony_ci			goto do_nonblock;
922262306a36Sopenharmony_ci		if (sk->sk_err || asoc->state >= SCTP_STATE_SHUTDOWN_PENDING)
922362306a36Sopenharmony_ci			goto do_error;
922462306a36Sopenharmony_ci		if (signal_pending(current))
922562306a36Sopenharmony_ci			goto do_interrupted;
922662306a36Sopenharmony_ci		if ((int)msg_len <= sctp_wspace(asoc) &&
922762306a36Sopenharmony_ci		    sk_wmem_schedule(sk, msg_len))
922862306a36Sopenharmony_ci			break;
922962306a36Sopenharmony_ci
923062306a36Sopenharmony_ci		/* Let another process have a go.  Since we are going
923162306a36Sopenharmony_ci		 * to sleep anyway.
923262306a36Sopenharmony_ci		 */
923362306a36Sopenharmony_ci		release_sock(sk);
923462306a36Sopenharmony_ci		current_timeo = schedule_timeout(current_timeo);
923562306a36Sopenharmony_ci		lock_sock(sk);
923662306a36Sopenharmony_ci		if (sk != asoc->base.sk)
923762306a36Sopenharmony_ci			goto do_error;
923862306a36Sopenharmony_ci
923962306a36Sopenharmony_ci		*timeo_p = current_timeo;
924062306a36Sopenharmony_ci	}
924162306a36Sopenharmony_ci
924262306a36Sopenharmony_ciout:
924362306a36Sopenharmony_ci	finish_wait(&asoc->wait, &wait);
924462306a36Sopenharmony_ci
924562306a36Sopenharmony_ci	/* Release the association's refcnt.  */
924662306a36Sopenharmony_ci	sctp_association_put(asoc);
924762306a36Sopenharmony_ci
924862306a36Sopenharmony_ci	return err;
924962306a36Sopenharmony_ci
925062306a36Sopenharmony_cido_dead:
925162306a36Sopenharmony_ci	err = -ESRCH;
925262306a36Sopenharmony_ci	goto out;
925362306a36Sopenharmony_ci
925462306a36Sopenharmony_cido_error:
925562306a36Sopenharmony_ci	err = -EPIPE;
925662306a36Sopenharmony_ci	goto out;
925762306a36Sopenharmony_ci
925862306a36Sopenharmony_cido_interrupted:
925962306a36Sopenharmony_ci	err = sock_intr_errno(*timeo_p);
926062306a36Sopenharmony_ci	goto out;
926162306a36Sopenharmony_ci
926262306a36Sopenharmony_cido_nonblock:
926362306a36Sopenharmony_ci	err = -EAGAIN;
926462306a36Sopenharmony_ci	goto out;
926562306a36Sopenharmony_ci}
926662306a36Sopenharmony_ci
926762306a36Sopenharmony_civoid sctp_data_ready(struct sock *sk)
926862306a36Sopenharmony_ci{
926962306a36Sopenharmony_ci	struct socket_wq *wq;
927062306a36Sopenharmony_ci
927162306a36Sopenharmony_ci	trace_sk_data_ready(sk);
927262306a36Sopenharmony_ci
927362306a36Sopenharmony_ci	rcu_read_lock();
927462306a36Sopenharmony_ci	wq = rcu_dereference(sk->sk_wq);
927562306a36Sopenharmony_ci	if (skwq_has_sleeper(wq))
927662306a36Sopenharmony_ci		wake_up_interruptible_sync_poll(&wq->wait, EPOLLIN |
927762306a36Sopenharmony_ci						EPOLLRDNORM | EPOLLRDBAND);
927862306a36Sopenharmony_ci	sk_wake_async(sk, SOCK_WAKE_WAITD, POLL_IN);
927962306a36Sopenharmony_ci	rcu_read_unlock();
928062306a36Sopenharmony_ci}
928162306a36Sopenharmony_ci
928262306a36Sopenharmony_ci/* If socket sndbuf has changed, wake up all per association waiters.  */
928362306a36Sopenharmony_civoid sctp_write_space(struct sock *sk)
928462306a36Sopenharmony_ci{
928562306a36Sopenharmony_ci	struct sctp_association *asoc;
928662306a36Sopenharmony_ci
928762306a36Sopenharmony_ci	/* Wake up the tasks in each wait queue.  */
928862306a36Sopenharmony_ci	list_for_each_entry(asoc, &((sctp_sk(sk))->ep->asocs), asocs) {
928962306a36Sopenharmony_ci		__sctp_write_space(asoc);
929062306a36Sopenharmony_ci	}
929162306a36Sopenharmony_ci}
929262306a36Sopenharmony_ci
929362306a36Sopenharmony_ci/* Is there any sndbuf space available on the socket?
929462306a36Sopenharmony_ci *
929562306a36Sopenharmony_ci * Note that sk_wmem_alloc is the sum of the send buffers on all of the
929662306a36Sopenharmony_ci * associations on the same socket.  For a UDP-style socket with
929762306a36Sopenharmony_ci * multiple associations, it is possible for it to be "unwriteable"
929862306a36Sopenharmony_ci * prematurely.  I assume that this is acceptable because
929962306a36Sopenharmony_ci * a premature "unwriteable" is better than an accidental "writeable" which
930062306a36Sopenharmony_ci * would cause an unwanted block under certain circumstances.  For the 1-1
930162306a36Sopenharmony_ci * UDP-style sockets or TCP-style sockets, this code should work.
930262306a36Sopenharmony_ci *  - Daisy
930362306a36Sopenharmony_ci */
930462306a36Sopenharmony_cistatic bool sctp_writeable(const struct sock *sk)
930562306a36Sopenharmony_ci{
930662306a36Sopenharmony_ci	return READ_ONCE(sk->sk_sndbuf) > READ_ONCE(sk->sk_wmem_queued);
930762306a36Sopenharmony_ci}
930862306a36Sopenharmony_ci
930962306a36Sopenharmony_ci/* Wait for an association to go into ESTABLISHED state. If timeout is 0,
931062306a36Sopenharmony_ci * returns immediately with EINPROGRESS.
931162306a36Sopenharmony_ci */
931262306a36Sopenharmony_cistatic int sctp_wait_for_connect(struct sctp_association *asoc, long *timeo_p)
931362306a36Sopenharmony_ci{
931462306a36Sopenharmony_ci	struct sock *sk = asoc->base.sk;
931562306a36Sopenharmony_ci	int err = 0;
931662306a36Sopenharmony_ci	long current_timeo = *timeo_p;
931762306a36Sopenharmony_ci	DEFINE_WAIT(wait);
931862306a36Sopenharmony_ci
931962306a36Sopenharmony_ci	pr_debug("%s: asoc:%p, timeo:%ld\n", __func__, asoc, *timeo_p);
932062306a36Sopenharmony_ci
932162306a36Sopenharmony_ci	/* Increment the association's refcnt.  */
932262306a36Sopenharmony_ci	sctp_association_hold(asoc);
932362306a36Sopenharmony_ci
932462306a36Sopenharmony_ci	for (;;) {
932562306a36Sopenharmony_ci		prepare_to_wait_exclusive(&asoc->wait, &wait,
932662306a36Sopenharmony_ci					  TASK_INTERRUPTIBLE);
932762306a36Sopenharmony_ci		if (!*timeo_p)
932862306a36Sopenharmony_ci			goto do_nonblock;
932962306a36Sopenharmony_ci		if (sk->sk_shutdown & RCV_SHUTDOWN)
933062306a36Sopenharmony_ci			break;
933162306a36Sopenharmony_ci		if (sk->sk_err || asoc->state >= SCTP_STATE_SHUTDOWN_PENDING ||
933262306a36Sopenharmony_ci		    asoc->base.dead)
933362306a36Sopenharmony_ci			goto do_error;
933462306a36Sopenharmony_ci		if (signal_pending(current))
933562306a36Sopenharmony_ci			goto do_interrupted;
933662306a36Sopenharmony_ci
933762306a36Sopenharmony_ci		if (sctp_state(asoc, ESTABLISHED))
933862306a36Sopenharmony_ci			break;
933962306a36Sopenharmony_ci
934062306a36Sopenharmony_ci		/* Let another process have a go.  Since we are going
934162306a36Sopenharmony_ci		 * to sleep anyway.
934262306a36Sopenharmony_ci		 */
934362306a36Sopenharmony_ci		release_sock(sk);
934462306a36Sopenharmony_ci		current_timeo = schedule_timeout(current_timeo);
934562306a36Sopenharmony_ci		lock_sock(sk);
934662306a36Sopenharmony_ci
934762306a36Sopenharmony_ci		*timeo_p = current_timeo;
934862306a36Sopenharmony_ci	}
934962306a36Sopenharmony_ci
935062306a36Sopenharmony_ciout:
935162306a36Sopenharmony_ci	finish_wait(&asoc->wait, &wait);
935262306a36Sopenharmony_ci
935362306a36Sopenharmony_ci	/* Release the association's refcnt.  */
935462306a36Sopenharmony_ci	sctp_association_put(asoc);
935562306a36Sopenharmony_ci
935662306a36Sopenharmony_ci	return err;
935762306a36Sopenharmony_ci
935862306a36Sopenharmony_cido_error:
935962306a36Sopenharmony_ci	if (asoc->init_err_counter + 1 > asoc->max_init_attempts)
936062306a36Sopenharmony_ci		err = -ETIMEDOUT;
936162306a36Sopenharmony_ci	else
936262306a36Sopenharmony_ci		err = -ECONNREFUSED;
936362306a36Sopenharmony_ci	goto out;
936462306a36Sopenharmony_ci
936562306a36Sopenharmony_cido_interrupted:
936662306a36Sopenharmony_ci	err = sock_intr_errno(*timeo_p);
936762306a36Sopenharmony_ci	goto out;
936862306a36Sopenharmony_ci
936962306a36Sopenharmony_cido_nonblock:
937062306a36Sopenharmony_ci	err = -EINPROGRESS;
937162306a36Sopenharmony_ci	goto out;
937262306a36Sopenharmony_ci}
937362306a36Sopenharmony_ci
937462306a36Sopenharmony_cistatic int sctp_wait_for_accept(struct sock *sk, long timeo)
937562306a36Sopenharmony_ci{
937662306a36Sopenharmony_ci	struct sctp_endpoint *ep;
937762306a36Sopenharmony_ci	int err = 0;
937862306a36Sopenharmony_ci	DEFINE_WAIT(wait);
937962306a36Sopenharmony_ci
938062306a36Sopenharmony_ci	ep = sctp_sk(sk)->ep;
938162306a36Sopenharmony_ci
938262306a36Sopenharmony_ci
938362306a36Sopenharmony_ci	for (;;) {
938462306a36Sopenharmony_ci		prepare_to_wait_exclusive(sk_sleep(sk), &wait,
938562306a36Sopenharmony_ci					  TASK_INTERRUPTIBLE);
938662306a36Sopenharmony_ci
938762306a36Sopenharmony_ci		if (list_empty(&ep->asocs)) {
938862306a36Sopenharmony_ci			release_sock(sk);
938962306a36Sopenharmony_ci			timeo = schedule_timeout(timeo);
939062306a36Sopenharmony_ci			lock_sock(sk);
939162306a36Sopenharmony_ci		}
939262306a36Sopenharmony_ci
939362306a36Sopenharmony_ci		err = -EINVAL;
939462306a36Sopenharmony_ci		if (!sctp_sstate(sk, LISTENING))
939562306a36Sopenharmony_ci			break;
939662306a36Sopenharmony_ci
939762306a36Sopenharmony_ci		err = 0;
939862306a36Sopenharmony_ci		if (!list_empty(&ep->asocs))
939962306a36Sopenharmony_ci			break;
940062306a36Sopenharmony_ci
940162306a36Sopenharmony_ci		err = sock_intr_errno(timeo);
940262306a36Sopenharmony_ci		if (signal_pending(current))
940362306a36Sopenharmony_ci			break;
940462306a36Sopenharmony_ci
940562306a36Sopenharmony_ci		err = -EAGAIN;
940662306a36Sopenharmony_ci		if (!timeo)
940762306a36Sopenharmony_ci			break;
940862306a36Sopenharmony_ci	}
940962306a36Sopenharmony_ci
941062306a36Sopenharmony_ci	finish_wait(sk_sleep(sk), &wait);
941162306a36Sopenharmony_ci
941262306a36Sopenharmony_ci	return err;
941362306a36Sopenharmony_ci}
941462306a36Sopenharmony_ci
941562306a36Sopenharmony_cistatic void sctp_wait_for_close(struct sock *sk, long timeout)
941662306a36Sopenharmony_ci{
941762306a36Sopenharmony_ci	DEFINE_WAIT(wait);
941862306a36Sopenharmony_ci
941962306a36Sopenharmony_ci	do {
942062306a36Sopenharmony_ci		prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE);
942162306a36Sopenharmony_ci		if (list_empty(&sctp_sk(sk)->ep->asocs))
942262306a36Sopenharmony_ci			break;
942362306a36Sopenharmony_ci		release_sock(sk);
942462306a36Sopenharmony_ci		timeout = schedule_timeout(timeout);
942562306a36Sopenharmony_ci		lock_sock(sk);
942662306a36Sopenharmony_ci	} while (!signal_pending(current) && timeout);
942762306a36Sopenharmony_ci
942862306a36Sopenharmony_ci	finish_wait(sk_sleep(sk), &wait);
942962306a36Sopenharmony_ci}
943062306a36Sopenharmony_ci
943162306a36Sopenharmony_cistatic void sctp_skb_set_owner_r_frag(struct sk_buff *skb, struct sock *sk)
943262306a36Sopenharmony_ci{
943362306a36Sopenharmony_ci	struct sk_buff *frag;
943462306a36Sopenharmony_ci
943562306a36Sopenharmony_ci	if (!skb->data_len)
943662306a36Sopenharmony_ci		goto done;
943762306a36Sopenharmony_ci
943862306a36Sopenharmony_ci	/* Don't forget the fragments. */
943962306a36Sopenharmony_ci	skb_walk_frags(skb, frag)
944062306a36Sopenharmony_ci		sctp_skb_set_owner_r_frag(frag, sk);
944162306a36Sopenharmony_ci
944262306a36Sopenharmony_cidone:
944362306a36Sopenharmony_ci	sctp_skb_set_owner_r(skb, sk);
944462306a36Sopenharmony_ci}
944562306a36Sopenharmony_ci
944662306a36Sopenharmony_civoid sctp_copy_sock(struct sock *newsk, struct sock *sk,
944762306a36Sopenharmony_ci		    struct sctp_association *asoc)
944862306a36Sopenharmony_ci{
944962306a36Sopenharmony_ci	struct inet_sock *inet = inet_sk(sk);
945062306a36Sopenharmony_ci	struct inet_sock *newinet;
945162306a36Sopenharmony_ci	struct sctp_sock *sp = sctp_sk(sk);
945262306a36Sopenharmony_ci
945362306a36Sopenharmony_ci	newsk->sk_type = sk->sk_type;
945462306a36Sopenharmony_ci	newsk->sk_bound_dev_if = sk->sk_bound_dev_if;
945562306a36Sopenharmony_ci	newsk->sk_flags = sk->sk_flags;
945662306a36Sopenharmony_ci	newsk->sk_tsflags = sk->sk_tsflags;
945762306a36Sopenharmony_ci	newsk->sk_no_check_tx = sk->sk_no_check_tx;
945862306a36Sopenharmony_ci	newsk->sk_no_check_rx = sk->sk_no_check_rx;
945962306a36Sopenharmony_ci	newsk->sk_reuse = sk->sk_reuse;
946062306a36Sopenharmony_ci	sctp_sk(newsk)->reuse = sp->reuse;
946162306a36Sopenharmony_ci
946262306a36Sopenharmony_ci	newsk->sk_shutdown = sk->sk_shutdown;
946362306a36Sopenharmony_ci	newsk->sk_destruct = sk->sk_destruct;
946462306a36Sopenharmony_ci	newsk->sk_family = sk->sk_family;
946562306a36Sopenharmony_ci	newsk->sk_protocol = IPPROTO_SCTP;
946662306a36Sopenharmony_ci	newsk->sk_backlog_rcv = sk->sk_prot->backlog_rcv;
946762306a36Sopenharmony_ci	newsk->sk_sndbuf = sk->sk_sndbuf;
946862306a36Sopenharmony_ci	newsk->sk_rcvbuf = sk->sk_rcvbuf;
946962306a36Sopenharmony_ci	newsk->sk_lingertime = sk->sk_lingertime;
947062306a36Sopenharmony_ci	newsk->sk_rcvtimeo = sk->sk_rcvtimeo;
947162306a36Sopenharmony_ci	newsk->sk_sndtimeo = sk->sk_sndtimeo;
947262306a36Sopenharmony_ci	newsk->sk_rxhash = sk->sk_rxhash;
947362306a36Sopenharmony_ci
947462306a36Sopenharmony_ci	newinet = inet_sk(newsk);
947562306a36Sopenharmony_ci
947662306a36Sopenharmony_ci	/* Initialize sk's sport, dport, rcv_saddr and daddr for
947762306a36Sopenharmony_ci	 * getsockname() and getpeername()
947862306a36Sopenharmony_ci	 */
947962306a36Sopenharmony_ci	newinet->inet_sport = inet->inet_sport;
948062306a36Sopenharmony_ci	newinet->inet_saddr = inet->inet_saddr;
948162306a36Sopenharmony_ci	newinet->inet_rcv_saddr = inet->inet_rcv_saddr;
948262306a36Sopenharmony_ci	newinet->inet_dport = htons(asoc->peer.port);
948362306a36Sopenharmony_ci	newinet->pmtudisc = inet->pmtudisc;
948462306a36Sopenharmony_ci	atomic_set(&newinet->inet_id, get_random_u16());
948562306a36Sopenharmony_ci
948662306a36Sopenharmony_ci	newinet->uc_ttl = inet->uc_ttl;
948762306a36Sopenharmony_ci	inet_set_bit(MC_LOOP, newsk);
948862306a36Sopenharmony_ci	newinet->mc_ttl = 1;
948962306a36Sopenharmony_ci	newinet->mc_index = 0;
949062306a36Sopenharmony_ci	newinet->mc_list = NULL;
949162306a36Sopenharmony_ci
949262306a36Sopenharmony_ci	if (newsk->sk_flags & SK_FLAGS_TIMESTAMP)
949362306a36Sopenharmony_ci		net_enable_timestamp();
949462306a36Sopenharmony_ci
949562306a36Sopenharmony_ci	/* Set newsk security attributes from original sk and connection
949662306a36Sopenharmony_ci	 * security attribute from asoc.
949762306a36Sopenharmony_ci	 */
949862306a36Sopenharmony_ci	security_sctp_sk_clone(asoc, sk, newsk);
949962306a36Sopenharmony_ci}
950062306a36Sopenharmony_ci
950162306a36Sopenharmony_cistatic inline void sctp_copy_descendant(struct sock *sk_to,
950262306a36Sopenharmony_ci					const struct sock *sk_from)
950362306a36Sopenharmony_ci{
950462306a36Sopenharmony_ci	size_t ancestor_size = sizeof(struct inet_sock);
950562306a36Sopenharmony_ci
950662306a36Sopenharmony_ci	ancestor_size += sk_from->sk_prot->obj_size;
950762306a36Sopenharmony_ci	ancestor_size -= offsetof(struct sctp_sock, pd_lobby);
950862306a36Sopenharmony_ci	__inet_sk_copy_descendant(sk_to, sk_from, ancestor_size);
950962306a36Sopenharmony_ci}
951062306a36Sopenharmony_ci
951162306a36Sopenharmony_ci/* Populate the fields of the newsk from the oldsk and migrate the assoc
951262306a36Sopenharmony_ci * and its messages to the newsk.
951362306a36Sopenharmony_ci */
951462306a36Sopenharmony_cistatic int sctp_sock_migrate(struct sock *oldsk, struct sock *newsk,
951562306a36Sopenharmony_ci			     struct sctp_association *assoc,
951662306a36Sopenharmony_ci			     enum sctp_socket_type type)
951762306a36Sopenharmony_ci{
951862306a36Sopenharmony_ci	struct sctp_sock *oldsp = sctp_sk(oldsk);
951962306a36Sopenharmony_ci	struct sctp_sock *newsp = sctp_sk(newsk);
952062306a36Sopenharmony_ci	struct sctp_bind_bucket *pp; /* hash list port iterator */
952162306a36Sopenharmony_ci	struct sctp_endpoint *newep = newsp->ep;
952262306a36Sopenharmony_ci	struct sk_buff *skb, *tmp;
952362306a36Sopenharmony_ci	struct sctp_ulpevent *event;
952462306a36Sopenharmony_ci	struct sctp_bind_hashbucket *head;
952562306a36Sopenharmony_ci	int err;
952662306a36Sopenharmony_ci
952762306a36Sopenharmony_ci	/* Migrate socket buffer sizes and all the socket level options to the
952862306a36Sopenharmony_ci	 * new socket.
952962306a36Sopenharmony_ci	 */
953062306a36Sopenharmony_ci	newsk->sk_sndbuf = oldsk->sk_sndbuf;
953162306a36Sopenharmony_ci	newsk->sk_rcvbuf = oldsk->sk_rcvbuf;
953262306a36Sopenharmony_ci	/* Brute force copy old sctp opt. */
953362306a36Sopenharmony_ci	sctp_copy_descendant(newsk, oldsk);
953462306a36Sopenharmony_ci
953562306a36Sopenharmony_ci	/* Restore the ep value that was overwritten with the above structure
953662306a36Sopenharmony_ci	 * copy.
953762306a36Sopenharmony_ci	 */
953862306a36Sopenharmony_ci	newsp->ep = newep;
953962306a36Sopenharmony_ci	newsp->hmac = NULL;
954062306a36Sopenharmony_ci
954162306a36Sopenharmony_ci	/* Hook this new socket in to the bind_hash list. */
954262306a36Sopenharmony_ci	head = &sctp_port_hashtable[sctp_phashfn(sock_net(oldsk),
954362306a36Sopenharmony_ci						 inet_sk(oldsk)->inet_num)];
954462306a36Sopenharmony_ci	spin_lock_bh(&head->lock);
954562306a36Sopenharmony_ci	pp = sctp_sk(oldsk)->bind_hash;
954662306a36Sopenharmony_ci	sk_add_bind_node(newsk, &pp->owner);
954762306a36Sopenharmony_ci	sctp_sk(newsk)->bind_hash = pp;
954862306a36Sopenharmony_ci	inet_sk(newsk)->inet_num = inet_sk(oldsk)->inet_num;
954962306a36Sopenharmony_ci	spin_unlock_bh(&head->lock);
955062306a36Sopenharmony_ci
955162306a36Sopenharmony_ci	/* Copy the bind_addr list from the original endpoint to the new
955262306a36Sopenharmony_ci	 * endpoint so that we can handle restarts properly
955362306a36Sopenharmony_ci	 */
955462306a36Sopenharmony_ci	err = sctp_bind_addr_dup(&newsp->ep->base.bind_addr,
955562306a36Sopenharmony_ci				 &oldsp->ep->base.bind_addr, GFP_KERNEL);
955662306a36Sopenharmony_ci	if (err)
955762306a36Sopenharmony_ci		return err;
955862306a36Sopenharmony_ci
955962306a36Sopenharmony_ci	/* New ep's auth_hmacs should be set if old ep's is set, in case
956062306a36Sopenharmony_ci	 * that net->sctp.auth_enable has been changed to 0 by users and
956162306a36Sopenharmony_ci	 * new ep's auth_hmacs couldn't be set in sctp_endpoint_init().
956262306a36Sopenharmony_ci	 */
956362306a36Sopenharmony_ci	if (oldsp->ep->auth_hmacs) {
956462306a36Sopenharmony_ci		err = sctp_auth_init_hmacs(newsp->ep, GFP_KERNEL);
956562306a36Sopenharmony_ci		if (err)
956662306a36Sopenharmony_ci			return err;
956762306a36Sopenharmony_ci	}
956862306a36Sopenharmony_ci
956962306a36Sopenharmony_ci	sctp_auto_asconf_init(newsp);
957062306a36Sopenharmony_ci
957162306a36Sopenharmony_ci	/* Move any messages in the old socket's receive queue that are for the
957262306a36Sopenharmony_ci	 * peeled off association to the new socket's receive queue.
957362306a36Sopenharmony_ci	 */
957462306a36Sopenharmony_ci	sctp_skb_for_each(skb, &oldsk->sk_receive_queue, tmp) {
957562306a36Sopenharmony_ci		event = sctp_skb2event(skb);
957662306a36Sopenharmony_ci		if (event->asoc == assoc) {
957762306a36Sopenharmony_ci			__skb_unlink(skb, &oldsk->sk_receive_queue);
957862306a36Sopenharmony_ci			__skb_queue_tail(&newsk->sk_receive_queue, skb);
957962306a36Sopenharmony_ci			sctp_skb_set_owner_r_frag(skb, newsk);
958062306a36Sopenharmony_ci		}
958162306a36Sopenharmony_ci	}
958262306a36Sopenharmony_ci
958362306a36Sopenharmony_ci	/* Clean up any messages pending delivery due to partial
958462306a36Sopenharmony_ci	 * delivery.   Three cases:
958562306a36Sopenharmony_ci	 * 1) No partial deliver;  no work.
958662306a36Sopenharmony_ci	 * 2) Peeling off partial delivery; keep pd_lobby in new pd_lobby.
958762306a36Sopenharmony_ci	 * 3) Peeling off non-partial delivery; move pd_lobby to receive_queue.
958862306a36Sopenharmony_ci	 */
958962306a36Sopenharmony_ci	atomic_set(&sctp_sk(newsk)->pd_mode, assoc->ulpq.pd_mode);
959062306a36Sopenharmony_ci
959162306a36Sopenharmony_ci	if (atomic_read(&sctp_sk(oldsk)->pd_mode)) {
959262306a36Sopenharmony_ci		struct sk_buff_head *queue;
959362306a36Sopenharmony_ci
959462306a36Sopenharmony_ci		/* Decide which queue to move pd_lobby skbs to. */
959562306a36Sopenharmony_ci		if (assoc->ulpq.pd_mode) {
959662306a36Sopenharmony_ci			queue = &newsp->pd_lobby;
959762306a36Sopenharmony_ci		} else
959862306a36Sopenharmony_ci			queue = &newsk->sk_receive_queue;
959962306a36Sopenharmony_ci
960062306a36Sopenharmony_ci		/* Walk through the pd_lobby, looking for skbs that
960162306a36Sopenharmony_ci		 * need moved to the new socket.
960262306a36Sopenharmony_ci		 */
960362306a36Sopenharmony_ci		sctp_skb_for_each(skb, &oldsp->pd_lobby, tmp) {
960462306a36Sopenharmony_ci			event = sctp_skb2event(skb);
960562306a36Sopenharmony_ci			if (event->asoc == assoc) {
960662306a36Sopenharmony_ci				__skb_unlink(skb, &oldsp->pd_lobby);
960762306a36Sopenharmony_ci				__skb_queue_tail(queue, skb);
960862306a36Sopenharmony_ci				sctp_skb_set_owner_r_frag(skb, newsk);
960962306a36Sopenharmony_ci			}
961062306a36Sopenharmony_ci		}
961162306a36Sopenharmony_ci
961262306a36Sopenharmony_ci		/* Clear up any skbs waiting for the partial
961362306a36Sopenharmony_ci		 * delivery to finish.
961462306a36Sopenharmony_ci		 */
961562306a36Sopenharmony_ci		if (assoc->ulpq.pd_mode)
961662306a36Sopenharmony_ci			sctp_clear_pd(oldsk, NULL);
961762306a36Sopenharmony_ci
961862306a36Sopenharmony_ci	}
961962306a36Sopenharmony_ci
962062306a36Sopenharmony_ci	sctp_for_each_rx_skb(assoc, newsk, sctp_skb_set_owner_r_frag);
962162306a36Sopenharmony_ci
962262306a36Sopenharmony_ci	/* Set the type of socket to indicate that it is peeled off from the
962362306a36Sopenharmony_ci	 * original UDP-style socket or created with the accept() call on a
962462306a36Sopenharmony_ci	 * TCP-style socket..
962562306a36Sopenharmony_ci	 */
962662306a36Sopenharmony_ci	newsp->type = type;
962762306a36Sopenharmony_ci
962862306a36Sopenharmony_ci	/* Mark the new socket "in-use" by the user so that any packets
962962306a36Sopenharmony_ci	 * that may arrive on the association after we've moved it are
963062306a36Sopenharmony_ci	 * queued to the backlog.  This prevents a potential race between
963162306a36Sopenharmony_ci	 * backlog processing on the old socket and new-packet processing
963262306a36Sopenharmony_ci	 * on the new socket.
963362306a36Sopenharmony_ci	 *
963462306a36Sopenharmony_ci	 * The caller has just allocated newsk so we can guarantee that other
963562306a36Sopenharmony_ci	 * paths won't try to lock it and then oldsk.
963662306a36Sopenharmony_ci	 */
963762306a36Sopenharmony_ci	lock_sock_nested(newsk, SINGLE_DEPTH_NESTING);
963862306a36Sopenharmony_ci	sctp_for_each_tx_datachunk(assoc, true, sctp_clear_owner_w);
963962306a36Sopenharmony_ci	sctp_assoc_migrate(assoc, newsk);
964062306a36Sopenharmony_ci	sctp_for_each_tx_datachunk(assoc, false, sctp_set_owner_w);
964162306a36Sopenharmony_ci
964262306a36Sopenharmony_ci	/* If the association on the newsk is already closed before accept()
964362306a36Sopenharmony_ci	 * is called, set RCV_SHUTDOWN flag.
964462306a36Sopenharmony_ci	 */
964562306a36Sopenharmony_ci	if (sctp_state(assoc, CLOSED) && sctp_style(newsk, TCP)) {
964662306a36Sopenharmony_ci		inet_sk_set_state(newsk, SCTP_SS_CLOSED);
964762306a36Sopenharmony_ci		newsk->sk_shutdown |= RCV_SHUTDOWN;
964862306a36Sopenharmony_ci	} else {
964962306a36Sopenharmony_ci		inet_sk_set_state(newsk, SCTP_SS_ESTABLISHED);
965062306a36Sopenharmony_ci	}
965162306a36Sopenharmony_ci
965262306a36Sopenharmony_ci	release_sock(newsk);
965362306a36Sopenharmony_ci
965462306a36Sopenharmony_ci	return 0;
965562306a36Sopenharmony_ci}
965662306a36Sopenharmony_ci
965762306a36Sopenharmony_ci
965862306a36Sopenharmony_ci/* This proto struct describes the ULP interface for SCTP.  */
965962306a36Sopenharmony_cistruct proto sctp_prot = {
966062306a36Sopenharmony_ci	.name        =	"SCTP",
966162306a36Sopenharmony_ci	.owner       =	THIS_MODULE,
966262306a36Sopenharmony_ci	.close       =	sctp_close,
966362306a36Sopenharmony_ci	.disconnect  =	sctp_disconnect,
966462306a36Sopenharmony_ci	.accept      =	sctp_accept,
966562306a36Sopenharmony_ci	.ioctl       =	sctp_ioctl,
966662306a36Sopenharmony_ci	.init        =	sctp_init_sock,
966762306a36Sopenharmony_ci	.destroy     =	sctp_destroy_sock,
966862306a36Sopenharmony_ci	.shutdown    =	sctp_shutdown,
966962306a36Sopenharmony_ci	.setsockopt  =	sctp_setsockopt,
967062306a36Sopenharmony_ci	.getsockopt  =	sctp_getsockopt,
967162306a36Sopenharmony_ci	.bpf_bypass_getsockopt	= sctp_bpf_bypass_getsockopt,
967262306a36Sopenharmony_ci	.sendmsg     =	sctp_sendmsg,
967362306a36Sopenharmony_ci	.recvmsg     =	sctp_recvmsg,
967462306a36Sopenharmony_ci	.bind        =	sctp_bind,
967562306a36Sopenharmony_ci	.bind_add    =  sctp_bind_add,
967662306a36Sopenharmony_ci	.backlog_rcv =	sctp_backlog_rcv,
967762306a36Sopenharmony_ci	.hash        =	sctp_hash,
967862306a36Sopenharmony_ci	.unhash      =	sctp_unhash,
967962306a36Sopenharmony_ci	.no_autobind =	true,
968062306a36Sopenharmony_ci	.obj_size    =  sizeof(struct sctp_sock),
968162306a36Sopenharmony_ci	.useroffset  =  offsetof(struct sctp_sock, subscribe),
968262306a36Sopenharmony_ci	.usersize    =  offsetof(struct sctp_sock, initmsg) -
968362306a36Sopenharmony_ci				offsetof(struct sctp_sock, subscribe) +
968462306a36Sopenharmony_ci				sizeof_field(struct sctp_sock, initmsg),
968562306a36Sopenharmony_ci	.sysctl_mem  =  sysctl_sctp_mem,
968662306a36Sopenharmony_ci	.sysctl_rmem =  sysctl_sctp_rmem,
968762306a36Sopenharmony_ci	.sysctl_wmem =  sysctl_sctp_wmem,
968862306a36Sopenharmony_ci	.memory_pressure = &sctp_memory_pressure,
968962306a36Sopenharmony_ci	.enter_memory_pressure = sctp_enter_memory_pressure,
969062306a36Sopenharmony_ci
969162306a36Sopenharmony_ci	.memory_allocated = &sctp_memory_allocated,
969262306a36Sopenharmony_ci	.per_cpu_fw_alloc = &sctp_memory_per_cpu_fw_alloc,
969362306a36Sopenharmony_ci
969462306a36Sopenharmony_ci	.sockets_allocated = &sctp_sockets_allocated,
969562306a36Sopenharmony_ci};
969662306a36Sopenharmony_ci
969762306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
969862306a36Sopenharmony_ci
969962306a36Sopenharmony_cistatic void sctp_v6_destruct_sock(struct sock *sk)
970062306a36Sopenharmony_ci{
970162306a36Sopenharmony_ci	sctp_destruct_common(sk);
970262306a36Sopenharmony_ci	inet6_sock_destruct(sk);
970362306a36Sopenharmony_ci}
970462306a36Sopenharmony_ci
970562306a36Sopenharmony_cistatic int sctp_v6_init_sock(struct sock *sk)
970662306a36Sopenharmony_ci{
970762306a36Sopenharmony_ci	int ret = sctp_init_sock(sk);
970862306a36Sopenharmony_ci
970962306a36Sopenharmony_ci	if (!ret)
971062306a36Sopenharmony_ci		sk->sk_destruct = sctp_v6_destruct_sock;
971162306a36Sopenharmony_ci
971262306a36Sopenharmony_ci	return ret;
971362306a36Sopenharmony_ci}
971462306a36Sopenharmony_ci
971562306a36Sopenharmony_cistruct proto sctpv6_prot = {
971662306a36Sopenharmony_ci	.name		= "SCTPv6",
971762306a36Sopenharmony_ci	.owner		= THIS_MODULE,
971862306a36Sopenharmony_ci	.close		= sctp_close,
971962306a36Sopenharmony_ci	.disconnect	= sctp_disconnect,
972062306a36Sopenharmony_ci	.accept		= sctp_accept,
972162306a36Sopenharmony_ci	.ioctl		= sctp_ioctl,
972262306a36Sopenharmony_ci	.init		= sctp_v6_init_sock,
972362306a36Sopenharmony_ci	.destroy	= sctp_destroy_sock,
972462306a36Sopenharmony_ci	.shutdown	= sctp_shutdown,
972562306a36Sopenharmony_ci	.setsockopt	= sctp_setsockopt,
972662306a36Sopenharmony_ci	.getsockopt	= sctp_getsockopt,
972762306a36Sopenharmony_ci	.bpf_bypass_getsockopt	= sctp_bpf_bypass_getsockopt,
972862306a36Sopenharmony_ci	.sendmsg	= sctp_sendmsg,
972962306a36Sopenharmony_ci	.recvmsg	= sctp_recvmsg,
973062306a36Sopenharmony_ci	.bind		= sctp_bind,
973162306a36Sopenharmony_ci	.bind_add	= sctp_bind_add,
973262306a36Sopenharmony_ci	.backlog_rcv	= sctp_backlog_rcv,
973362306a36Sopenharmony_ci	.hash		= sctp_hash,
973462306a36Sopenharmony_ci	.unhash		= sctp_unhash,
973562306a36Sopenharmony_ci	.no_autobind	= true,
973662306a36Sopenharmony_ci	.obj_size	= sizeof(struct sctp6_sock),
973762306a36Sopenharmony_ci	.ipv6_pinfo_offset = offsetof(struct sctp6_sock, inet6),
973862306a36Sopenharmony_ci	.useroffset	= offsetof(struct sctp6_sock, sctp.subscribe),
973962306a36Sopenharmony_ci	.usersize	= offsetof(struct sctp6_sock, sctp.initmsg) -
974062306a36Sopenharmony_ci				offsetof(struct sctp6_sock, sctp.subscribe) +
974162306a36Sopenharmony_ci				sizeof_field(struct sctp6_sock, sctp.initmsg),
974262306a36Sopenharmony_ci	.sysctl_mem	= sysctl_sctp_mem,
974362306a36Sopenharmony_ci	.sysctl_rmem	= sysctl_sctp_rmem,
974462306a36Sopenharmony_ci	.sysctl_wmem	= sysctl_sctp_wmem,
974562306a36Sopenharmony_ci	.memory_pressure = &sctp_memory_pressure,
974662306a36Sopenharmony_ci	.enter_memory_pressure = sctp_enter_memory_pressure,
974762306a36Sopenharmony_ci
974862306a36Sopenharmony_ci	.memory_allocated = &sctp_memory_allocated,
974962306a36Sopenharmony_ci	.per_cpu_fw_alloc = &sctp_memory_per_cpu_fw_alloc,
975062306a36Sopenharmony_ci
975162306a36Sopenharmony_ci	.sockets_allocated = &sctp_sockets_allocated,
975262306a36Sopenharmony_ci};
975362306a36Sopenharmony_ci#endif /* IS_ENABLED(CONFIG_IPV6) */
9754