xref: /kernel/linux/linux-5.10/net/sctp/socket.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/* SCTP kernel implementation
38c2ecf20Sopenharmony_ci * (C) Copyright IBM Corp. 2001, 2004
48c2ecf20Sopenharmony_ci * Copyright (c) 1999-2000 Cisco, Inc.
58c2ecf20Sopenharmony_ci * Copyright (c) 1999-2001 Motorola, Inc.
68c2ecf20Sopenharmony_ci * Copyright (c) 2001-2003 Intel Corp.
78c2ecf20Sopenharmony_ci * Copyright (c) 2001-2002 Nokia, Inc.
88c2ecf20Sopenharmony_ci * Copyright (c) 2001 La Monte H.P. Yarroll
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * This file is part of the SCTP kernel implementation
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci * These functions interface with the sockets layer to implement the
138c2ecf20Sopenharmony_ci * SCTP Extensions for the Sockets API.
148c2ecf20Sopenharmony_ci *
158c2ecf20Sopenharmony_ci * Note that the descriptions from the specification are USER level
168c2ecf20Sopenharmony_ci * functions--this file is the functions which populate the struct proto
178c2ecf20Sopenharmony_ci * for SCTP which is the BOTTOM of the sockets interface.
188c2ecf20Sopenharmony_ci *
198c2ecf20Sopenharmony_ci * Please send any bug reports or fixes you make to the
208c2ecf20Sopenharmony_ci * email address(es):
218c2ecf20Sopenharmony_ci *    lksctp developers <linux-sctp@vger.kernel.org>
228c2ecf20Sopenharmony_ci *
238c2ecf20Sopenharmony_ci * Written or modified by:
248c2ecf20Sopenharmony_ci *    La Monte H.P. Yarroll <piggy@acm.org>
258c2ecf20Sopenharmony_ci *    Narasimha Budihal     <narsi@refcode.org>
268c2ecf20Sopenharmony_ci *    Karl Knutson          <karl@athena.chicago.il.us>
278c2ecf20Sopenharmony_ci *    Jon Grimm             <jgrimm@us.ibm.com>
288c2ecf20Sopenharmony_ci *    Xingang Guo           <xingang.guo@intel.com>
298c2ecf20Sopenharmony_ci *    Daisy Chang           <daisyc@us.ibm.com>
308c2ecf20Sopenharmony_ci *    Sridhar Samudrala     <samudrala@us.ibm.com>
318c2ecf20Sopenharmony_ci *    Inaky Perez-Gonzalez  <inaky.gonzalez@intel.com>
328c2ecf20Sopenharmony_ci *    Ardelle Fan	    <ardelle.fan@intel.com>
338c2ecf20Sopenharmony_ci *    Ryan Layer	    <rmlayer@us.ibm.com>
348c2ecf20Sopenharmony_ci *    Anup Pemmaiah         <pemmaiah@cc.usu.edu>
358c2ecf20Sopenharmony_ci *    Kevin Gao             <kevin.gao@intel.com>
368c2ecf20Sopenharmony_ci */
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci#include <crypto/hash.h>
418c2ecf20Sopenharmony_ci#include <linux/types.h>
428c2ecf20Sopenharmony_ci#include <linux/kernel.h>
438c2ecf20Sopenharmony_ci#include <linux/wait.h>
448c2ecf20Sopenharmony_ci#include <linux/time.h>
458c2ecf20Sopenharmony_ci#include <linux/sched/signal.h>
468c2ecf20Sopenharmony_ci#include <linux/ip.h>
478c2ecf20Sopenharmony_ci#include <linux/capability.h>
488c2ecf20Sopenharmony_ci#include <linux/fcntl.h>
498c2ecf20Sopenharmony_ci#include <linux/poll.h>
508c2ecf20Sopenharmony_ci#include <linux/init.h>
518c2ecf20Sopenharmony_ci#include <linux/slab.h>
528c2ecf20Sopenharmony_ci#include <linux/file.h>
538c2ecf20Sopenharmony_ci#include <linux/compat.h>
548c2ecf20Sopenharmony_ci#include <linux/rhashtable.h>
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci#include <net/ip.h>
578c2ecf20Sopenharmony_ci#include <net/icmp.h>
588c2ecf20Sopenharmony_ci#include <net/route.h>
598c2ecf20Sopenharmony_ci#include <net/ipv6.h>
608c2ecf20Sopenharmony_ci#include <net/inet_common.h>
618c2ecf20Sopenharmony_ci#include <net/busy_poll.h>
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci#include <linux/socket.h> /* for sa_family_t */
648c2ecf20Sopenharmony_ci#include <linux/export.h>
658c2ecf20Sopenharmony_ci#include <net/sock.h>
668c2ecf20Sopenharmony_ci#include <net/sctp/sctp.h>
678c2ecf20Sopenharmony_ci#include <net/sctp/sm.h>
688c2ecf20Sopenharmony_ci#include <net/sctp/stream_sched.h>
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci/* Forward declarations for internal helper functions. */
718c2ecf20Sopenharmony_cistatic bool sctp_writeable(const struct sock *sk);
728c2ecf20Sopenharmony_cistatic void sctp_wfree(struct sk_buff *skb);
738c2ecf20Sopenharmony_cistatic int sctp_wait_for_sndbuf(struct sctp_association *asoc, long *timeo_p,
748c2ecf20Sopenharmony_ci				size_t msg_len);
758c2ecf20Sopenharmony_cistatic int sctp_wait_for_packet(struct sock *sk, int *err, long *timeo_p);
768c2ecf20Sopenharmony_cistatic int sctp_wait_for_connect(struct sctp_association *, long *timeo_p);
778c2ecf20Sopenharmony_cistatic int sctp_wait_for_accept(struct sock *sk, long timeo);
788c2ecf20Sopenharmony_cistatic void sctp_wait_for_close(struct sock *sk, long timeo);
798c2ecf20Sopenharmony_cistatic void sctp_destruct_sock(struct sock *sk);
808c2ecf20Sopenharmony_cistatic struct sctp_af *sctp_sockaddr_af(struct sctp_sock *opt,
818c2ecf20Sopenharmony_ci					union sctp_addr *addr, int len);
828c2ecf20Sopenharmony_cistatic int sctp_bindx_add(struct sock *, struct sockaddr *, int);
838c2ecf20Sopenharmony_cistatic int sctp_bindx_rem(struct sock *, struct sockaddr *, int);
848c2ecf20Sopenharmony_cistatic int sctp_send_asconf_add_ip(struct sock *, struct sockaddr *, int);
858c2ecf20Sopenharmony_cistatic int sctp_send_asconf_del_ip(struct sock *, struct sockaddr *, int);
868c2ecf20Sopenharmony_cistatic int sctp_send_asconf(struct sctp_association *asoc,
878c2ecf20Sopenharmony_ci			    struct sctp_chunk *chunk);
888c2ecf20Sopenharmony_cistatic int sctp_do_bind(struct sock *, union sctp_addr *, int);
898c2ecf20Sopenharmony_cistatic int sctp_autobind(struct sock *sk);
908c2ecf20Sopenharmony_cistatic int sctp_sock_migrate(struct sock *oldsk, struct sock *newsk,
918c2ecf20Sopenharmony_ci			     struct sctp_association *assoc,
928c2ecf20Sopenharmony_ci			     enum sctp_socket_type type);
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_cistatic unsigned long sctp_memory_pressure;
958c2ecf20Sopenharmony_cistatic atomic_long_t sctp_memory_allocated;
968c2ecf20Sopenharmony_cistruct percpu_counter sctp_sockets_allocated;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_cistatic void sctp_enter_memory_pressure(struct sock *sk)
998c2ecf20Sopenharmony_ci{
1008c2ecf20Sopenharmony_ci	WRITE_ONCE(sctp_memory_pressure, 1);
1018c2ecf20Sopenharmony_ci}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci/* Get the sndbuf space available at the time on the association.  */
1058c2ecf20Sopenharmony_cistatic inline int sctp_wspace(struct sctp_association *asoc)
1068c2ecf20Sopenharmony_ci{
1078c2ecf20Sopenharmony_ci	struct sock *sk = asoc->base.sk;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	return asoc->ep->sndbuf_policy ? sk->sk_sndbuf - asoc->sndbuf_used
1108c2ecf20Sopenharmony_ci				       : sk_stream_wspace(sk);
1118c2ecf20Sopenharmony_ci}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci/* Increment the used sndbuf space count of the corresponding association by
1148c2ecf20Sopenharmony_ci * the size of the outgoing data chunk.
1158c2ecf20Sopenharmony_ci * Also, set the skb destructor for sndbuf accounting later.
1168c2ecf20Sopenharmony_ci *
1178c2ecf20Sopenharmony_ci * Since it is always 1-1 between chunk and skb, and also a new skb is always
1188c2ecf20Sopenharmony_ci * allocated for chunk bundling in sctp_packet_transmit(), we can use the
1198c2ecf20Sopenharmony_ci * destructor in the data chunk skb for the purpose of the sndbuf space
1208c2ecf20Sopenharmony_ci * tracking.
1218c2ecf20Sopenharmony_ci */
1228c2ecf20Sopenharmony_cistatic inline void sctp_set_owner_w(struct sctp_chunk *chunk)
1238c2ecf20Sopenharmony_ci{
1248c2ecf20Sopenharmony_ci	struct sctp_association *asoc = chunk->asoc;
1258c2ecf20Sopenharmony_ci	struct sock *sk = asoc->base.sk;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	/* The sndbuf space is tracked per association.  */
1288c2ecf20Sopenharmony_ci	sctp_association_hold(asoc);
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	if (chunk->shkey)
1318c2ecf20Sopenharmony_ci		sctp_auth_shkey_hold(chunk->shkey);
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	skb_set_owner_w(chunk->skb, sk);
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	chunk->skb->destructor = sctp_wfree;
1368c2ecf20Sopenharmony_ci	/* Save the chunk pointer in skb for sctp_wfree to use later.  */
1378c2ecf20Sopenharmony_ci	skb_shinfo(chunk->skb)->destructor_arg = chunk;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	refcount_add(sizeof(struct sctp_chunk), &sk->sk_wmem_alloc);
1408c2ecf20Sopenharmony_ci	asoc->sndbuf_used += chunk->skb->truesize + sizeof(struct sctp_chunk);
1418c2ecf20Sopenharmony_ci	sk_wmem_queued_add(sk, chunk->skb->truesize + sizeof(struct sctp_chunk));
1428c2ecf20Sopenharmony_ci	sk_mem_charge(sk, chunk->skb->truesize);
1438c2ecf20Sopenharmony_ci}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_cistatic void sctp_clear_owner_w(struct sctp_chunk *chunk)
1468c2ecf20Sopenharmony_ci{
1478c2ecf20Sopenharmony_ci	skb_orphan(chunk->skb);
1488c2ecf20Sopenharmony_ci}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci#define traverse_and_process()	\
1518c2ecf20Sopenharmony_cido {				\
1528c2ecf20Sopenharmony_ci	msg = chunk->msg;	\
1538c2ecf20Sopenharmony_ci	if (msg == prev_msg)	\
1548c2ecf20Sopenharmony_ci		continue;	\
1558c2ecf20Sopenharmony_ci	list_for_each_entry(c, &msg->chunks, frag_list) {	\
1568c2ecf20Sopenharmony_ci		if ((clear && asoc->base.sk == c->skb->sk) ||	\
1578c2ecf20Sopenharmony_ci		    (!clear && asoc->base.sk != c->skb->sk))	\
1588c2ecf20Sopenharmony_ci			cb(c);	\
1598c2ecf20Sopenharmony_ci	}			\
1608c2ecf20Sopenharmony_ci	prev_msg = msg;		\
1618c2ecf20Sopenharmony_ci} while (0)
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_cistatic void sctp_for_each_tx_datachunk(struct sctp_association *asoc,
1648c2ecf20Sopenharmony_ci				       bool clear,
1658c2ecf20Sopenharmony_ci				       void (*cb)(struct sctp_chunk *))
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci{
1688c2ecf20Sopenharmony_ci	struct sctp_datamsg *msg, *prev_msg = NULL;
1698c2ecf20Sopenharmony_ci	struct sctp_outq *q = &asoc->outqueue;
1708c2ecf20Sopenharmony_ci	struct sctp_chunk *chunk, *c;
1718c2ecf20Sopenharmony_ci	struct sctp_transport *t;
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	list_for_each_entry(t, &asoc->peer.transport_addr_list, transports)
1748c2ecf20Sopenharmony_ci		list_for_each_entry(chunk, &t->transmitted, transmitted_list)
1758c2ecf20Sopenharmony_ci			traverse_and_process();
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	list_for_each_entry(chunk, &q->retransmit, transmitted_list)
1788c2ecf20Sopenharmony_ci		traverse_and_process();
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	list_for_each_entry(chunk, &q->sacked, transmitted_list)
1818c2ecf20Sopenharmony_ci		traverse_and_process();
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	list_for_each_entry(chunk, &q->abandoned, transmitted_list)
1848c2ecf20Sopenharmony_ci		traverse_and_process();
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	list_for_each_entry(chunk, &q->out_chunk_list, list)
1878c2ecf20Sopenharmony_ci		traverse_and_process();
1888c2ecf20Sopenharmony_ci}
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_cistatic void sctp_for_each_rx_skb(struct sctp_association *asoc, struct sock *sk,
1918c2ecf20Sopenharmony_ci				 void (*cb)(struct sk_buff *, struct sock *))
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci{
1948c2ecf20Sopenharmony_ci	struct sk_buff *skb, *tmp;
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	sctp_skb_for_each(skb, &asoc->ulpq.lobby, tmp)
1978c2ecf20Sopenharmony_ci		cb(skb, sk);
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	sctp_skb_for_each(skb, &asoc->ulpq.reasm, tmp)
2008c2ecf20Sopenharmony_ci		cb(skb, sk);
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	sctp_skb_for_each(skb, &asoc->ulpq.reasm_uo, tmp)
2038c2ecf20Sopenharmony_ci		cb(skb, sk);
2048c2ecf20Sopenharmony_ci}
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci/* Verify that this is a valid address. */
2078c2ecf20Sopenharmony_cistatic inline int sctp_verify_addr(struct sock *sk, union sctp_addr *addr,
2088c2ecf20Sopenharmony_ci				   int len)
2098c2ecf20Sopenharmony_ci{
2108c2ecf20Sopenharmony_ci	struct sctp_af *af;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	/* Verify basic sockaddr. */
2138c2ecf20Sopenharmony_ci	af = sctp_sockaddr_af(sctp_sk(sk), addr, len);
2148c2ecf20Sopenharmony_ci	if (!af)
2158c2ecf20Sopenharmony_ci		return -EINVAL;
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	/* Is this a valid SCTP address?  */
2188c2ecf20Sopenharmony_ci	if (!af->addr_valid(addr, sctp_sk(sk), NULL))
2198c2ecf20Sopenharmony_ci		return -EINVAL;
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	if (!sctp_sk(sk)->pf->send_verify(sctp_sk(sk), (addr)))
2228c2ecf20Sopenharmony_ci		return -EINVAL;
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	return 0;
2258c2ecf20Sopenharmony_ci}
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci/* Look up the association by its id.  If this is not a UDP-style
2288c2ecf20Sopenharmony_ci * socket, the ID field is always ignored.
2298c2ecf20Sopenharmony_ci */
2308c2ecf20Sopenharmony_cistruct sctp_association *sctp_id2assoc(struct sock *sk, sctp_assoc_t id)
2318c2ecf20Sopenharmony_ci{
2328c2ecf20Sopenharmony_ci	struct sctp_association *asoc = NULL;
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	/* If this is not a UDP-style socket, assoc id should be ignored. */
2358c2ecf20Sopenharmony_ci	if (!sctp_style(sk, UDP)) {
2368c2ecf20Sopenharmony_ci		/* Return NULL if the socket state is not ESTABLISHED. It
2378c2ecf20Sopenharmony_ci		 * could be a TCP-style listening socket or a socket which
2388c2ecf20Sopenharmony_ci		 * hasn't yet called connect() to establish an association.
2398c2ecf20Sopenharmony_ci		 */
2408c2ecf20Sopenharmony_ci		if (!sctp_sstate(sk, ESTABLISHED) && !sctp_sstate(sk, CLOSING))
2418c2ecf20Sopenharmony_ci			return NULL;
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci		/* Get the first and the only association from the list. */
2448c2ecf20Sopenharmony_ci		if (!list_empty(&sctp_sk(sk)->ep->asocs))
2458c2ecf20Sopenharmony_ci			asoc = list_entry(sctp_sk(sk)->ep->asocs.next,
2468c2ecf20Sopenharmony_ci					  struct sctp_association, asocs);
2478c2ecf20Sopenharmony_ci		return asoc;
2488c2ecf20Sopenharmony_ci	}
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	/* Otherwise this is a UDP-style socket. */
2518c2ecf20Sopenharmony_ci	if (id <= SCTP_ALL_ASSOC)
2528c2ecf20Sopenharmony_ci		return NULL;
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	spin_lock_bh(&sctp_assocs_id_lock);
2558c2ecf20Sopenharmony_ci	asoc = (struct sctp_association *)idr_find(&sctp_assocs_id, (int)id);
2568c2ecf20Sopenharmony_ci	if (asoc && (asoc->base.sk != sk || asoc->base.dead))
2578c2ecf20Sopenharmony_ci		asoc = NULL;
2588c2ecf20Sopenharmony_ci	spin_unlock_bh(&sctp_assocs_id_lock);
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	return asoc;
2618c2ecf20Sopenharmony_ci}
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci/* Look up the transport from an address and an assoc id. If both address and
2648c2ecf20Sopenharmony_ci * id are specified, the associations matching the address and the id should be
2658c2ecf20Sopenharmony_ci * the same.
2668c2ecf20Sopenharmony_ci */
2678c2ecf20Sopenharmony_cistatic struct sctp_transport *sctp_addr_id2transport(struct sock *sk,
2688c2ecf20Sopenharmony_ci					      struct sockaddr_storage *addr,
2698c2ecf20Sopenharmony_ci					      sctp_assoc_t id)
2708c2ecf20Sopenharmony_ci{
2718c2ecf20Sopenharmony_ci	struct sctp_association *addr_asoc = NULL, *id_asoc = NULL;
2728c2ecf20Sopenharmony_ci	struct sctp_af *af = sctp_get_af_specific(addr->ss_family);
2738c2ecf20Sopenharmony_ci	union sctp_addr *laddr = (union sctp_addr *)addr;
2748c2ecf20Sopenharmony_ci	struct sctp_transport *transport;
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	if (!af || sctp_verify_addr(sk, laddr, af->sockaddr_len))
2778c2ecf20Sopenharmony_ci		return NULL;
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	addr_asoc = sctp_endpoint_lookup_assoc(sctp_sk(sk)->ep,
2808c2ecf20Sopenharmony_ci					       laddr,
2818c2ecf20Sopenharmony_ci					       &transport);
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	if (!addr_asoc)
2848c2ecf20Sopenharmony_ci		return NULL;
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	id_asoc = sctp_id2assoc(sk, id);
2878c2ecf20Sopenharmony_ci	if (id_asoc && (id_asoc != addr_asoc))
2888c2ecf20Sopenharmony_ci		return NULL;
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	sctp_get_pf_specific(sk->sk_family)->addr_to_user(sctp_sk(sk),
2918c2ecf20Sopenharmony_ci						(union sctp_addr *)addr);
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	return transport;
2948c2ecf20Sopenharmony_ci}
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci/* API 3.1.2 bind() - UDP Style Syntax
2978c2ecf20Sopenharmony_ci * The syntax of bind() is,
2988c2ecf20Sopenharmony_ci *
2998c2ecf20Sopenharmony_ci *   ret = bind(int sd, struct sockaddr *addr, int addrlen);
3008c2ecf20Sopenharmony_ci *
3018c2ecf20Sopenharmony_ci *   sd      - the socket descriptor returned by socket().
3028c2ecf20Sopenharmony_ci *   addr    - the address structure (struct sockaddr_in or struct
3038c2ecf20Sopenharmony_ci *             sockaddr_in6 [RFC 2553]),
3048c2ecf20Sopenharmony_ci *   addr_len - the size of the address structure.
3058c2ecf20Sopenharmony_ci */
3068c2ecf20Sopenharmony_cistatic int sctp_bind(struct sock *sk, struct sockaddr *addr, int addr_len)
3078c2ecf20Sopenharmony_ci{
3088c2ecf20Sopenharmony_ci	int retval = 0;
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	lock_sock(sk);
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	pr_debug("%s: sk:%p, addr:%p, addr_len:%d\n", __func__, sk,
3138c2ecf20Sopenharmony_ci		 addr, addr_len);
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	/* Disallow binding twice. */
3168c2ecf20Sopenharmony_ci	if (!sctp_sk(sk)->ep->base.bind_addr.port)
3178c2ecf20Sopenharmony_ci		retval = sctp_do_bind(sk, (union sctp_addr *)addr,
3188c2ecf20Sopenharmony_ci				      addr_len);
3198c2ecf20Sopenharmony_ci	else
3208c2ecf20Sopenharmony_ci		retval = -EINVAL;
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	release_sock(sk);
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	return retval;
3258c2ecf20Sopenharmony_ci}
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_cistatic int sctp_get_port_local(struct sock *, union sctp_addr *);
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci/* Verify this is a valid sockaddr. */
3308c2ecf20Sopenharmony_cistatic struct sctp_af *sctp_sockaddr_af(struct sctp_sock *opt,
3318c2ecf20Sopenharmony_ci					union sctp_addr *addr, int len)
3328c2ecf20Sopenharmony_ci{
3338c2ecf20Sopenharmony_ci	struct sctp_af *af;
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	/* Check minimum size.  */
3368c2ecf20Sopenharmony_ci	if (len < sizeof (struct sockaddr))
3378c2ecf20Sopenharmony_ci		return NULL;
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	if (!opt->pf->af_supported(addr->sa.sa_family, opt))
3408c2ecf20Sopenharmony_ci		return NULL;
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	if (addr->sa.sa_family == AF_INET6) {
3438c2ecf20Sopenharmony_ci		if (len < SIN6_LEN_RFC2133)
3448c2ecf20Sopenharmony_ci			return NULL;
3458c2ecf20Sopenharmony_ci		/* V4 mapped address are really of AF_INET family */
3468c2ecf20Sopenharmony_ci		if (ipv6_addr_v4mapped(&addr->v6.sin6_addr) &&
3478c2ecf20Sopenharmony_ci		    !opt->pf->af_supported(AF_INET, opt))
3488c2ecf20Sopenharmony_ci			return NULL;
3498c2ecf20Sopenharmony_ci	}
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	/* If we get this far, af is valid. */
3528c2ecf20Sopenharmony_ci	af = sctp_get_af_specific(addr->sa.sa_family);
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	if (len < af->sockaddr_len)
3558c2ecf20Sopenharmony_ci		return NULL;
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	return af;
3588c2ecf20Sopenharmony_ci}
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_cistatic void sctp_auto_asconf_init(struct sctp_sock *sp)
3618c2ecf20Sopenharmony_ci{
3628c2ecf20Sopenharmony_ci	struct net *net = sock_net(&sp->inet.sk);
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	if (net->sctp.default_auto_asconf) {
3658c2ecf20Sopenharmony_ci		spin_lock_bh(&net->sctp.addr_wq_lock);
3668c2ecf20Sopenharmony_ci		list_add_tail(&sp->auto_asconf_list, &net->sctp.auto_asconf_splist);
3678c2ecf20Sopenharmony_ci		spin_unlock_bh(&net->sctp.addr_wq_lock);
3688c2ecf20Sopenharmony_ci		sp->do_auto_asconf = 1;
3698c2ecf20Sopenharmony_ci	}
3708c2ecf20Sopenharmony_ci}
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci/* Bind a local address either to an endpoint or to an association.  */
3738c2ecf20Sopenharmony_cistatic int sctp_do_bind(struct sock *sk, union sctp_addr *addr, int len)
3748c2ecf20Sopenharmony_ci{
3758c2ecf20Sopenharmony_ci	struct net *net = sock_net(sk);
3768c2ecf20Sopenharmony_ci	struct sctp_sock *sp = sctp_sk(sk);
3778c2ecf20Sopenharmony_ci	struct sctp_endpoint *ep = sp->ep;
3788c2ecf20Sopenharmony_ci	struct sctp_bind_addr *bp = &ep->base.bind_addr;
3798c2ecf20Sopenharmony_ci	struct sctp_af *af;
3808c2ecf20Sopenharmony_ci	unsigned short snum;
3818c2ecf20Sopenharmony_ci	int ret = 0;
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	/* Common sockaddr verification. */
3848c2ecf20Sopenharmony_ci	af = sctp_sockaddr_af(sp, addr, len);
3858c2ecf20Sopenharmony_ci	if (!af) {
3868c2ecf20Sopenharmony_ci		pr_debug("%s: sk:%p, newaddr:%p, len:%d EINVAL\n",
3878c2ecf20Sopenharmony_ci			 __func__, sk, addr, len);
3888c2ecf20Sopenharmony_ci		return -EINVAL;
3898c2ecf20Sopenharmony_ci	}
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	snum = ntohs(addr->v4.sin_port);
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	pr_debug("%s: sk:%p, new addr:%pISc, port:%d, new port:%d, len:%d\n",
3948c2ecf20Sopenharmony_ci		 __func__, sk, &addr->sa, bp->port, snum, len);
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	/* PF specific bind() address verification. */
3978c2ecf20Sopenharmony_ci	if (!sp->pf->bind_verify(sp, addr))
3988c2ecf20Sopenharmony_ci		return -EADDRNOTAVAIL;
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	/* We must either be unbound, or bind to the same port.
4018c2ecf20Sopenharmony_ci	 * It's OK to allow 0 ports if we are already bound.
4028c2ecf20Sopenharmony_ci	 * We'll just inhert an already bound port in this case
4038c2ecf20Sopenharmony_ci	 */
4048c2ecf20Sopenharmony_ci	if (bp->port) {
4058c2ecf20Sopenharmony_ci		if (!snum)
4068c2ecf20Sopenharmony_ci			snum = bp->port;
4078c2ecf20Sopenharmony_ci		else if (snum != bp->port) {
4088c2ecf20Sopenharmony_ci			pr_debug("%s: new port %d doesn't match existing port "
4098c2ecf20Sopenharmony_ci				 "%d\n", __func__, snum, bp->port);
4108c2ecf20Sopenharmony_ci			return -EINVAL;
4118c2ecf20Sopenharmony_ci		}
4128c2ecf20Sopenharmony_ci	}
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	if (snum && inet_port_requires_bind_service(net, snum) &&
4158c2ecf20Sopenharmony_ci	    !ns_capable(net->user_ns, CAP_NET_BIND_SERVICE))
4168c2ecf20Sopenharmony_ci		return -EACCES;
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	/* See if the address matches any of the addresses we may have
4198c2ecf20Sopenharmony_ci	 * already bound before checking against other endpoints.
4208c2ecf20Sopenharmony_ci	 */
4218c2ecf20Sopenharmony_ci	if (sctp_bind_addr_match(bp, addr, sp))
4228c2ecf20Sopenharmony_ci		return -EINVAL;
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	/* Make sure we are allowed to bind here.
4258c2ecf20Sopenharmony_ci	 * The function sctp_get_port_local() does duplicate address
4268c2ecf20Sopenharmony_ci	 * detection.
4278c2ecf20Sopenharmony_ci	 */
4288c2ecf20Sopenharmony_ci	addr->v4.sin_port = htons(snum);
4298c2ecf20Sopenharmony_ci	if (sctp_get_port_local(sk, addr))
4308c2ecf20Sopenharmony_ci		return -EADDRINUSE;
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	/* Refresh ephemeral port.  */
4338c2ecf20Sopenharmony_ci	if (!bp->port) {
4348c2ecf20Sopenharmony_ci		bp->port = inet_sk(sk)->inet_num;
4358c2ecf20Sopenharmony_ci		sctp_auto_asconf_init(sp);
4368c2ecf20Sopenharmony_ci	}
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	/* Add the address to the bind address list.
4398c2ecf20Sopenharmony_ci	 * Use GFP_ATOMIC since BHs will be disabled.
4408c2ecf20Sopenharmony_ci	 */
4418c2ecf20Sopenharmony_ci	ret = sctp_add_bind_addr(bp, addr, af->sockaddr_len,
4428c2ecf20Sopenharmony_ci				 SCTP_ADDR_SRC, GFP_ATOMIC);
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	if (ret) {
4458c2ecf20Sopenharmony_ci		sctp_put_port(sk);
4468c2ecf20Sopenharmony_ci		return ret;
4478c2ecf20Sopenharmony_ci	}
4488c2ecf20Sopenharmony_ci	/* Copy back into socket for getsockname() use. */
4498c2ecf20Sopenharmony_ci	inet_sk(sk)->inet_sport = htons(inet_sk(sk)->inet_num);
4508c2ecf20Sopenharmony_ci	sp->pf->to_sk_saddr(addr, sk);
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci	return ret;
4538c2ecf20Sopenharmony_ci}
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci /* ADDIP Section 4.1.1 Congestion Control of ASCONF Chunks
4568c2ecf20Sopenharmony_ci *
4578c2ecf20Sopenharmony_ci * R1) One and only one ASCONF Chunk MAY be in transit and unacknowledged
4588c2ecf20Sopenharmony_ci * at any one time.  If a sender, after sending an ASCONF chunk, decides
4598c2ecf20Sopenharmony_ci * it needs to transfer another ASCONF Chunk, it MUST wait until the
4608c2ecf20Sopenharmony_ci * ASCONF-ACK Chunk returns from the previous ASCONF Chunk before sending a
4618c2ecf20Sopenharmony_ci * subsequent ASCONF. Note this restriction binds each side, so at any
4628c2ecf20Sopenharmony_ci * time two ASCONF may be in-transit on any given association (one sent
4638c2ecf20Sopenharmony_ci * from each endpoint).
4648c2ecf20Sopenharmony_ci */
4658c2ecf20Sopenharmony_cistatic int sctp_send_asconf(struct sctp_association *asoc,
4668c2ecf20Sopenharmony_ci			    struct sctp_chunk *chunk)
4678c2ecf20Sopenharmony_ci{
4688c2ecf20Sopenharmony_ci	int retval = 0;
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	/* If there is an outstanding ASCONF chunk, queue it for later
4718c2ecf20Sopenharmony_ci	 * transmission.
4728c2ecf20Sopenharmony_ci	 */
4738c2ecf20Sopenharmony_ci	if (asoc->addip_last_asconf) {
4748c2ecf20Sopenharmony_ci		list_add_tail(&chunk->list, &asoc->addip_chunk_list);
4758c2ecf20Sopenharmony_ci		goto out;
4768c2ecf20Sopenharmony_ci	}
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	/* Hold the chunk until an ASCONF_ACK is received. */
4798c2ecf20Sopenharmony_ci	sctp_chunk_hold(chunk);
4808c2ecf20Sopenharmony_ci	retval = sctp_primitive_ASCONF(asoc->base.net, asoc, chunk);
4818c2ecf20Sopenharmony_ci	if (retval)
4828c2ecf20Sopenharmony_ci		sctp_chunk_free(chunk);
4838c2ecf20Sopenharmony_ci	else
4848c2ecf20Sopenharmony_ci		asoc->addip_last_asconf = chunk;
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ciout:
4878c2ecf20Sopenharmony_ci	return retval;
4888c2ecf20Sopenharmony_ci}
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci/* Add a list of addresses as bind addresses to local endpoint or
4918c2ecf20Sopenharmony_ci * association.
4928c2ecf20Sopenharmony_ci *
4938c2ecf20Sopenharmony_ci * Basically run through each address specified in the addrs/addrcnt
4948c2ecf20Sopenharmony_ci * array/length pair, determine if it is IPv6 or IPv4 and call
4958c2ecf20Sopenharmony_ci * sctp_do_bind() on it.
4968c2ecf20Sopenharmony_ci *
4978c2ecf20Sopenharmony_ci * If any of them fails, then the operation will be reversed and the
4988c2ecf20Sopenharmony_ci * ones that were added will be removed.
4998c2ecf20Sopenharmony_ci *
5008c2ecf20Sopenharmony_ci * Only sctp_setsockopt_bindx() is supposed to call this function.
5018c2ecf20Sopenharmony_ci */
5028c2ecf20Sopenharmony_cistatic int sctp_bindx_add(struct sock *sk, struct sockaddr *addrs, int addrcnt)
5038c2ecf20Sopenharmony_ci{
5048c2ecf20Sopenharmony_ci	int cnt;
5058c2ecf20Sopenharmony_ci	int retval = 0;
5068c2ecf20Sopenharmony_ci	void *addr_buf;
5078c2ecf20Sopenharmony_ci	struct sockaddr *sa_addr;
5088c2ecf20Sopenharmony_ci	struct sctp_af *af;
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci	pr_debug("%s: sk:%p, addrs:%p, addrcnt:%d\n", __func__, sk,
5118c2ecf20Sopenharmony_ci		 addrs, addrcnt);
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci	addr_buf = addrs;
5148c2ecf20Sopenharmony_ci	for (cnt = 0; cnt < addrcnt; cnt++) {
5158c2ecf20Sopenharmony_ci		/* The list may contain either IPv4 or IPv6 address;
5168c2ecf20Sopenharmony_ci		 * determine the address length for walking thru the list.
5178c2ecf20Sopenharmony_ci		 */
5188c2ecf20Sopenharmony_ci		sa_addr = addr_buf;
5198c2ecf20Sopenharmony_ci		af = sctp_get_af_specific(sa_addr->sa_family);
5208c2ecf20Sopenharmony_ci		if (!af) {
5218c2ecf20Sopenharmony_ci			retval = -EINVAL;
5228c2ecf20Sopenharmony_ci			goto err_bindx_add;
5238c2ecf20Sopenharmony_ci		}
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci		retval = sctp_do_bind(sk, (union sctp_addr *)sa_addr,
5268c2ecf20Sopenharmony_ci				      af->sockaddr_len);
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci		addr_buf += af->sockaddr_len;
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_cierr_bindx_add:
5318c2ecf20Sopenharmony_ci		if (retval < 0) {
5328c2ecf20Sopenharmony_ci			/* Failed. Cleanup the ones that have been added */
5338c2ecf20Sopenharmony_ci			if (cnt > 0)
5348c2ecf20Sopenharmony_ci				sctp_bindx_rem(sk, addrs, cnt);
5358c2ecf20Sopenharmony_ci			return retval;
5368c2ecf20Sopenharmony_ci		}
5378c2ecf20Sopenharmony_ci	}
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci	return retval;
5408c2ecf20Sopenharmony_ci}
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci/* Send an ASCONF chunk with Add IP address parameters to all the peers of the
5438c2ecf20Sopenharmony_ci * associations that are part of the endpoint indicating that a list of local
5448c2ecf20Sopenharmony_ci * addresses are added to the endpoint.
5458c2ecf20Sopenharmony_ci *
5468c2ecf20Sopenharmony_ci * If any of the addresses is already in the bind address list of the
5478c2ecf20Sopenharmony_ci * association, we do not send the chunk for that association.  But it will not
5488c2ecf20Sopenharmony_ci * affect other associations.
5498c2ecf20Sopenharmony_ci *
5508c2ecf20Sopenharmony_ci * Only sctp_setsockopt_bindx() is supposed to call this function.
5518c2ecf20Sopenharmony_ci */
5528c2ecf20Sopenharmony_cistatic int sctp_send_asconf_add_ip(struct sock		*sk,
5538c2ecf20Sopenharmony_ci				   struct sockaddr	*addrs,
5548c2ecf20Sopenharmony_ci				   int 			addrcnt)
5558c2ecf20Sopenharmony_ci{
5568c2ecf20Sopenharmony_ci	struct sctp_sock		*sp;
5578c2ecf20Sopenharmony_ci	struct sctp_endpoint		*ep;
5588c2ecf20Sopenharmony_ci	struct sctp_association		*asoc;
5598c2ecf20Sopenharmony_ci	struct sctp_bind_addr		*bp;
5608c2ecf20Sopenharmony_ci	struct sctp_chunk		*chunk;
5618c2ecf20Sopenharmony_ci	struct sctp_sockaddr_entry	*laddr;
5628c2ecf20Sopenharmony_ci	union sctp_addr			*addr;
5638c2ecf20Sopenharmony_ci	union sctp_addr			saveaddr;
5648c2ecf20Sopenharmony_ci	void				*addr_buf;
5658c2ecf20Sopenharmony_ci	struct sctp_af			*af;
5668c2ecf20Sopenharmony_ci	struct list_head		*p;
5678c2ecf20Sopenharmony_ci	int 				i;
5688c2ecf20Sopenharmony_ci	int 				retval = 0;
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci	sp = sctp_sk(sk);
5718c2ecf20Sopenharmony_ci	ep = sp->ep;
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci	if (!ep->asconf_enable)
5748c2ecf20Sopenharmony_ci		return retval;
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci	pr_debug("%s: sk:%p, addrs:%p, addrcnt:%d\n",
5778c2ecf20Sopenharmony_ci		 __func__, sk, addrs, addrcnt);
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci	list_for_each_entry(asoc, &ep->asocs, asocs) {
5808c2ecf20Sopenharmony_ci		if (!asoc->peer.asconf_capable)
5818c2ecf20Sopenharmony_ci			continue;
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci		if (asoc->peer.addip_disabled_mask & SCTP_PARAM_ADD_IP)
5848c2ecf20Sopenharmony_ci			continue;
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci		if (!sctp_state(asoc, ESTABLISHED))
5878c2ecf20Sopenharmony_ci			continue;
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ci		/* Check if any address in the packed array of addresses is
5908c2ecf20Sopenharmony_ci		 * in the bind address list of the association. If so,
5918c2ecf20Sopenharmony_ci		 * do not send the asconf chunk to its peer, but continue with
5928c2ecf20Sopenharmony_ci		 * other associations.
5938c2ecf20Sopenharmony_ci		 */
5948c2ecf20Sopenharmony_ci		addr_buf = addrs;
5958c2ecf20Sopenharmony_ci		for (i = 0; i < addrcnt; i++) {
5968c2ecf20Sopenharmony_ci			addr = addr_buf;
5978c2ecf20Sopenharmony_ci			af = sctp_get_af_specific(addr->v4.sin_family);
5988c2ecf20Sopenharmony_ci			if (!af) {
5998c2ecf20Sopenharmony_ci				retval = -EINVAL;
6008c2ecf20Sopenharmony_ci				goto out;
6018c2ecf20Sopenharmony_ci			}
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_ci			if (sctp_assoc_lookup_laddr(asoc, addr))
6048c2ecf20Sopenharmony_ci				break;
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci			addr_buf += af->sockaddr_len;
6078c2ecf20Sopenharmony_ci		}
6088c2ecf20Sopenharmony_ci		if (i < addrcnt)
6098c2ecf20Sopenharmony_ci			continue;
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci		/* Use the first valid address in bind addr list of
6128c2ecf20Sopenharmony_ci		 * association as Address Parameter of ASCONF CHUNK.
6138c2ecf20Sopenharmony_ci		 */
6148c2ecf20Sopenharmony_ci		bp = &asoc->base.bind_addr;
6158c2ecf20Sopenharmony_ci		p = bp->address_list.next;
6168c2ecf20Sopenharmony_ci		laddr = list_entry(p, struct sctp_sockaddr_entry, list);
6178c2ecf20Sopenharmony_ci		chunk = sctp_make_asconf_update_ip(asoc, &laddr->a, addrs,
6188c2ecf20Sopenharmony_ci						   addrcnt, SCTP_PARAM_ADD_IP);
6198c2ecf20Sopenharmony_ci		if (!chunk) {
6208c2ecf20Sopenharmony_ci			retval = -ENOMEM;
6218c2ecf20Sopenharmony_ci			goto out;
6228c2ecf20Sopenharmony_ci		}
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci		/* Add the new addresses to the bind address list with
6258c2ecf20Sopenharmony_ci		 * use_as_src set to 0.
6268c2ecf20Sopenharmony_ci		 */
6278c2ecf20Sopenharmony_ci		addr_buf = addrs;
6288c2ecf20Sopenharmony_ci		for (i = 0; i < addrcnt; i++) {
6298c2ecf20Sopenharmony_ci			addr = addr_buf;
6308c2ecf20Sopenharmony_ci			af = sctp_get_af_specific(addr->v4.sin_family);
6318c2ecf20Sopenharmony_ci			memcpy(&saveaddr, addr, af->sockaddr_len);
6328c2ecf20Sopenharmony_ci			retval = sctp_add_bind_addr(bp, &saveaddr,
6338c2ecf20Sopenharmony_ci						    sizeof(saveaddr),
6348c2ecf20Sopenharmony_ci						    SCTP_ADDR_NEW, GFP_ATOMIC);
6358c2ecf20Sopenharmony_ci			addr_buf += af->sockaddr_len;
6368c2ecf20Sopenharmony_ci		}
6378c2ecf20Sopenharmony_ci		if (asoc->src_out_of_asoc_ok) {
6388c2ecf20Sopenharmony_ci			struct sctp_transport *trans;
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_ci			list_for_each_entry(trans,
6418c2ecf20Sopenharmony_ci			    &asoc->peer.transport_addr_list, transports) {
6428c2ecf20Sopenharmony_ci				trans->cwnd = min(4*asoc->pathmtu, max_t(__u32,
6438c2ecf20Sopenharmony_ci				    2*asoc->pathmtu, 4380));
6448c2ecf20Sopenharmony_ci				trans->ssthresh = asoc->peer.i.a_rwnd;
6458c2ecf20Sopenharmony_ci				trans->rto = asoc->rto_initial;
6468c2ecf20Sopenharmony_ci				sctp_max_rto(asoc, trans);
6478c2ecf20Sopenharmony_ci				trans->rtt = trans->srtt = trans->rttvar = 0;
6488c2ecf20Sopenharmony_ci				/* Clear the source and route cache */
6498c2ecf20Sopenharmony_ci				sctp_transport_route(trans, NULL,
6508c2ecf20Sopenharmony_ci						     sctp_sk(asoc->base.sk));
6518c2ecf20Sopenharmony_ci			}
6528c2ecf20Sopenharmony_ci		}
6538c2ecf20Sopenharmony_ci		retval = sctp_send_asconf(asoc, chunk);
6548c2ecf20Sopenharmony_ci	}
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ciout:
6578c2ecf20Sopenharmony_ci	return retval;
6588c2ecf20Sopenharmony_ci}
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci/* Remove a list of addresses from bind addresses list.  Do not remove the
6618c2ecf20Sopenharmony_ci * last address.
6628c2ecf20Sopenharmony_ci *
6638c2ecf20Sopenharmony_ci * Basically run through each address specified in the addrs/addrcnt
6648c2ecf20Sopenharmony_ci * array/length pair, determine if it is IPv6 or IPv4 and call
6658c2ecf20Sopenharmony_ci * sctp_del_bind() on it.
6668c2ecf20Sopenharmony_ci *
6678c2ecf20Sopenharmony_ci * If any of them fails, then the operation will be reversed and the
6688c2ecf20Sopenharmony_ci * ones that were removed will be added back.
6698c2ecf20Sopenharmony_ci *
6708c2ecf20Sopenharmony_ci * At least one address has to be left; if only one address is
6718c2ecf20Sopenharmony_ci * available, the operation will return -EBUSY.
6728c2ecf20Sopenharmony_ci *
6738c2ecf20Sopenharmony_ci * Only sctp_setsockopt_bindx() is supposed to call this function.
6748c2ecf20Sopenharmony_ci */
6758c2ecf20Sopenharmony_cistatic int sctp_bindx_rem(struct sock *sk, struct sockaddr *addrs, int addrcnt)
6768c2ecf20Sopenharmony_ci{
6778c2ecf20Sopenharmony_ci	struct sctp_sock *sp = sctp_sk(sk);
6788c2ecf20Sopenharmony_ci	struct sctp_endpoint *ep = sp->ep;
6798c2ecf20Sopenharmony_ci	int cnt;
6808c2ecf20Sopenharmony_ci	struct sctp_bind_addr *bp = &ep->base.bind_addr;
6818c2ecf20Sopenharmony_ci	int retval = 0;
6828c2ecf20Sopenharmony_ci	void *addr_buf;
6838c2ecf20Sopenharmony_ci	union sctp_addr *sa_addr;
6848c2ecf20Sopenharmony_ci	struct sctp_af *af;
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_ci	pr_debug("%s: sk:%p, addrs:%p, addrcnt:%d\n",
6878c2ecf20Sopenharmony_ci		 __func__, sk, addrs, addrcnt);
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_ci	addr_buf = addrs;
6908c2ecf20Sopenharmony_ci	for (cnt = 0; cnt < addrcnt; cnt++) {
6918c2ecf20Sopenharmony_ci		/* If the bind address list is empty or if there is only one
6928c2ecf20Sopenharmony_ci		 * bind address, there is nothing more to be removed (we need
6938c2ecf20Sopenharmony_ci		 * at least one address here).
6948c2ecf20Sopenharmony_ci		 */
6958c2ecf20Sopenharmony_ci		if (list_empty(&bp->address_list) ||
6968c2ecf20Sopenharmony_ci		    (sctp_list_single_entry(&bp->address_list))) {
6978c2ecf20Sopenharmony_ci			retval = -EBUSY;
6988c2ecf20Sopenharmony_ci			goto err_bindx_rem;
6998c2ecf20Sopenharmony_ci		}
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_ci		sa_addr = addr_buf;
7028c2ecf20Sopenharmony_ci		af = sctp_get_af_specific(sa_addr->sa.sa_family);
7038c2ecf20Sopenharmony_ci		if (!af) {
7048c2ecf20Sopenharmony_ci			retval = -EINVAL;
7058c2ecf20Sopenharmony_ci			goto err_bindx_rem;
7068c2ecf20Sopenharmony_ci		}
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_ci		if (!af->addr_valid(sa_addr, sp, NULL)) {
7098c2ecf20Sopenharmony_ci			retval = -EADDRNOTAVAIL;
7108c2ecf20Sopenharmony_ci			goto err_bindx_rem;
7118c2ecf20Sopenharmony_ci		}
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_ci		if (sa_addr->v4.sin_port &&
7148c2ecf20Sopenharmony_ci		    sa_addr->v4.sin_port != htons(bp->port)) {
7158c2ecf20Sopenharmony_ci			retval = -EINVAL;
7168c2ecf20Sopenharmony_ci			goto err_bindx_rem;
7178c2ecf20Sopenharmony_ci		}
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_ci		if (!sa_addr->v4.sin_port)
7208c2ecf20Sopenharmony_ci			sa_addr->v4.sin_port = htons(bp->port);
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_ci		/* FIXME - There is probably a need to check if sk->sk_saddr and
7238c2ecf20Sopenharmony_ci		 * sk->sk_rcv_addr are currently set to one of the addresses to
7248c2ecf20Sopenharmony_ci		 * be removed. This is something which needs to be looked into
7258c2ecf20Sopenharmony_ci		 * when we are fixing the outstanding issues with multi-homing
7268c2ecf20Sopenharmony_ci		 * socket routing and failover schemes. Refer to comments in
7278c2ecf20Sopenharmony_ci		 * sctp_do_bind(). -daisy
7288c2ecf20Sopenharmony_ci		 */
7298c2ecf20Sopenharmony_ci		retval = sctp_del_bind_addr(bp, sa_addr);
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_ci		addr_buf += af->sockaddr_len;
7328c2ecf20Sopenharmony_cierr_bindx_rem:
7338c2ecf20Sopenharmony_ci		if (retval < 0) {
7348c2ecf20Sopenharmony_ci			/* Failed. Add the ones that has been removed back */
7358c2ecf20Sopenharmony_ci			if (cnt > 0)
7368c2ecf20Sopenharmony_ci				sctp_bindx_add(sk, addrs, cnt);
7378c2ecf20Sopenharmony_ci			return retval;
7388c2ecf20Sopenharmony_ci		}
7398c2ecf20Sopenharmony_ci	}
7408c2ecf20Sopenharmony_ci
7418c2ecf20Sopenharmony_ci	return retval;
7428c2ecf20Sopenharmony_ci}
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci/* Send an ASCONF chunk with Delete IP address parameters to all the peers of
7458c2ecf20Sopenharmony_ci * the associations that are part of the endpoint indicating that a list of
7468c2ecf20Sopenharmony_ci * local addresses are removed from the endpoint.
7478c2ecf20Sopenharmony_ci *
7488c2ecf20Sopenharmony_ci * If any of the addresses is already in the bind address list of the
7498c2ecf20Sopenharmony_ci * association, we do not send the chunk for that association.  But it will not
7508c2ecf20Sopenharmony_ci * affect other associations.
7518c2ecf20Sopenharmony_ci *
7528c2ecf20Sopenharmony_ci * Only sctp_setsockopt_bindx() is supposed to call this function.
7538c2ecf20Sopenharmony_ci */
7548c2ecf20Sopenharmony_cistatic int sctp_send_asconf_del_ip(struct sock		*sk,
7558c2ecf20Sopenharmony_ci				   struct sockaddr	*addrs,
7568c2ecf20Sopenharmony_ci				   int			addrcnt)
7578c2ecf20Sopenharmony_ci{
7588c2ecf20Sopenharmony_ci	struct sctp_sock	*sp;
7598c2ecf20Sopenharmony_ci	struct sctp_endpoint	*ep;
7608c2ecf20Sopenharmony_ci	struct sctp_association	*asoc;
7618c2ecf20Sopenharmony_ci	struct sctp_transport	*transport;
7628c2ecf20Sopenharmony_ci	struct sctp_bind_addr	*bp;
7638c2ecf20Sopenharmony_ci	struct sctp_chunk	*chunk;
7648c2ecf20Sopenharmony_ci	union sctp_addr		*laddr;
7658c2ecf20Sopenharmony_ci	void			*addr_buf;
7668c2ecf20Sopenharmony_ci	struct sctp_af		*af;
7678c2ecf20Sopenharmony_ci	struct sctp_sockaddr_entry *saddr;
7688c2ecf20Sopenharmony_ci	int 			i;
7698c2ecf20Sopenharmony_ci	int 			retval = 0;
7708c2ecf20Sopenharmony_ci	int			stored = 0;
7718c2ecf20Sopenharmony_ci
7728c2ecf20Sopenharmony_ci	chunk = NULL;
7738c2ecf20Sopenharmony_ci	sp = sctp_sk(sk);
7748c2ecf20Sopenharmony_ci	ep = sp->ep;
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_ci	if (!ep->asconf_enable)
7778c2ecf20Sopenharmony_ci		return retval;
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_ci	pr_debug("%s: sk:%p, addrs:%p, addrcnt:%d\n",
7808c2ecf20Sopenharmony_ci		 __func__, sk, addrs, addrcnt);
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_ci	list_for_each_entry(asoc, &ep->asocs, asocs) {
7838c2ecf20Sopenharmony_ci
7848c2ecf20Sopenharmony_ci		if (!asoc->peer.asconf_capable)
7858c2ecf20Sopenharmony_ci			continue;
7868c2ecf20Sopenharmony_ci
7878c2ecf20Sopenharmony_ci		if (asoc->peer.addip_disabled_mask & SCTP_PARAM_DEL_IP)
7888c2ecf20Sopenharmony_ci			continue;
7898c2ecf20Sopenharmony_ci
7908c2ecf20Sopenharmony_ci		if (!sctp_state(asoc, ESTABLISHED))
7918c2ecf20Sopenharmony_ci			continue;
7928c2ecf20Sopenharmony_ci
7938c2ecf20Sopenharmony_ci		/* Check if any address in the packed array of addresses is
7948c2ecf20Sopenharmony_ci		 * not present in the bind address list of the association.
7958c2ecf20Sopenharmony_ci		 * If so, do not send the asconf chunk to its peer, but
7968c2ecf20Sopenharmony_ci		 * continue with other associations.
7978c2ecf20Sopenharmony_ci		 */
7988c2ecf20Sopenharmony_ci		addr_buf = addrs;
7998c2ecf20Sopenharmony_ci		for (i = 0; i < addrcnt; i++) {
8008c2ecf20Sopenharmony_ci			laddr = addr_buf;
8018c2ecf20Sopenharmony_ci			af = sctp_get_af_specific(laddr->v4.sin_family);
8028c2ecf20Sopenharmony_ci			if (!af) {
8038c2ecf20Sopenharmony_ci				retval = -EINVAL;
8048c2ecf20Sopenharmony_ci				goto out;
8058c2ecf20Sopenharmony_ci			}
8068c2ecf20Sopenharmony_ci
8078c2ecf20Sopenharmony_ci			if (!sctp_assoc_lookup_laddr(asoc, laddr))
8088c2ecf20Sopenharmony_ci				break;
8098c2ecf20Sopenharmony_ci
8108c2ecf20Sopenharmony_ci			addr_buf += af->sockaddr_len;
8118c2ecf20Sopenharmony_ci		}
8128c2ecf20Sopenharmony_ci		if (i < addrcnt)
8138c2ecf20Sopenharmony_ci			continue;
8148c2ecf20Sopenharmony_ci
8158c2ecf20Sopenharmony_ci		/* Find one address in the association's bind address list
8168c2ecf20Sopenharmony_ci		 * that is not in the packed array of addresses. This is to
8178c2ecf20Sopenharmony_ci		 * make sure that we do not delete all the addresses in the
8188c2ecf20Sopenharmony_ci		 * association.
8198c2ecf20Sopenharmony_ci		 */
8208c2ecf20Sopenharmony_ci		bp = &asoc->base.bind_addr;
8218c2ecf20Sopenharmony_ci		laddr = sctp_find_unmatch_addr(bp, (union sctp_addr *)addrs,
8228c2ecf20Sopenharmony_ci					       addrcnt, sp);
8238c2ecf20Sopenharmony_ci		if ((laddr == NULL) && (addrcnt == 1)) {
8248c2ecf20Sopenharmony_ci			if (asoc->asconf_addr_del_pending)
8258c2ecf20Sopenharmony_ci				continue;
8268c2ecf20Sopenharmony_ci			asoc->asconf_addr_del_pending =
8278c2ecf20Sopenharmony_ci			    kzalloc(sizeof(union sctp_addr), GFP_ATOMIC);
8288c2ecf20Sopenharmony_ci			if (asoc->asconf_addr_del_pending == NULL) {
8298c2ecf20Sopenharmony_ci				retval = -ENOMEM;
8308c2ecf20Sopenharmony_ci				goto out;
8318c2ecf20Sopenharmony_ci			}
8328c2ecf20Sopenharmony_ci			asoc->asconf_addr_del_pending->sa.sa_family =
8338c2ecf20Sopenharmony_ci				    addrs->sa_family;
8348c2ecf20Sopenharmony_ci			asoc->asconf_addr_del_pending->v4.sin_port =
8358c2ecf20Sopenharmony_ci				    htons(bp->port);
8368c2ecf20Sopenharmony_ci			if (addrs->sa_family == AF_INET) {
8378c2ecf20Sopenharmony_ci				struct sockaddr_in *sin;
8388c2ecf20Sopenharmony_ci
8398c2ecf20Sopenharmony_ci				sin = (struct sockaddr_in *)addrs;
8408c2ecf20Sopenharmony_ci				asoc->asconf_addr_del_pending->v4.sin_addr.s_addr = sin->sin_addr.s_addr;
8418c2ecf20Sopenharmony_ci			} else if (addrs->sa_family == AF_INET6) {
8428c2ecf20Sopenharmony_ci				struct sockaddr_in6 *sin6;
8438c2ecf20Sopenharmony_ci
8448c2ecf20Sopenharmony_ci				sin6 = (struct sockaddr_in6 *)addrs;
8458c2ecf20Sopenharmony_ci				asoc->asconf_addr_del_pending->v6.sin6_addr = sin6->sin6_addr;
8468c2ecf20Sopenharmony_ci			}
8478c2ecf20Sopenharmony_ci
8488c2ecf20Sopenharmony_ci			pr_debug("%s: keep the last address asoc:%p %pISc at %p\n",
8498c2ecf20Sopenharmony_ci				 __func__, asoc, &asoc->asconf_addr_del_pending->sa,
8508c2ecf20Sopenharmony_ci				 asoc->asconf_addr_del_pending);
8518c2ecf20Sopenharmony_ci
8528c2ecf20Sopenharmony_ci			asoc->src_out_of_asoc_ok = 1;
8538c2ecf20Sopenharmony_ci			stored = 1;
8548c2ecf20Sopenharmony_ci			goto skip_mkasconf;
8558c2ecf20Sopenharmony_ci		}
8568c2ecf20Sopenharmony_ci
8578c2ecf20Sopenharmony_ci		if (laddr == NULL)
8588c2ecf20Sopenharmony_ci			return -EINVAL;
8598c2ecf20Sopenharmony_ci
8608c2ecf20Sopenharmony_ci		/* We do not need RCU protection throughout this loop
8618c2ecf20Sopenharmony_ci		 * because this is done under a socket lock from the
8628c2ecf20Sopenharmony_ci		 * setsockopt call.
8638c2ecf20Sopenharmony_ci		 */
8648c2ecf20Sopenharmony_ci		chunk = sctp_make_asconf_update_ip(asoc, laddr, addrs, addrcnt,
8658c2ecf20Sopenharmony_ci						   SCTP_PARAM_DEL_IP);
8668c2ecf20Sopenharmony_ci		if (!chunk) {
8678c2ecf20Sopenharmony_ci			retval = -ENOMEM;
8688c2ecf20Sopenharmony_ci			goto out;
8698c2ecf20Sopenharmony_ci		}
8708c2ecf20Sopenharmony_ci
8718c2ecf20Sopenharmony_ciskip_mkasconf:
8728c2ecf20Sopenharmony_ci		/* Reset use_as_src flag for the addresses in the bind address
8738c2ecf20Sopenharmony_ci		 * list that are to be deleted.
8748c2ecf20Sopenharmony_ci		 */
8758c2ecf20Sopenharmony_ci		addr_buf = addrs;
8768c2ecf20Sopenharmony_ci		for (i = 0; i < addrcnt; i++) {
8778c2ecf20Sopenharmony_ci			laddr = addr_buf;
8788c2ecf20Sopenharmony_ci			af = sctp_get_af_specific(laddr->v4.sin_family);
8798c2ecf20Sopenharmony_ci			list_for_each_entry(saddr, &bp->address_list, list) {
8808c2ecf20Sopenharmony_ci				if (sctp_cmp_addr_exact(&saddr->a, laddr))
8818c2ecf20Sopenharmony_ci					saddr->state = SCTP_ADDR_DEL;
8828c2ecf20Sopenharmony_ci			}
8838c2ecf20Sopenharmony_ci			addr_buf += af->sockaddr_len;
8848c2ecf20Sopenharmony_ci		}
8858c2ecf20Sopenharmony_ci
8868c2ecf20Sopenharmony_ci		/* Update the route and saddr entries for all the transports
8878c2ecf20Sopenharmony_ci		 * as some of the addresses in the bind address list are
8888c2ecf20Sopenharmony_ci		 * about to be deleted and cannot be used as source addresses.
8898c2ecf20Sopenharmony_ci		 */
8908c2ecf20Sopenharmony_ci		list_for_each_entry(transport, &asoc->peer.transport_addr_list,
8918c2ecf20Sopenharmony_ci					transports) {
8928c2ecf20Sopenharmony_ci			sctp_transport_route(transport, NULL,
8938c2ecf20Sopenharmony_ci					     sctp_sk(asoc->base.sk));
8948c2ecf20Sopenharmony_ci		}
8958c2ecf20Sopenharmony_ci
8968c2ecf20Sopenharmony_ci		if (stored)
8978c2ecf20Sopenharmony_ci			/* We don't need to transmit ASCONF */
8988c2ecf20Sopenharmony_ci			continue;
8998c2ecf20Sopenharmony_ci		retval = sctp_send_asconf(asoc, chunk);
9008c2ecf20Sopenharmony_ci	}
9018c2ecf20Sopenharmony_ciout:
9028c2ecf20Sopenharmony_ci	return retval;
9038c2ecf20Sopenharmony_ci}
9048c2ecf20Sopenharmony_ci
9058c2ecf20Sopenharmony_ci/* set addr events to assocs in the endpoint.  ep and addr_wq must be locked */
9068c2ecf20Sopenharmony_ciint sctp_asconf_mgmt(struct sctp_sock *sp, struct sctp_sockaddr_entry *addrw)
9078c2ecf20Sopenharmony_ci{
9088c2ecf20Sopenharmony_ci	struct sock *sk = sctp_opt2sk(sp);
9098c2ecf20Sopenharmony_ci	union sctp_addr *addr;
9108c2ecf20Sopenharmony_ci	struct sctp_af *af;
9118c2ecf20Sopenharmony_ci
9128c2ecf20Sopenharmony_ci	/* It is safe to write port space in caller. */
9138c2ecf20Sopenharmony_ci	addr = &addrw->a;
9148c2ecf20Sopenharmony_ci	addr->v4.sin_port = htons(sp->ep->base.bind_addr.port);
9158c2ecf20Sopenharmony_ci	af = sctp_get_af_specific(addr->sa.sa_family);
9168c2ecf20Sopenharmony_ci	if (!af)
9178c2ecf20Sopenharmony_ci		return -EINVAL;
9188c2ecf20Sopenharmony_ci	if (sctp_verify_addr(sk, addr, af->sockaddr_len))
9198c2ecf20Sopenharmony_ci		return -EINVAL;
9208c2ecf20Sopenharmony_ci
9218c2ecf20Sopenharmony_ci	if (addrw->state == SCTP_ADDR_NEW)
9228c2ecf20Sopenharmony_ci		return sctp_send_asconf_add_ip(sk, (struct sockaddr *)addr, 1);
9238c2ecf20Sopenharmony_ci	else
9248c2ecf20Sopenharmony_ci		return sctp_send_asconf_del_ip(sk, (struct sockaddr *)addr, 1);
9258c2ecf20Sopenharmony_ci}
9268c2ecf20Sopenharmony_ci
9278c2ecf20Sopenharmony_ci/* Helper for tunneling sctp_bindx() requests through sctp_setsockopt()
9288c2ecf20Sopenharmony_ci *
9298c2ecf20Sopenharmony_ci * API 8.1
9308c2ecf20Sopenharmony_ci * int sctp_bindx(int sd, struct sockaddr *addrs, int addrcnt,
9318c2ecf20Sopenharmony_ci *                int flags);
9328c2ecf20Sopenharmony_ci *
9338c2ecf20Sopenharmony_ci * If sd is an IPv4 socket, the addresses passed must be IPv4 addresses.
9348c2ecf20Sopenharmony_ci * If the sd is an IPv6 socket, the addresses passed can either be IPv4
9358c2ecf20Sopenharmony_ci * or IPv6 addresses.
9368c2ecf20Sopenharmony_ci *
9378c2ecf20Sopenharmony_ci * A single address may be specified as INADDR_ANY or IN6ADDR_ANY, see
9388c2ecf20Sopenharmony_ci * Section 3.1.2 for this usage.
9398c2ecf20Sopenharmony_ci *
9408c2ecf20Sopenharmony_ci * addrs is a pointer to an array of one or more socket addresses. Each
9418c2ecf20Sopenharmony_ci * address is contained in its appropriate structure (i.e. struct
9428c2ecf20Sopenharmony_ci * sockaddr_in or struct sockaddr_in6) the family of the address type
9438c2ecf20Sopenharmony_ci * must be used to distinguish the address length (note that this
9448c2ecf20Sopenharmony_ci * representation is termed a "packed array" of addresses). The caller
9458c2ecf20Sopenharmony_ci * specifies the number of addresses in the array with addrcnt.
9468c2ecf20Sopenharmony_ci *
9478c2ecf20Sopenharmony_ci * On success, sctp_bindx() returns 0. On failure, sctp_bindx() returns
9488c2ecf20Sopenharmony_ci * -1, and sets errno to the appropriate error code.
9498c2ecf20Sopenharmony_ci *
9508c2ecf20Sopenharmony_ci * For SCTP, the port given in each socket address must be the same, or
9518c2ecf20Sopenharmony_ci * sctp_bindx() will fail, setting errno to EINVAL.
9528c2ecf20Sopenharmony_ci *
9538c2ecf20Sopenharmony_ci * The flags parameter is formed from the bitwise OR of zero or more of
9548c2ecf20Sopenharmony_ci * the following currently defined flags:
9558c2ecf20Sopenharmony_ci *
9568c2ecf20Sopenharmony_ci * SCTP_BINDX_ADD_ADDR
9578c2ecf20Sopenharmony_ci *
9588c2ecf20Sopenharmony_ci * SCTP_BINDX_REM_ADDR
9598c2ecf20Sopenharmony_ci *
9608c2ecf20Sopenharmony_ci * SCTP_BINDX_ADD_ADDR directs SCTP to add the given addresses to the
9618c2ecf20Sopenharmony_ci * association, and SCTP_BINDX_REM_ADDR directs SCTP to remove the given
9628c2ecf20Sopenharmony_ci * addresses from the association. The two flags are mutually exclusive;
9638c2ecf20Sopenharmony_ci * if both are given, sctp_bindx() will fail with EINVAL. A caller may
9648c2ecf20Sopenharmony_ci * not remove all addresses from an association; sctp_bindx() will
9658c2ecf20Sopenharmony_ci * reject such an attempt with EINVAL.
9668c2ecf20Sopenharmony_ci *
9678c2ecf20Sopenharmony_ci * An application can use sctp_bindx(SCTP_BINDX_ADD_ADDR) to associate
9688c2ecf20Sopenharmony_ci * additional addresses with an endpoint after calling bind().  Or use
9698c2ecf20Sopenharmony_ci * sctp_bindx(SCTP_BINDX_REM_ADDR) to remove some addresses a listening
9708c2ecf20Sopenharmony_ci * socket is associated with so that no new association accepted will be
9718c2ecf20Sopenharmony_ci * associated with those addresses. If the endpoint supports dynamic
9728c2ecf20Sopenharmony_ci * address a SCTP_BINDX_REM_ADDR or SCTP_BINDX_ADD_ADDR may cause a
9738c2ecf20Sopenharmony_ci * endpoint to send the appropriate message to the peer to change the
9748c2ecf20Sopenharmony_ci * peers address lists.
9758c2ecf20Sopenharmony_ci *
9768c2ecf20Sopenharmony_ci * Adding and removing addresses from a connected association is
9778c2ecf20Sopenharmony_ci * optional functionality. Implementations that do not support this
9788c2ecf20Sopenharmony_ci * functionality should return EOPNOTSUPP.
9798c2ecf20Sopenharmony_ci *
9808c2ecf20Sopenharmony_ci * Basically do nothing but copying the addresses from user to kernel
9818c2ecf20Sopenharmony_ci * land and invoking either sctp_bindx_add() or sctp_bindx_rem() on the sk.
9828c2ecf20Sopenharmony_ci * This is used for tunneling the sctp_bindx() request through sctp_setsockopt()
9838c2ecf20Sopenharmony_ci * from userspace.
9848c2ecf20Sopenharmony_ci *
9858c2ecf20Sopenharmony_ci * On exit there is no need to do sockfd_put(), sys_setsockopt() does
9868c2ecf20Sopenharmony_ci * it.
9878c2ecf20Sopenharmony_ci *
9888c2ecf20Sopenharmony_ci * sk        The sk of the socket
9898c2ecf20Sopenharmony_ci * addrs     The pointer to the addresses
9908c2ecf20Sopenharmony_ci * addrssize Size of the addrs buffer
9918c2ecf20Sopenharmony_ci * op        Operation to perform (add or remove, see the flags of
9928c2ecf20Sopenharmony_ci *           sctp_bindx)
9938c2ecf20Sopenharmony_ci *
9948c2ecf20Sopenharmony_ci * Returns 0 if ok, <0 errno code on error.
9958c2ecf20Sopenharmony_ci */
9968c2ecf20Sopenharmony_cistatic int sctp_setsockopt_bindx(struct sock *sk, struct sockaddr *addrs,
9978c2ecf20Sopenharmony_ci				 int addrs_size, int op)
9988c2ecf20Sopenharmony_ci{
9998c2ecf20Sopenharmony_ci	int err;
10008c2ecf20Sopenharmony_ci	int addrcnt = 0;
10018c2ecf20Sopenharmony_ci	int walk_size = 0;
10028c2ecf20Sopenharmony_ci	struct sockaddr *sa_addr;
10038c2ecf20Sopenharmony_ci	void *addr_buf = addrs;
10048c2ecf20Sopenharmony_ci	struct sctp_af *af;
10058c2ecf20Sopenharmony_ci
10068c2ecf20Sopenharmony_ci	pr_debug("%s: sk:%p addrs:%p addrs_size:%d opt:%d\n",
10078c2ecf20Sopenharmony_ci		 __func__, sk, addr_buf, addrs_size, op);
10088c2ecf20Sopenharmony_ci
10098c2ecf20Sopenharmony_ci	if (unlikely(addrs_size <= 0))
10108c2ecf20Sopenharmony_ci		return -EINVAL;
10118c2ecf20Sopenharmony_ci
10128c2ecf20Sopenharmony_ci	/* Walk through the addrs buffer and count the number of addresses. */
10138c2ecf20Sopenharmony_ci	while (walk_size < addrs_size) {
10148c2ecf20Sopenharmony_ci		if (walk_size + sizeof(sa_family_t) > addrs_size)
10158c2ecf20Sopenharmony_ci			return -EINVAL;
10168c2ecf20Sopenharmony_ci
10178c2ecf20Sopenharmony_ci		sa_addr = addr_buf;
10188c2ecf20Sopenharmony_ci		af = sctp_get_af_specific(sa_addr->sa_family);
10198c2ecf20Sopenharmony_ci
10208c2ecf20Sopenharmony_ci		/* If the address family is not supported or if this address
10218c2ecf20Sopenharmony_ci		 * causes the address buffer to overflow return EINVAL.
10228c2ecf20Sopenharmony_ci		 */
10238c2ecf20Sopenharmony_ci		if (!af || (walk_size + af->sockaddr_len) > addrs_size)
10248c2ecf20Sopenharmony_ci			return -EINVAL;
10258c2ecf20Sopenharmony_ci		addrcnt++;
10268c2ecf20Sopenharmony_ci		addr_buf += af->sockaddr_len;
10278c2ecf20Sopenharmony_ci		walk_size += af->sockaddr_len;
10288c2ecf20Sopenharmony_ci	}
10298c2ecf20Sopenharmony_ci
10308c2ecf20Sopenharmony_ci	/* Do the work. */
10318c2ecf20Sopenharmony_ci	switch (op) {
10328c2ecf20Sopenharmony_ci	case SCTP_BINDX_ADD_ADDR:
10338c2ecf20Sopenharmony_ci		/* Allow security module to validate bindx addresses. */
10348c2ecf20Sopenharmony_ci		err = security_sctp_bind_connect(sk, SCTP_SOCKOPT_BINDX_ADD,
10358c2ecf20Sopenharmony_ci						 addrs, addrs_size);
10368c2ecf20Sopenharmony_ci		if (err)
10378c2ecf20Sopenharmony_ci			return err;
10388c2ecf20Sopenharmony_ci		err = sctp_bindx_add(sk, addrs, addrcnt);
10398c2ecf20Sopenharmony_ci		if (err)
10408c2ecf20Sopenharmony_ci			return err;
10418c2ecf20Sopenharmony_ci		return sctp_send_asconf_add_ip(sk, addrs, addrcnt);
10428c2ecf20Sopenharmony_ci	case SCTP_BINDX_REM_ADDR:
10438c2ecf20Sopenharmony_ci		err = sctp_bindx_rem(sk, addrs, addrcnt);
10448c2ecf20Sopenharmony_ci		if (err)
10458c2ecf20Sopenharmony_ci			return err;
10468c2ecf20Sopenharmony_ci		return sctp_send_asconf_del_ip(sk, addrs, addrcnt);
10478c2ecf20Sopenharmony_ci
10488c2ecf20Sopenharmony_ci	default:
10498c2ecf20Sopenharmony_ci		return -EINVAL;
10508c2ecf20Sopenharmony_ci	}
10518c2ecf20Sopenharmony_ci}
10528c2ecf20Sopenharmony_ci
10538c2ecf20Sopenharmony_cistatic int sctp_bind_add(struct sock *sk, struct sockaddr *addrs,
10548c2ecf20Sopenharmony_ci		int addrlen)
10558c2ecf20Sopenharmony_ci{
10568c2ecf20Sopenharmony_ci	int err;
10578c2ecf20Sopenharmony_ci
10588c2ecf20Sopenharmony_ci	lock_sock(sk);
10598c2ecf20Sopenharmony_ci	err = sctp_setsockopt_bindx(sk, addrs, addrlen, SCTP_BINDX_ADD_ADDR);
10608c2ecf20Sopenharmony_ci	release_sock(sk);
10618c2ecf20Sopenharmony_ci	return err;
10628c2ecf20Sopenharmony_ci}
10638c2ecf20Sopenharmony_ci
10648c2ecf20Sopenharmony_cistatic int sctp_connect_new_asoc(struct sctp_endpoint *ep,
10658c2ecf20Sopenharmony_ci				 const union sctp_addr *daddr,
10668c2ecf20Sopenharmony_ci				 const struct sctp_initmsg *init,
10678c2ecf20Sopenharmony_ci				 struct sctp_transport **tp)
10688c2ecf20Sopenharmony_ci{
10698c2ecf20Sopenharmony_ci	struct sctp_association *asoc;
10708c2ecf20Sopenharmony_ci	struct sock *sk = ep->base.sk;
10718c2ecf20Sopenharmony_ci	struct net *net = sock_net(sk);
10728c2ecf20Sopenharmony_ci	enum sctp_scope scope;
10738c2ecf20Sopenharmony_ci	int err;
10748c2ecf20Sopenharmony_ci
10758c2ecf20Sopenharmony_ci	if (sctp_endpoint_is_peeled_off(ep, daddr))
10768c2ecf20Sopenharmony_ci		return -EADDRNOTAVAIL;
10778c2ecf20Sopenharmony_ci
10788c2ecf20Sopenharmony_ci	if (!ep->base.bind_addr.port) {
10798c2ecf20Sopenharmony_ci		if (sctp_autobind(sk))
10808c2ecf20Sopenharmony_ci			return -EAGAIN;
10818c2ecf20Sopenharmony_ci	} else {
10828c2ecf20Sopenharmony_ci		if (inet_port_requires_bind_service(net, ep->base.bind_addr.port) &&
10838c2ecf20Sopenharmony_ci		    !ns_capable(net->user_ns, CAP_NET_BIND_SERVICE))
10848c2ecf20Sopenharmony_ci			return -EACCES;
10858c2ecf20Sopenharmony_ci	}
10868c2ecf20Sopenharmony_ci
10878c2ecf20Sopenharmony_ci	scope = sctp_scope(daddr);
10888c2ecf20Sopenharmony_ci	asoc = sctp_association_new(ep, sk, scope, GFP_KERNEL);
10898c2ecf20Sopenharmony_ci	if (!asoc)
10908c2ecf20Sopenharmony_ci		return -ENOMEM;
10918c2ecf20Sopenharmony_ci
10928c2ecf20Sopenharmony_ci	err = sctp_assoc_set_bind_addr_from_ep(asoc, scope, GFP_KERNEL);
10938c2ecf20Sopenharmony_ci	if (err < 0)
10948c2ecf20Sopenharmony_ci		goto free;
10958c2ecf20Sopenharmony_ci
10968c2ecf20Sopenharmony_ci	*tp = sctp_assoc_add_peer(asoc, daddr, GFP_KERNEL, SCTP_UNKNOWN);
10978c2ecf20Sopenharmony_ci	if (!*tp) {
10988c2ecf20Sopenharmony_ci		err = -ENOMEM;
10998c2ecf20Sopenharmony_ci		goto free;
11008c2ecf20Sopenharmony_ci	}
11018c2ecf20Sopenharmony_ci
11028c2ecf20Sopenharmony_ci	if (!init)
11038c2ecf20Sopenharmony_ci		return 0;
11048c2ecf20Sopenharmony_ci
11058c2ecf20Sopenharmony_ci	if (init->sinit_num_ostreams) {
11068c2ecf20Sopenharmony_ci		__u16 outcnt = init->sinit_num_ostreams;
11078c2ecf20Sopenharmony_ci
11088c2ecf20Sopenharmony_ci		asoc->c.sinit_num_ostreams = outcnt;
11098c2ecf20Sopenharmony_ci		/* outcnt has been changed, need to re-init stream */
11108c2ecf20Sopenharmony_ci		err = sctp_stream_init(&asoc->stream, outcnt, 0, GFP_KERNEL);
11118c2ecf20Sopenharmony_ci		if (err)
11128c2ecf20Sopenharmony_ci			goto free;
11138c2ecf20Sopenharmony_ci	}
11148c2ecf20Sopenharmony_ci
11158c2ecf20Sopenharmony_ci	if (init->sinit_max_instreams)
11168c2ecf20Sopenharmony_ci		asoc->c.sinit_max_instreams = init->sinit_max_instreams;
11178c2ecf20Sopenharmony_ci
11188c2ecf20Sopenharmony_ci	if (init->sinit_max_attempts)
11198c2ecf20Sopenharmony_ci		asoc->max_init_attempts = init->sinit_max_attempts;
11208c2ecf20Sopenharmony_ci
11218c2ecf20Sopenharmony_ci	if (init->sinit_max_init_timeo)
11228c2ecf20Sopenharmony_ci		asoc->max_init_timeo =
11238c2ecf20Sopenharmony_ci			msecs_to_jiffies(init->sinit_max_init_timeo);
11248c2ecf20Sopenharmony_ci
11258c2ecf20Sopenharmony_ci	return 0;
11268c2ecf20Sopenharmony_cifree:
11278c2ecf20Sopenharmony_ci	sctp_association_free(asoc);
11288c2ecf20Sopenharmony_ci	return err;
11298c2ecf20Sopenharmony_ci}
11308c2ecf20Sopenharmony_ci
11318c2ecf20Sopenharmony_cistatic int sctp_connect_add_peer(struct sctp_association *asoc,
11328c2ecf20Sopenharmony_ci				 union sctp_addr *daddr, int addr_len)
11338c2ecf20Sopenharmony_ci{
11348c2ecf20Sopenharmony_ci	struct sctp_endpoint *ep = asoc->ep;
11358c2ecf20Sopenharmony_ci	struct sctp_association *old;
11368c2ecf20Sopenharmony_ci	struct sctp_transport *t;
11378c2ecf20Sopenharmony_ci	int err;
11388c2ecf20Sopenharmony_ci
11398c2ecf20Sopenharmony_ci	err = sctp_verify_addr(ep->base.sk, daddr, addr_len);
11408c2ecf20Sopenharmony_ci	if (err)
11418c2ecf20Sopenharmony_ci		return err;
11428c2ecf20Sopenharmony_ci
11438c2ecf20Sopenharmony_ci	old = sctp_endpoint_lookup_assoc(ep, daddr, &t);
11448c2ecf20Sopenharmony_ci	if (old && old != asoc)
11458c2ecf20Sopenharmony_ci		return old->state >= SCTP_STATE_ESTABLISHED ? -EISCONN
11468c2ecf20Sopenharmony_ci							    : -EALREADY;
11478c2ecf20Sopenharmony_ci
11488c2ecf20Sopenharmony_ci	if (sctp_endpoint_is_peeled_off(ep, daddr))
11498c2ecf20Sopenharmony_ci		return -EADDRNOTAVAIL;
11508c2ecf20Sopenharmony_ci
11518c2ecf20Sopenharmony_ci	t = sctp_assoc_add_peer(asoc, daddr, GFP_KERNEL, SCTP_UNKNOWN);
11528c2ecf20Sopenharmony_ci	if (!t)
11538c2ecf20Sopenharmony_ci		return -ENOMEM;
11548c2ecf20Sopenharmony_ci
11558c2ecf20Sopenharmony_ci	return 0;
11568c2ecf20Sopenharmony_ci}
11578c2ecf20Sopenharmony_ci
11588c2ecf20Sopenharmony_ci/* __sctp_connect(struct sock* sk, struct sockaddr *kaddrs, int addrs_size)
11598c2ecf20Sopenharmony_ci *
11608c2ecf20Sopenharmony_ci * Common routine for handling connect() and sctp_connectx().
11618c2ecf20Sopenharmony_ci * Connect will come in with just a single address.
11628c2ecf20Sopenharmony_ci */
11638c2ecf20Sopenharmony_cistatic int __sctp_connect(struct sock *sk, struct sockaddr *kaddrs,
11648c2ecf20Sopenharmony_ci			  int addrs_size, int flags, sctp_assoc_t *assoc_id)
11658c2ecf20Sopenharmony_ci{
11668c2ecf20Sopenharmony_ci	struct sctp_sock *sp = sctp_sk(sk);
11678c2ecf20Sopenharmony_ci	struct sctp_endpoint *ep = sp->ep;
11688c2ecf20Sopenharmony_ci	struct sctp_transport *transport;
11698c2ecf20Sopenharmony_ci	struct sctp_association *asoc;
11708c2ecf20Sopenharmony_ci	void *addr_buf = kaddrs;
11718c2ecf20Sopenharmony_ci	union sctp_addr *daddr;
11728c2ecf20Sopenharmony_ci	struct sctp_af *af;
11738c2ecf20Sopenharmony_ci	int walk_size, err;
11748c2ecf20Sopenharmony_ci	long timeo;
11758c2ecf20Sopenharmony_ci
11768c2ecf20Sopenharmony_ci	if (sctp_sstate(sk, ESTABLISHED) || sctp_sstate(sk, CLOSING) ||
11778c2ecf20Sopenharmony_ci	    (sctp_style(sk, TCP) && sctp_sstate(sk, LISTENING)))
11788c2ecf20Sopenharmony_ci		return -EISCONN;
11798c2ecf20Sopenharmony_ci
11808c2ecf20Sopenharmony_ci	daddr = addr_buf;
11818c2ecf20Sopenharmony_ci	af = sctp_get_af_specific(daddr->sa.sa_family);
11828c2ecf20Sopenharmony_ci	if (!af || af->sockaddr_len > addrs_size)
11838c2ecf20Sopenharmony_ci		return -EINVAL;
11848c2ecf20Sopenharmony_ci
11858c2ecf20Sopenharmony_ci	err = sctp_verify_addr(sk, daddr, af->sockaddr_len);
11868c2ecf20Sopenharmony_ci	if (err)
11878c2ecf20Sopenharmony_ci		return err;
11888c2ecf20Sopenharmony_ci
11898c2ecf20Sopenharmony_ci	asoc = sctp_endpoint_lookup_assoc(ep, daddr, &transport);
11908c2ecf20Sopenharmony_ci	if (asoc)
11918c2ecf20Sopenharmony_ci		return asoc->state >= SCTP_STATE_ESTABLISHED ? -EISCONN
11928c2ecf20Sopenharmony_ci							     : -EALREADY;
11938c2ecf20Sopenharmony_ci
11948c2ecf20Sopenharmony_ci	err = sctp_connect_new_asoc(ep, daddr, NULL, &transport);
11958c2ecf20Sopenharmony_ci	if (err)
11968c2ecf20Sopenharmony_ci		return err;
11978c2ecf20Sopenharmony_ci	asoc = transport->asoc;
11988c2ecf20Sopenharmony_ci
11998c2ecf20Sopenharmony_ci	addr_buf += af->sockaddr_len;
12008c2ecf20Sopenharmony_ci	walk_size = af->sockaddr_len;
12018c2ecf20Sopenharmony_ci	while (walk_size < addrs_size) {
12028c2ecf20Sopenharmony_ci		err = -EINVAL;
12038c2ecf20Sopenharmony_ci		if (walk_size + sizeof(sa_family_t) > addrs_size)
12048c2ecf20Sopenharmony_ci			goto out_free;
12058c2ecf20Sopenharmony_ci
12068c2ecf20Sopenharmony_ci		daddr = addr_buf;
12078c2ecf20Sopenharmony_ci		af = sctp_get_af_specific(daddr->sa.sa_family);
12088c2ecf20Sopenharmony_ci		if (!af || af->sockaddr_len + walk_size > addrs_size)
12098c2ecf20Sopenharmony_ci			goto out_free;
12108c2ecf20Sopenharmony_ci
12118c2ecf20Sopenharmony_ci		if (asoc->peer.port != ntohs(daddr->v4.sin_port))
12128c2ecf20Sopenharmony_ci			goto out_free;
12138c2ecf20Sopenharmony_ci
12148c2ecf20Sopenharmony_ci		err = sctp_connect_add_peer(asoc, daddr, af->sockaddr_len);
12158c2ecf20Sopenharmony_ci		if (err)
12168c2ecf20Sopenharmony_ci			goto out_free;
12178c2ecf20Sopenharmony_ci
12188c2ecf20Sopenharmony_ci		addr_buf  += af->sockaddr_len;
12198c2ecf20Sopenharmony_ci		walk_size += af->sockaddr_len;
12208c2ecf20Sopenharmony_ci	}
12218c2ecf20Sopenharmony_ci
12228c2ecf20Sopenharmony_ci	/* In case the user of sctp_connectx() wants an association
12238c2ecf20Sopenharmony_ci	 * id back, assign one now.
12248c2ecf20Sopenharmony_ci	 */
12258c2ecf20Sopenharmony_ci	if (assoc_id) {
12268c2ecf20Sopenharmony_ci		err = sctp_assoc_set_id(asoc, GFP_KERNEL);
12278c2ecf20Sopenharmony_ci		if (err < 0)
12288c2ecf20Sopenharmony_ci			goto out_free;
12298c2ecf20Sopenharmony_ci	}
12308c2ecf20Sopenharmony_ci
12318c2ecf20Sopenharmony_ci	err = sctp_primitive_ASSOCIATE(sock_net(sk), asoc, NULL);
12328c2ecf20Sopenharmony_ci	if (err < 0)
12338c2ecf20Sopenharmony_ci		goto out_free;
12348c2ecf20Sopenharmony_ci
12358c2ecf20Sopenharmony_ci	/* Initialize sk's dport and daddr for getpeername() */
12368c2ecf20Sopenharmony_ci	inet_sk(sk)->inet_dport = htons(asoc->peer.port);
12378c2ecf20Sopenharmony_ci	sp->pf->to_sk_daddr(daddr, sk);
12388c2ecf20Sopenharmony_ci	sk->sk_err = 0;
12398c2ecf20Sopenharmony_ci
12408c2ecf20Sopenharmony_ci	if (assoc_id)
12418c2ecf20Sopenharmony_ci		*assoc_id = asoc->assoc_id;
12428c2ecf20Sopenharmony_ci
12438c2ecf20Sopenharmony_ci	timeo = sock_sndtimeo(sk, flags & O_NONBLOCK);
12448c2ecf20Sopenharmony_ci	return sctp_wait_for_connect(asoc, &timeo);
12458c2ecf20Sopenharmony_ci
12468c2ecf20Sopenharmony_ciout_free:
12478c2ecf20Sopenharmony_ci	pr_debug("%s: took out_free path with asoc:%p kaddrs:%p err:%d\n",
12488c2ecf20Sopenharmony_ci		 __func__, asoc, kaddrs, err);
12498c2ecf20Sopenharmony_ci	sctp_association_free(asoc);
12508c2ecf20Sopenharmony_ci	return err;
12518c2ecf20Sopenharmony_ci}
12528c2ecf20Sopenharmony_ci
12538c2ecf20Sopenharmony_ci/* Helper for tunneling sctp_connectx() requests through sctp_setsockopt()
12548c2ecf20Sopenharmony_ci *
12558c2ecf20Sopenharmony_ci * API 8.9
12568c2ecf20Sopenharmony_ci * int sctp_connectx(int sd, struct sockaddr *addrs, int addrcnt,
12578c2ecf20Sopenharmony_ci * 			sctp_assoc_t *asoc);
12588c2ecf20Sopenharmony_ci *
12598c2ecf20Sopenharmony_ci * If sd is an IPv4 socket, the addresses passed must be IPv4 addresses.
12608c2ecf20Sopenharmony_ci * If the sd is an IPv6 socket, the addresses passed can either be IPv4
12618c2ecf20Sopenharmony_ci * or IPv6 addresses.
12628c2ecf20Sopenharmony_ci *
12638c2ecf20Sopenharmony_ci * A single address may be specified as INADDR_ANY or IN6ADDR_ANY, see
12648c2ecf20Sopenharmony_ci * Section 3.1.2 for this usage.
12658c2ecf20Sopenharmony_ci *
12668c2ecf20Sopenharmony_ci * addrs is a pointer to an array of one or more socket addresses. Each
12678c2ecf20Sopenharmony_ci * address is contained in its appropriate structure (i.e. struct
12688c2ecf20Sopenharmony_ci * sockaddr_in or struct sockaddr_in6) the family of the address type
12698c2ecf20Sopenharmony_ci * must be used to distengish the address length (note that this
12708c2ecf20Sopenharmony_ci * representation is termed a "packed array" of addresses). The caller
12718c2ecf20Sopenharmony_ci * specifies the number of addresses in the array with addrcnt.
12728c2ecf20Sopenharmony_ci *
12738c2ecf20Sopenharmony_ci * On success, sctp_connectx() returns 0. It also sets the assoc_id to
12748c2ecf20Sopenharmony_ci * the association id of the new association.  On failure, sctp_connectx()
12758c2ecf20Sopenharmony_ci * returns -1, and sets errno to the appropriate error code.  The assoc_id
12768c2ecf20Sopenharmony_ci * is not touched by the kernel.
12778c2ecf20Sopenharmony_ci *
12788c2ecf20Sopenharmony_ci * For SCTP, the port given in each socket address must be the same, or
12798c2ecf20Sopenharmony_ci * sctp_connectx() will fail, setting errno to EINVAL.
12808c2ecf20Sopenharmony_ci *
12818c2ecf20Sopenharmony_ci * An application can use sctp_connectx to initiate an association with
12828c2ecf20Sopenharmony_ci * an endpoint that is multi-homed.  Much like sctp_bindx() this call
12838c2ecf20Sopenharmony_ci * allows a caller to specify multiple addresses at which a peer can be
12848c2ecf20Sopenharmony_ci * reached.  The way the SCTP stack uses the list of addresses to set up
12858c2ecf20Sopenharmony_ci * the association is implementation dependent.  This function only
12868c2ecf20Sopenharmony_ci * specifies that the stack will try to make use of all the addresses in
12878c2ecf20Sopenharmony_ci * the list when needed.
12888c2ecf20Sopenharmony_ci *
12898c2ecf20Sopenharmony_ci * Note that the list of addresses passed in is only used for setting up
12908c2ecf20Sopenharmony_ci * the association.  It does not necessarily equal the set of addresses
12918c2ecf20Sopenharmony_ci * the peer uses for the resulting association.  If the caller wants to
12928c2ecf20Sopenharmony_ci * find out the set of peer addresses, it must use sctp_getpaddrs() to
12938c2ecf20Sopenharmony_ci * retrieve them after the association has been set up.
12948c2ecf20Sopenharmony_ci *
12958c2ecf20Sopenharmony_ci * Basically do nothing but copying the addresses from user to kernel
12968c2ecf20Sopenharmony_ci * land and invoking either sctp_connectx(). This is used for tunneling
12978c2ecf20Sopenharmony_ci * the sctp_connectx() request through sctp_setsockopt() from userspace.
12988c2ecf20Sopenharmony_ci *
12998c2ecf20Sopenharmony_ci * On exit there is no need to do sockfd_put(), sys_setsockopt() does
13008c2ecf20Sopenharmony_ci * it.
13018c2ecf20Sopenharmony_ci *
13028c2ecf20Sopenharmony_ci * sk        The sk of the socket
13038c2ecf20Sopenharmony_ci * addrs     The pointer to the addresses
13048c2ecf20Sopenharmony_ci * addrssize Size of the addrs buffer
13058c2ecf20Sopenharmony_ci *
13068c2ecf20Sopenharmony_ci * Returns >=0 if ok, <0 errno code on error.
13078c2ecf20Sopenharmony_ci */
13088c2ecf20Sopenharmony_cistatic int __sctp_setsockopt_connectx(struct sock *sk, struct sockaddr *kaddrs,
13098c2ecf20Sopenharmony_ci				      int addrs_size, sctp_assoc_t *assoc_id)
13108c2ecf20Sopenharmony_ci{
13118c2ecf20Sopenharmony_ci	int err = 0, flags = 0;
13128c2ecf20Sopenharmony_ci
13138c2ecf20Sopenharmony_ci	pr_debug("%s: sk:%p addrs:%p addrs_size:%d\n",
13148c2ecf20Sopenharmony_ci		 __func__, sk, kaddrs, addrs_size);
13158c2ecf20Sopenharmony_ci
13168c2ecf20Sopenharmony_ci	/* make sure the 1st addr's sa_family is accessible later */
13178c2ecf20Sopenharmony_ci	if (unlikely(addrs_size < sizeof(sa_family_t)))
13188c2ecf20Sopenharmony_ci		return -EINVAL;
13198c2ecf20Sopenharmony_ci
13208c2ecf20Sopenharmony_ci	/* Allow security module to validate connectx addresses. */
13218c2ecf20Sopenharmony_ci	err = security_sctp_bind_connect(sk, SCTP_SOCKOPT_CONNECTX,
13228c2ecf20Sopenharmony_ci					 (struct sockaddr *)kaddrs,
13238c2ecf20Sopenharmony_ci					  addrs_size);
13248c2ecf20Sopenharmony_ci	if (err)
13258c2ecf20Sopenharmony_ci		return err;
13268c2ecf20Sopenharmony_ci
13278c2ecf20Sopenharmony_ci	/* in-kernel sockets don't generally have a file allocated to them
13288c2ecf20Sopenharmony_ci	 * if all they do is call sock_create_kern().
13298c2ecf20Sopenharmony_ci	 */
13308c2ecf20Sopenharmony_ci	if (sk->sk_socket->file)
13318c2ecf20Sopenharmony_ci		flags = sk->sk_socket->file->f_flags;
13328c2ecf20Sopenharmony_ci
13338c2ecf20Sopenharmony_ci	return __sctp_connect(sk, kaddrs, addrs_size, flags, assoc_id);
13348c2ecf20Sopenharmony_ci}
13358c2ecf20Sopenharmony_ci
13368c2ecf20Sopenharmony_ci/*
13378c2ecf20Sopenharmony_ci * This is an older interface.  It's kept for backward compatibility
13388c2ecf20Sopenharmony_ci * to the option that doesn't provide association id.
13398c2ecf20Sopenharmony_ci */
13408c2ecf20Sopenharmony_cistatic int sctp_setsockopt_connectx_old(struct sock *sk,
13418c2ecf20Sopenharmony_ci					struct sockaddr *kaddrs,
13428c2ecf20Sopenharmony_ci					int addrs_size)
13438c2ecf20Sopenharmony_ci{
13448c2ecf20Sopenharmony_ci	return __sctp_setsockopt_connectx(sk, kaddrs, addrs_size, NULL);
13458c2ecf20Sopenharmony_ci}
13468c2ecf20Sopenharmony_ci
13478c2ecf20Sopenharmony_ci/*
13488c2ecf20Sopenharmony_ci * New interface for the API.  The since the API is done with a socket
13498c2ecf20Sopenharmony_ci * option, to make it simple we feed back the association id is as a return
13508c2ecf20Sopenharmony_ci * indication to the call.  Error is always negative and association id is
13518c2ecf20Sopenharmony_ci * always positive.
13528c2ecf20Sopenharmony_ci */
13538c2ecf20Sopenharmony_cistatic int sctp_setsockopt_connectx(struct sock *sk,
13548c2ecf20Sopenharmony_ci				    struct sockaddr *kaddrs,
13558c2ecf20Sopenharmony_ci				    int addrs_size)
13568c2ecf20Sopenharmony_ci{
13578c2ecf20Sopenharmony_ci	sctp_assoc_t assoc_id = 0;
13588c2ecf20Sopenharmony_ci	int err = 0;
13598c2ecf20Sopenharmony_ci
13608c2ecf20Sopenharmony_ci	err = __sctp_setsockopt_connectx(sk, kaddrs, addrs_size, &assoc_id);
13618c2ecf20Sopenharmony_ci
13628c2ecf20Sopenharmony_ci	if (err)
13638c2ecf20Sopenharmony_ci		return err;
13648c2ecf20Sopenharmony_ci	else
13658c2ecf20Sopenharmony_ci		return assoc_id;
13668c2ecf20Sopenharmony_ci}
13678c2ecf20Sopenharmony_ci
13688c2ecf20Sopenharmony_ci/*
13698c2ecf20Sopenharmony_ci * New (hopefully final) interface for the API.
13708c2ecf20Sopenharmony_ci * We use the sctp_getaddrs_old structure so that use-space library
13718c2ecf20Sopenharmony_ci * can avoid any unnecessary allocations. The only different part
13728c2ecf20Sopenharmony_ci * is that we store the actual length of the address buffer into the
13738c2ecf20Sopenharmony_ci * addrs_num structure member. That way we can re-use the existing
13748c2ecf20Sopenharmony_ci * code.
13758c2ecf20Sopenharmony_ci */
13768c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT
13778c2ecf20Sopenharmony_cistruct compat_sctp_getaddrs_old {
13788c2ecf20Sopenharmony_ci	sctp_assoc_t	assoc_id;
13798c2ecf20Sopenharmony_ci	s32		addr_num;
13808c2ecf20Sopenharmony_ci	compat_uptr_t	addrs;		/* struct sockaddr * */
13818c2ecf20Sopenharmony_ci};
13828c2ecf20Sopenharmony_ci#endif
13838c2ecf20Sopenharmony_ci
13848c2ecf20Sopenharmony_cistatic int sctp_getsockopt_connectx3(struct sock *sk, int len,
13858c2ecf20Sopenharmony_ci				     char __user *optval,
13868c2ecf20Sopenharmony_ci				     int __user *optlen)
13878c2ecf20Sopenharmony_ci{
13888c2ecf20Sopenharmony_ci	struct sctp_getaddrs_old param;
13898c2ecf20Sopenharmony_ci	sctp_assoc_t assoc_id = 0;
13908c2ecf20Sopenharmony_ci	struct sockaddr *kaddrs;
13918c2ecf20Sopenharmony_ci	int err = 0;
13928c2ecf20Sopenharmony_ci
13938c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT
13948c2ecf20Sopenharmony_ci	if (in_compat_syscall()) {
13958c2ecf20Sopenharmony_ci		struct compat_sctp_getaddrs_old param32;
13968c2ecf20Sopenharmony_ci
13978c2ecf20Sopenharmony_ci		if (len < sizeof(param32))
13988c2ecf20Sopenharmony_ci			return -EINVAL;
13998c2ecf20Sopenharmony_ci		if (copy_from_user(&param32, optval, sizeof(param32)))
14008c2ecf20Sopenharmony_ci			return -EFAULT;
14018c2ecf20Sopenharmony_ci
14028c2ecf20Sopenharmony_ci		param.assoc_id = param32.assoc_id;
14038c2ecf20Sopenharmony_ci		param.addr_num = param32.addr_num;
14048c2ecf20Sopenharmony_ci		param.addrs = compat_ptr(param32.addrs);
14058c2ecf20Sopenharmony_ci	} else
14068c2ecf20Sopenharmony_ci#endif
14078c2ecf20Sopenharmony_ci	{
14088c2ecf20Sopenharmony_ci		if (len < sizeof(param))
14098c2ecf20Sopenharmony_ci			return -EINVAL;
14108c2ecf20Sopenharmony_ci		if (copy_from_user(&param, optval, sizeof(param)))
14118c2ecf20Sopenharmony_ci			return -EFAULT;
14128c2ecf20Sopenharmony_ci	}
14138c2ecf20Sopenharmony_ci
14148c2ecf20Sopenharmony_ci	kaddrs = memdup_user(param.addrs, param.addr_num);
14158c2ecf20Sopenharmony_ci	if (IS_ERR(kaddrs))
14168c2ecf20Sopenharmony_ci		return PTR_ERR(kaddrs);
14178c2ecf20Sopenharmony_ci
14188c2ecf20Sopenharmony_ci	err = __sctp_setsockopt_connectx(sk, kaddrs, param.addr_num, &assoc_id);
14198c2ecf20Sopenharmony_ci	kfree(kaddrs);
14208c2ecf20Sopenharmony_ci	if (err == 0 || err == -EINPROGRESS) {
14218c2ecf20Sopenharmony_ci		if (copy_to_user(optval, &assoc_id, sizeof(assoc_id)))
14228c2ecf20Sopenharmony_ci			return -EFAULT;
14238c2ecf20Sopenharmony_ci		if (put_user(sizeof(assoc_id), optlen))
14248c2ecf20Sopenharmony_ci			return -EFAULT;
14258c2ecf20Sopenharmony_ci	}
14268c2ecf20Sopenharmony_ci
14278c2ecf20Sopenharmony_ci	return err;
14288c2ecf20Sopenharmony_ci}
14298c2ecf20Sopenharmony_ci
14308c2ecf20Sopenharmony_ci/* API 3.1.4 close() - UDP Style Syntax
14318c2ecf20Sopenharmony_ci * Applications use close() to perform graceful shutdown (as described in
14328c2ecf20Sopenharmony_ci * Section 10.1 of [SCTP]) on ALL the associations currently represented
14338c2ecf20Sopenharmony_ci * by a UDP-style socket.
14348c2ecf20Sopenharmony_ci *
14358c2ecf20Sopenharmony_ci * The syntax is
14368c2ecf20Sopenharmony_ci *
14378c2ecf20Sopenharmony_ci *   ret = close(int sd);
14388c2ecf20Sopenharmony_ci *
14398c2ecf20Sopenharmony_ci *   sd      - the socket descriptor of the associations to be closed.
14408c2ecf20Sopenharmony_ci *
14418c2ecf20Sopenharmony_ci * To gracefully shutdown a specific association represented by the
14428c2ecf20Sopenharmony_ci * UDP-style socket, an application should use the sendmsg() call,
14438c2ecf20Sopenharmony_ci * passing no user data, but including the appropriate flag in the
14448c2ecf20Sopenharmony_ci * ancillary data (see Section xxxx).
14458c2ecf20Sopenharmony_ci *
14468c2ecf20Sopenharmony_ci * If sd in the close() call is a branched-off socket representing only
14478c2ecf20Sopenharmony_ci * one association, the shutdown is performed on that association only.
14488c2ecf20Sopenharmony_ci *
14498c2ecf20Sopenharmony_ci * 4.1.6 close() - TCP Style Syntax
14508c2ecf20Sopenharmony_ci *
14518c2ecf20Sopenharmony_ci * Applications use close() to gracefully close down an association.
14528c2ecf20Sopenharmony_ci *
14538c2ecf20Sopenharmony_ci * The syntax is:
14548c2ecf20Sopenharmony_ci *
14558c2ecf20Sopenharmony_ci *    int close(int sd);
14568c2ecf20Sopenharmony_ci *
14578c2ecf20Sopenharmony_ci *      sd      - the socket descriptor of the association to be closed.
14588c2ecf20Sopenharmony_ci *
14598c2ecf20Sopenharmony_ci * After an application calls close() on a socket descriptor, no further
14608c2ecf20Sopenharmony_ci * socket operations will succeed on that descriptor.
14618c2ecf20Sopenharmony_ci *
14628c2ecf20Sopenharmony_ci * API 7.1.4 SO_LINGER
14638c2ecf20Sopenharmony_ci *
14648c2ecf20Sopenharmony_ci * An application using the TCP-style socket can use this option to
14658c2ecf20Sopenharmony_ci * perform the SCTP ABORT primitive.  The linger option structure is:
14668c2ecf20Sopenharmony_ci *
14678c2ecf20Sopenharmony_ci *  struct  linger {
14688c2ecf20Sopenharmony_ci *     int     l_onoff;                // option on/off
14698c2ecf20Sopenharmony_ci *     int     l_linger;               // linger time
14708c2ecf20Sopenharmony_ci * };
14718c2ecf20Sopenharmony_ci *
14728c2ecf20Sopenharmony_ci * To enable the option, set l_onoff to 1.  If the l_linger value is set
14738c2ecf20Sopenharmony_ci * to 0, calling close() is the same as the ABORT primitive.  If the
14748c2ecf20Sopenharmony_ci * value is set to a negative value, the setsockopt() call will return
14758c2ecf20Sopenharmony_ci * an error.  If the value is set to a positive value linger_time, the
14768c2ecf20Sopenharmony_ci * close() can be blocked for at most linger_time ms.  If the graceful
14778c2ecf20Sopenharmony_ci * shutdown phase does not finish during this period, close() will
14788c2ecf20Sopenharmony_ci * return but the graceful shutdown phase continues in the system.
14798c2ecf20Sopenharmony_ci */
14808c2ecf20Sopenharmony_cistatic void sctp_close(struct sock *sk, long timeout)
14818c2ecf20Sopenharmony_ci{
14828c2ecf20Sopenharmony_ci	struct net *net = sock_net(sk);
14838c2ecf20Sopenharmony_ci	struct sctp_endpoint *ep;
14848c2ecf20Sopenharmony_ci	struct sctp_association *asoc;
14858c2ecf20Sopenharmony_ci	struct list_head *pos, *temp;
14868c2ecf20Sopenharmony_ci	unsigned int data_was_unread;
14878c2ecf20Sopenharmony_ci
14888c2ecf20Sopenharmony_ci	pr_debug("%s: sk:%p, timeout:%ld\n", __func__, sk, timeout);
14898c2ecf20Sopenharmony_ci
14908c2ecf20Sopenharmony_ci	lock_sock_nested(sk, SINGLE_DEPTH_NESTING);
14918c2ecf20Sopenharmony_ci	sk->sk_shutdown = SHUTDOWN_MASK;
14928c2ecf20Sopenharmony_ci	inet_sk_set_state(sk, SCTP_SS_CLOSING);
14938c2ecf20Sopenharmony_ci
14948c2ecf20Sopenharmony_ci	ep = sctp_sk(sk)->ep;
14958c2ecf20Sopenharmony_ci
14968c2ecf20Sopenharmony_ci	/* Clean up any skbs sitting on the receive queue.  */
14978c2ecf20Sopenharmony_ci	data_was_unread = sctp_queue_purge_ulpevents(&sk->sk_receive_queue);
14988c2ecf20Sopenharmony_ci	data_was_unread += sctp_queue_purge_ulpevents(&sctp_sk(sk)->pd_lobby);
14998c2ecf20Sopenharmony_ci
15008c2ecf20Sopenharmony_ci	/* Walk all associations on an endpoint.  */
15018c2ecf20Sopenharmony_ci	list_for_each_safe(pos, temp, &ep->asocs) {
15028c2ecf20Sopenharmony_ci		asoc = list_entry(pos, struct sctp_association, asocs);
15038c2ecf20Sopenharmony_ci
15048c2ecf20Sopenharmony_ci		if (sctp_style(sk, TCP)) {
15058c2ecf20Sopenharmony_ci			/* A closed association can still be in the list if
15068c2ecf20Sopenharmony_ci			 * it belongs to a TCP-style listening socket that is
15078c2ecf20Sopenharmony_ci			 * not yet accepted. If so, free it. If not, send an
15088c2ecf20Sopenharmony_ci			 * ABORT or SHUTDOWN based on the linger options.
15098c2ecf20Sopenharmony_ci			 */
15108c2ecf20Sopenharmony_ci			if (sctp_state(asoc, CLOSED)) {
15118c2ecf20Sopenharmony_ci				sctp_association_free(asoc);
15128c2ecf20Sopenharmony_ci				continue;
15138c2ecf20Sopenharmony_ci			}
15148c2ecf20Sopenharmony_ci		}
15158c2ecf20Sopenharmony_ci
15168c2ecf20Sopenharmony_ci		if (data_was_unread || !skb_queue_empty(&asoc->ulpq.lobby) ||
15178c2ecf20Sopenharmony_ci		    !skb_queue_empty(&asoc->ulpq.reasm) ||
15188c2ecf20Sopenharmony_ci		    !skb_queue_empty(&asoc->ulpq.reasm_uo) ||
15198c2ecf20Sopenharmony_ci		    (sock_flag(sk, SOCK_LINGER) && !sk->sk_lingertime)) {
15208c2ecf20Sopenharmony_ci			struct sctp_chunk *chunk;
15218c2ecf20Sopenharmony_ci
15228c2ecf20Sopenharmony_ci			chunk = sctp_make_abort_user(asoc, NULL, 0);
15238c2ecf20Sopenharmony_ci			sctp_primitive_ABORT(net, asoc, chunk);
15248c2ecf20Sopenharmony_ci		} else
15258c2ecf20Sopenharmony_ci			sctp_primitive_SHUTDOWN(net, asoc, NULL);
15268c2ecf20Sopenharmony_ci	}
15278c2ecf20Sopenharmony_ci
15288c2ecf20Sopenharmony_ci	/* On a TCP-style socket, block for at most linger_time if set. */
15298c2ecf20Sopenharmony_ci	if (sctp_style(sk, TCP) && timeout)
15308c2ecf20Sopenharmony_ci		sctp_wait_for_close(sk, timeout);
15318c2ecf20Sopenharmony_ci
15328c2ecf20Sopenharmony_ci	/* This will run the backlog queue.  */
15338c2ecf20Sopenharmony_ci	release_sock(sk);
15348c2ecf20Sopenharmony_ci
15358c2ecf20Sopenharmony_ci	/* Supposedly, no process has access to the socket, but
15368c2ecf20Sopenharmony_ci	 * the net layers still may.
15378c2ecf20Sopenharmony_ci	 * Also, sctp_destroy_sock() needs to be called with addr_wq_lock
15388c2ecf20Sopenharmony_ci	 * held and that should be grabbed before socket lock.
15398c2ecf20Sopenharmony_ci	 */
15408c2ecf20Sopenharmony_ci	spin_lock_bh(&net->sctp.addr_wq_lock);
15418c2ecf20Sopenharmony_ci	bh_lock_sock_nested(sk);
15428c2ecf20Sopenharmony_ci
15438c2ecf20Sopenharmony_ci	/* Hold the sock, since sk_common_release() will put sock_put()
15448c2ecf20Sopenharmony_ci	 * and we have just a little more cleanup.
15458c2ecf20Sopenharmony_ci	 */
15468c2ecf20Sopenharmony_ci	sock_hold(sk);
15478c2ecf20Sopenharmony_ci	sk_common_release(sk);
15488c2ecf20Sopenharmony_ci
15498c2ecf20Sopenharmony_ci	bh_unlock_sock(sk);
15508c2ecf20Sopenharmony_ci	spin_unlock_bh(&net->sctp.addr_wq_lock);
15518c2ecf20Sopenharmony_ci
15528c2ecf20Sopenharmony_ci	sock_put(sk);
15538c2ecf20Sopenharmony_ci
15548c2ecf20Sopenharmony_ci	SCTP_DBG_OBJCNT_DEC(sock);
15558c2ecf20Sopenharmony_ci}
15568c2ecf20Sopenharmony_ci
15578c2ecf20Sopenharmony_ci/* Handle EPIPE error. */
15588c2ecf20Sopenharmony_cistatic int sctp_error(struct sock *sk, int flags, int err)
15598c2ecf20Sopenharmony_ci{
15608c2ecf20Sopenharmony_ci	if (err == -EPIPE)
15618c2ecf20Sopenharmony_ci		err = sock_error(sk) ? : -EPIPE;
15628c2ecf20Sopenharmony_ci	if (err == -EPIPE && !(flags & MSG_NOSIGNAL))
15638c2ecf20Sopenharmony_ci		send_sig(SIGPIPE, current, 0);
15648c2ecf20Sopenharmony_ci	return err;
15658c2ecf20Sopenharmony_ci}
15668c2ecf20Sopenharmony_ci
15678c2ecf20Sopenharmony_ci/* API 3.1.3 sendmsg() - UDP Style Syntax
15688c2ecf20Sopenharmony_ci *
15698c2ecf20Sopenharmony_ci * An application uses sendmsg() and recvmsg() calls to transmit data to
15708c2ecf20Sopenharmony_ci * and receive data from its peer.
15718c2ecf20Sopenharmony_ci *
15728c2ecf20Sopenharmony_ci *  ssize_t sendmsg(int socket, const struct msghdr *message,
15738c2ecf20Sopenharmony_ci *                  int flags);
15748c2ecf20Sopenharmony_ci *
15758c2ecf20Sopenharmony_ci *  socket  - the socket descriptor of the endpoint.
15768c2ecf20Sopenharmony_ci *  message - pointer to the msghdr structure which contains a single
15778c2ecf20Sopenharmony_ci *            user message and possibly some ancillary data.
15788c2ecf20Sopenharmony_ci *
15798c2ecf20Sopenharmony_ci *            See Section 5 for complete description of the data
15808c2ecf20Sopenharmony_ci *            structures.
15818c2ecf20Sopenharmony_ci *
15828c2ecf20Sopenharmony_ci *  flags   - flags sent or received with the user message, see Section
15838c2ecf20Sopenharmony_ci *            5 for complete description of the flags.
15848c2ecf20Sopenharmony_ci *
15858c2ecf20Sopenharmony_ci * Note:  This function could use a rewrite especially when explicit
15868c2ecf20Sopenharmony_ci * connect support comes in.
15878c2ecf20Sopenharmony_ci */
15888c2ecf20Sopenharmony_ci/* BUG:  We do not implement the equivalent of sk_stream_wait_memory(). */
15898c2ecf20Sopenharmony_ci
15908c2ecf20Sopenharmony_cistatic int sctp_msghdr_parse(const struct msghdr *msg,
15918c2ecf20Sopenharmony_ci			     struct sctp_cmsgs *cmsgs);
15928c2ecf20Sopenharmony_ci
15938c2ecf20Sopenharmony_cistatic int sctp_sendmsg_parse(struct sock *sk, struct sctp_cmsgs *cmsgs,
15948c2ecf20Sopenharmony_ci			      struct sctp_sndrcvinfo *srinfo,
15958c2ecf20Sopenharmony_ci			      const struct msghdr *msg, size_t msg_len)
15968c2ecf20Sopenharmony_ci{
15978c2ecf20Sopenharmony_ci	__u16 sflags;
15988c2ecf20Sopenharmony_ci	int err;
15998c2ecf20Sopenharmony_ci
16008c2ecf20Sopenharmony_ci	if (sctp_sstate(sk, LISTENING) && sctp_style(sk, TCP))
16018c2ecf20Sopenharmony_ci		return -EPIPE;
16028c2ecf20Sopenharmony_ci
16038c2ecf20Sopenharmony_ci	if (msg_len > sk->sk_sndbuf)
16048c2ecf20Sopenharmony_ci		return -EMSGSIZE;
16058c2ecf20Sopenharmony_ci
16068c2ecf20Sopenharmony_ci	memset(cmsgs, 0, sizeof(*cmsgs));
16078c2ecf20Sopenharmony_ci	err = sctp_msghdr_parse(msg, cmsgs);
16088c2ecf20Sopenharmony_ci	if (err) {
16098c2ecf20Sopenharmony_ci		pr_debug("%s: msghdr parse err:%x\n", __func__, err);
16108c2ecf20Sopenharmony_ci		return err;
16118c2ecf20Sopenharmony_ci	}
16128c2ecf20Sopenharmony_ci
16138c2ecf20Sopenharmony_ci	memset(srinfo, 0, sizeof(*srinfo));
16148c2ecf20Sopenharmony_ci	if (cmsgs->srinfo) {
16158c2ecf20Sopenharmony_ci		srinfo->sinfo_stream = cmsgs->srinfo->sinfo_stream;
16168c2ecf20Sopenharmony_ci		srinfo->sinfo_flags = cmsgs->srinfo->sinfo_flags;
16178c2ecf20Sopenharmony_ci		srinfo->sinfo_ppid = cmsgs->srinfo->sinfo_ppid;
16188c2ecf20Sopenharmony_ci		srinfo->sinfo_context = cmsgs->srinfo->sinfo_context;
16198c2ecf20Sopenharmony_ci		srinfo->sinfo_assoc_id = cmsgs->srinfo->sinfo_assoc_id;
16208c2ecf20Sopenharmony_ci		srinfo->sinfo_timetolive = cmsgs->srinfo->sinfo_timetolive;
16218c2ecf20Sopenharmony_ci	}
16228c2ecf20Sopenharmony_ci
16238c2ecf20Sopenharmony_ci	if (cmsgs->sinfo) {
16248c2ecf20Sopenharmony_ci		srinfo->sinfo_stream = cmsgs->sinfo->snd_sid;
16258c2ecf20Sopenharmony_ci		srinfo->sinfo_flags = cmsgs->sinfo->snd_flags;
16268c2ecf20Sopenharmony_ci		srinfo->sinfo_ppid = cmsgs->sinfo->snd_ppid;
16278c2ecf20Sopenharmony_ci		srinfo->sinfo_context = cmsgs->sinfo->snd_context;
16288c2ecf20Sopenharmony_ci		srinfo->sinfo_assoc_id = cmsgs->sinfo->snd_assoc_id;
16298c2ecf20Sopenharmony_ci	}
16308c2ecf20Sopenharmony_ci
16318c2ecf20Sopenharmony_ci	if (cmsgs->prinfo) {
16328c2ecf20Sopenharmony_ci		srinfo->sinfo_timetolive = cmsgs->prinfo->pr_value;
16338c2ecf20Sopenharmony_ci		SCTP_PR_SET_POLICY(srinfo->sinfo_flags,
16348c2ecf20Sopenharmony_ci				   cmsgs->prinfo->pr_policy);
16358c2ecf20Sopenharmony_ci	}
16368c2ecf20Sopenharmony_ci
16378c2ecf20Sopenharmony_ci	sflags = srinfo->sinfo_flags;
16388c2ecf20Sopenharmony_ci	if (!sflags && msg_len)
16398c2ecf20Sopenharmony_ci		return 0;
16408c2ecf20Sopenharmony_ci
16418c2ecf20Sopenharmony_ci	if (sctp_style(sk, TCP) && (sflags & (SCTP_EOF | SCTP_ABORT)))
16428c2ecf20Sopenharmony_ci		return -EINVAL;
16438c2ecf20Sopenharmony_ci
16448c2ecf20Sopenharmony_ci	if (((sflags & SCTP_EOF) && msg_len > 0) ||
16458c2ecf20Sopenharmony_ci	    (!(sflags & (SCTP_EOF | SCTP_ABORT)) && msg_len == 0))
16468c2ecf20Sopenharmony_ci		return -EINVAL;
16478c2ecf20Sopenharmony_ci
16488c2ecf20Sopenharmony_ci	if ((sflags & SCTP_ADDR_OVER) && !msg->msg_name)
16498c2ecf20Sopenharmony_ci		return -EINVAL;
16508c2ecf20Sopenharmony_ci
16518c2ecf20Sopenharmony_ci	return 0;
16528c2ecf20Sopenharmony_ci}
16538c2ecf20Sopenharmony_ci
16548c2ecf20Sopenharmony_cistatic int sctp_sendmsg_new_asoc(struct sock *sk, __u16 sflags,
16558c2ecf20Sopenharmony_ci				 struct sctp_cmsgs *cmsgs,
16568c2ecf20Sopenharmony_ci				 union sctp_addr *daddr,
16578c2ecf20Sopenharmony_ci				 struct sctp_transport **tp)
16588c2ecf20Sopenharmony_ci{
16598c2ecf20Sopenharmony_ci	struct sctp_endpoint *ep = sctp_sk(sk)->ep;
16608c2ecf20Sopenharmony_ci	struct sctp_association *asoc;
16618c2ecf20Sopenharmony_ci	struct cmsghdr *cmsg;
16628c2ecf20Sopenharmony_ci	__be32 flowinfo = 0;
16638c2ecf20Sopenharmony_ci	struct sctp_af *af;
16648c2ecf20Sopenharmony_ci	int err;
16658c2ecf20Sopenharmony_ci
16668c2ecf20Sopenharmony_ci	*tp = NULL;
16678c2ecf20Sopenharmony_ci
16688c2ecf20Sopenharmony_ci	if (sflags & (SCTP_EOF | SCTP_ABORT))
16698c2ecf20Sopenharmony_ci		return -EINVAL;
16708c2ecf20Sopenharmony_ci
16718c2ecf20Sopenharmony_ci	if (sctp_style(sk, TCP) && (sctp_sstate(sk, ESTABLISHED) ||
16728c2ecf20Sopenharmony_ci				    sctp_sstate(sk, CLOSING)))
16738c2ecf20Sopenharmony_ci		return -EADDRNOTAVAIL;
16748c2ecf20Sopenharmony_ci
16758c2ecf20Sopenharmony_ci	/* Label connection socket for first association 1-to-many
16768c2ecf20Sopenharmony_ci	 * style for client sequence socket()->sendmsg(). This
16778c2ecf20Sopenharmony_ci	 * needs to be done before sctp_assoc_add_peer() as that will
16788c2ecf20Sopenharmony_ci	 * set up the initial packet that needs to account for any
16798c2ecf20Sopenharmony_ci	 * security ip options (CIPSO/CALIPSO) added to the packet.
16808c2ecf20Sopenharmony_ci	 */
16818c2ecf20Sopenharmony_ci	af = sctp_get_af_specific(daddr->sa.sa_family);
16828c2ecf20Sopenharmony_ci	if (!af)
16838c2ecf20Sopenharmony_ci		return -EINVAL;
16848c2ecf20Sopenharmony_ci	err = security_sctp_bind_connect(sk, SCTP_SENDMSG_CONNECT,
16858c2ecf20Sopenharmony_ci					 (struct sockaddr *)daddr,
16868c2ecf20Sopenharmony_ci					 af->sockaddr_len);
16878c2ecf20Sopenharmony_ci	if (err < 0)
16888c2ecf20Sopenharmony_ci		return err;
16898c2ecf20Sopenharmony_ci
16908c2ecf20Sopenharmony_ci	err = sctp_connect_new_asoc(ep, daddr, cmsgs->init, tp);
16918c2ecf20Sopenharmony_ci	if (err)
16928c2ecf20Sopenharmony_ci		return err;
16938c2ecf20Sopenharmony_ci	asoc = (*tp)->asoc;
16948c2ecf20Sopenharmony_ci
16958c2ecf20Sopenharmony_ci	if (!cmsgs->addrs_msg)
16968c2ecf20Sopenharmony_ci		return 0;
16978c2ecf20Sopenharmony_ci
16988c2ecf20Sopenharmony_ci	if (daddr->sa.sa_family == AF_INET6)
16998c2ecf20Sopenharmony_ci		flowinfo = daddr->v6.sin6_flowinfo;
17008c2ecf20Sopenharmony_ci
17018c2ecf20Sopenharmony_ci	/* sendv addr list parse */
17028c2ecf20Sopenharmony_ci	for_each_cmsghdr(cmsg, cmsgs->addrs_msg) {
17038c2ecf20Sopenharmony_ci		union sctp_addr _daddr;
17048c2ecf20Sopenharmony_ci		int dlen;
17058c2ecf20Sopenharmony_ci
17068c2ecf20Sopenharmony_ci		if (cmsg->cmsg_level != IPPROTO_SCTP ||
17078c2ecf20Sopenharmony_ci		    (cmsg->cmsg_type != SCTP_DSTADDRV4 &&
17088c2ecf20Sopenharmony_ci		     cmsg->cmsg_type != SCTP_DSTADDRV6))
17098c2ecf20Sopenharmony_ci			continue;
17108c2ecf20Sopenharmony_ci
17118c2ecf20Sopenharmony_ci		daddr = &_daddr;
17128c2ecf20Sopenharmony_ci		memset(daddr, 0, sizeof(*daddr));
17138c2ecf20Sopenharmony_ci		dlen = cmsg->cmsg_len - sizeof(struct cmsghdr);
17148c2ecf20Sopenharmony_ci		if (cmsg->cmsg_type == SCTP_DSTADDRV4) {
17158c2ecf20Sopenharmony_ci			if (dlen < sizeof(struct in_addr)) {
17168c2ecf20Sopenharmony_ci				err = -EINVAL;
17178c2ecf20Sopenharmony_ci				goto free;
17188c2ecf20Sopenharmony_ci			}
17198c2ecf20Sopenharmony_ci
17208c2ecf20Sopenharmony_ci			dlen = sizeof(struct in_addr);
17218c2ecf20Sopenharmony_ci			daddr->v4.sin_family = AF_INET;
17228c2ecf20Sopenharmony_ci			daddr->v4.sin_port = htons(asoc->peer.port);
17238c2ecf20Sopenharmony_ci			memcpy(&daddr->v4.sin_addr, CMSG_DATA(cmsg), dlen);
17248c2ecf20Sopenharmony_ci		} else {
17258c2ecf20Sopenharmony_ci			if (dlen < sizeof(struct in6_addr)) {
17268c2ecf20Sopenharmony_ci				err = -EINVAL;
17278c2ecf20Sopenharmony_ci				goto free;
17288c2ecf20Sopenharmony_ci			}
17298c2ecf20Sopenharmony_ci
17308c2ecf20Sopenharmony_ci			dlen = sizeof(struct in6_addr);
17318c2ecf20Sopenharmony_ci			daddr->v6.sin6_flowinfo = flowinfo;
17328c2ecf20Sopenharmony_ci			daddr->v6.sin6_family = AF_INET6;
17338c2ecf20Sopenharmony_ci			daddr->v6.sin6_port = htons(asoc->peer.port);
17348c2ecf20Sopenharmony_ci			memcpy(&daddr->v6.sin6_addr, CMSG_DATA(cmsg), dlen);
17358c2ecf20Sopenharmony_ci		}
17368c2ecf20Sopenharmony_ci
17378c2ecf20Sopenharmony_ci		err = sctp_connect_add_peer(asoc, daddr, sizeof(*daddr));
17388c2ecf20Sopenharmony_ci		if (err)
17398c2ecf20Sopenharmony_ci			goto free;
17408c2ecf20Sopenharmony_ci	}
17418c2ecf20Sopenharmony_ci
17428c2ecf20Sopenharmony_ci	return 0;
17438c2ecf20Sopenharmony_ci
17448c2ecf20Sopenharmony_cifree:
17458c2ecf20Sopenharmony_ci	sctp_association_free(asoc);
17468c2ecf20Sopenharmony_ci	return err;
17478c2ecf20Sopenharmony_ci}
17488c2ecf20Sopenharmony_ci
17498c2ecf20Sopenharmony_cistatic int sctp_sendmsg_check_sflags(struct sctp_association *asoc,
17508c2ecf20Sopenharmony_ci				     __u16 sflags, struct msghdr *msg,
17518c2ecf20Sopenharmony_ci				     size_t msg_len)
17528c2ecf20Sopenharmony_ci{
17538c2ecf20Sopenharmony_ci	struct sock *sk = asoc->base.sk;
17548c2ecf20Sopenharmony_ci	struct net *net = sock_net(sk);
17558c2ecf20Sopenharmony_ci
17568c2ecf20Sopenharmony_ci	if (sctp_state(asoc, CLOSED) && sctp_style(sk, TCP))
17578c2ecf20Sopenharmony_ci		return -EPIPE;
17588c2ecf20Sopenharmony_ci
17598c2ecf20Sopenharmony_ci	if ((sflags & SCTP_SENDALL) && sctp_style(sk, UDP) &&
17608c2ecf20Sopenharmony_ci	    !sctp_state(asoc, ESTABLISHED))
17618c2ecf20Sopenharmony_ci		return 0;
17628c2ecf20Sopenharmony_ci
17638c2ecf20Sopenharmony_ci	if (sflags & SCTP_EOF) {
17648c2ecf20Sopenharmony_ci		pr_debug("%s: shutting down association:%p\n", __func__, asoc);
17658c2ecf20Sopenharmony_ci		sctp_primitive_SHUTDOWN(net, asoc, NULL);
17668c2ecf20Sopenharmony_ci
17678c2ecf20Sopenharmony_ci		return 0;
17688c2ecf20Sopenharmony_ci	}
17698c2ecf20Sopenharmony_ci
17708c2ecf20Sopenharmony_ci	if (sflags & SCTP_ABORT) {
17718c2ecf20Sopenharmony_ci		struct sctp_chunk *chunk;
17728c2ecf20Sopenharmony_ci
17738c2ecf20Sopenharmony_ci		chunk = sctp_make_abort_user(asoc, msg, msg_len);
17748c2ecf20Sopenharmony_ci		if (!chunk)
17758c2ecf20Sopenharmony_ci			return -ENOMEM;
17768c2ecf20Sopenharmony_ci
17778c2ecf20Sopenharmony_ci		pr_debug("%s: aborting association:%p\n", __func__, asoc);
17788c2ecf20Sopenharmony_ci		sctp_primitive_ABORT(net, asoc, chunk);
17798c2ecf20Sopenharmony_ci		iov_iter_revert(&msg->msg_iter, msg_len);
17808c2ecf20Sopenharmony_ci
17818c2ecf20Sopenharmony_ci		return 0;
17828c2ecf20Sopenharmony_ci	}
17838c2ecf20Sopenharmony_ci
17848c2ecf20Sopenharmony_ci	return 1;
17858c2ecf20Sopenharmony_ci}
17868c2ecf20Sopenharmony_ci
17878c2ecf20Sopenharmony_cistatic int sctp_sendmsg_to_asoc(struct sctp_association *asoc,
17888c2ecf20Sopenharmony_ci				struct msghdr *msg, size_t msg_len,
17898c2ecf20Sopenharmony_ci				struct sctp_transport *transport,
17908c2ecf20Sopenharmony_ci				struct sctp_sndrcvinfo *sinfo)
17918c2ecf20Sopenharmony_ci{
17928c2ecf20Sopenharmony_ci	struct sock *sk = asoc->base.sk;
17938c2ecf20Sopenharmony_ci	struct sctp_sock *sp = sctp_sk(sk);
17948c2ecf20Sopenharmony_ci	struct net *net = sock_net(sk);
17958c2ecf20Sopenharmony_ci	struct sctp_datamsg *datamsg;
17968c2ecf20Sopenharmony_ci	bool wait_connect = false;
17978c2ecf20Sopenharmony_ci	struct sctp_chunk *chunk;
17988c2ecf20Sopenharmony_ci	long timeo;
17998c2ecf20Sopenharmony_ci	int err;
18008c2ecf20Sopenharmony_ci
18018c2ecf20Sopenharmony_ci	if (sinfo->sinfo_stream >= asoc->stream.outcnt) {
18028c2ecf20Sopenharmony_ci		err = -EINVAL;
18038c2ecf20Sopenharmony_ci		goto err;
18048c2ecf20Sopenharmony_ci	}
18058c2ecf20Sopenharmony_ci
18068c2ecf20Sopenharmony_ci	if (unlikely(!SCTP_SO(&asoc->stream, sinfo->sinfo_stream)->ext)) {
18078c2ecf20Sopenharmony_ci		err = sctp_stream_init_ext(&asoc->stream, sinfo->sinfo_stream);
18088c2ecf20Sopenharmony_ci		if (err)
18098c2ecf20Sopenharmony_ci			goto err;
18108c2ecf20Sopenharmony_ci	}
18118c2ecf20Sopenharmony_ci
18128c2ecf20Sopenharmony_ci	if (sp->disable_fragments && msg_len > asoc->frag_point) {
18138c2ecf20Sopenharmony_ci		err = -EMSGSIZE;
18148c2ecf20Sopenharmony_ci		goto err;
18158c2ecf20Sopenharmony_ci	}
18168c2ecf20Sopenharmony_ci
18178c2ecf20Sopenharmony_ci	if (asoc->pmtu_pending) {
18188c2ecf20Sopenharmony_ci		if (sp->param_flags & SPP_PMTUD_ENABLE)
18198c2ecf20Sopenharmony_ci			sctp_assoc_sync_pmtu(asoc);
18208c2ecf20Sopenharmony_ci		asoc->pmtu_pending = 0;
18218c2ecf20Sopenharmony_ci	}
18228c2ecf20Sopenharmony_ci
18238c2ecf20Sopenharmony_ci	if (sctp_wspace(asoc) < (int)msg_len)
18248c2ecf20Sopenharmony_ci		sctp_prsctp_prune(asoc, sinfo, msg_len - sctp_wspace(asoc));
18258c2ecf20Sopenharmony_ci
18268c2ecf20Sopenharmony_ci	if (sk_under_memory_pressure(sk))
18278c2ecf20Sopenharmony_ci		sk_mem_reclaim(sk);
18288c2ecf20Sopenharmony_ci
18298c2ecf20Sopenharmony_ci	if (sctp_wspace(asoc) <= 0 || !sk_wmem_schedule(sk, msg_len)) {
18308c2ecf20Sopenharmony_ci		timeo = sock_sndtimeo(sk, msg->msg_flags & MSG_DONTWAIT);
18318c2ecf20Sopenharmony_ci		err = sctp_wait_for_sndbuf(asoc, &timeo, msg_len);
18328c2ecf20Sopenharmony_ci		if (err)
18338c2ecf20Sopenharmony_ci			goto err;
18348c2ecf20Sopenharmony_ci		if (unlikely(sinfo->sinfo_stream >= asoc->stream.outcnt)) {
18358c2ecf20Sopenharmony_ci			err = -EINVAL;
18368c2ecf20Sopenharmony_ci			goto err;
18378c2ecf20Sopenharmony_ci		}
18388c2ecf20Sopenharmony_ci	}
18398c2ecf20Sopenharmony_ci
18408c2ecf20Sopenharmony_ci	if (sctp_state(asoc, CLOSED)) {
18418c2ecf20Sopenharmony_ci		err = sctp_primitive_ASSOCIATE(net, asoc, NULL);
18428c2ecf20Sopenharmony_ci		if (err)
18438c2ecf20Sopenharmony_ci			goto err;
18448c2ecf20Sopenharmony_ci
18458c2ecf20Sopenharmony_ci		if (asoc->ep->intl_enable) {
18468c2ecf20Sopenharmony_ci			timeo = sock_sndtimeo(sk, 0);
18478c2ecf20Sopenharmony_ci			err = sctp_wait_for_connect(asoc, &timeo);
18488c2ecf20Sopenharmony_ci			if (err) {
18498c2ecf20Sopenharmony_ci				err = -ESRCH;
18508c2ecf20Sopenharmony_ci				goto err;
18518c2ecf20Sopenharmony_ci			}
18528c2ecf20Sopenharmony_ci		} else {
18538c2ecf20Sopenharmony_ci			wait_connect = true;
18548c2ecf20Sopenharmony_ci		}
18558c2ecf20Sopenharmony_ci
18568c2ecf20Sopenharmony_ci		pr_debug("%s: we associated primitively\n", __func__);
18578c2ecf20Sopenharmony_ci	}
18588c2ecf20Sopenharmony_ci
18598c2ecf20Sopenharmony_ci	datamsg = sctp_datamsg_from_user(asoc, sinfo, &msg->msg_iter);
18608c2ecf20Sopenharmony_ci	if (IS_ERR(datamsg)) {
18618c2ecf20Sopenharmony_ci		err = PTR_ERR(datamsg);
18628c2ecf20Sopenharmony_ci		goto err;
18638c2ecf20Sopenharmony_ci	}
18648c2ecf20Sopenharmony_ci
18658c2ecf20Sopenharmony_ci	asoc->force_delay = !!(msg->msg_flags & MSG_MORE);
18668c2ecf20Sopenharmony_ci
18678c2ecf20Sopenharmony_ci	list_for_each_entry(chunk, &datamsg->chunks, frag_list) {
18688c2ecf20Sopenharmony_ci		sctp_chunk_hold(chunk);
18698c2ecf20Sopenharmony_ci		sctp_set_owner_w(chunk);
18708c2ecf20Sopenharmony_ci		chunk->transport = transport;
18718c2ecf20Sopenharmony_ci	}
18728c2ecf20Sopenharmony_ci
18738c2ecf20Sopenharmony_ci	err = sctp_primitive_SEND(net, asoc, datamsg);
18748c2ecf20Sopenharmony_ci	if (err) {
18758c2ecf20Sopenharmony_ci		sctp_datamsg_free(datamsg);
18768c2ecf20Sopenharmony_ci		goto err;
18778c2ecf20Sopenharmony_ci	}
18788c2ecf20Sopenharmony_ci
18798c2ecf20Sopenharmony_ci	pr_debug("%s: we sent primitively\n", __func__);
18808c2ecf20Sopenharmony_ci
18818c2ecf20Sopenharmony_ci	sctp_datamsg_put(datamsg);
18828c2ecf20Sopenharmony_ci
18838c2ecf20Sopenharmony_ci	if (unlikely(wait_connect)) {
18848c2ecf20Sopenharmony_ci		timeo = sock_sndtimeo(sk, msg->msg_flags & MSG_DONTWAIT);
18858c2ecf20Sopenharmony_ci		sctp_wait_for_connect(asoc, &timeo);
18868c2ecf20Sopenharmony_ci	}
18878c2ecf20Sopenharmony_ci
18888c2ecf20Sopenharmony_ci	err = msg_len;
18898c2ecf20Sopenharmony_ci
18908c2ecf20Sopenharmony_cierr:
18918c2ecf20Sopenharmony_ci	return err;
18928c2ecf20Sopenharmony_ci}
18938c2ecf20Sopenharmony_ci
18948c2ecf20Sopenharmony_cistatic union sctp_addr *sctp_sendmsg_get_daddr(struct sock *sk,
18958c2ecf20Sopenharmony_ci					       const struct msghdr *msg,
18968c2ecf20Sopenharmony_ci					       struct sctp_cmsgs *cmsgs)
18978c2ecf20Sopenharmony_ci{
18988c2ecf20Sopenharmony_ci	union sctp_addr *daddr = NULL;
18998c2ecf20Sopenharmony_ci	int err;
19008c2ecf20Sopenharmony_ci
19018c2ecf20Sopenharmony_ci	if (!sctp_style(sk, UDP_HIGH_BANDWIDTH) && msg->msg_name) {
19028c2ecf20Sopenharmony_ci		int len = msg->msg_namelen;
19038c2ecf20Sopenharmony_ci
19048c2ecf20Sopenharmony_ci		if (len > sizeof(*daddr))
19058c2ecf20Sopenharmony_ci			len = sizeof(*daddr);
19068c2ecf20Sopenharmony_ci
19078c2ecf20Sopenharmony_ci		daddr = (union sctp_addr *)msg->msg_name;
19088c2ecf20Sopenharmony_ci
19098c2ecf20Sopenharmony_ci		err = sctp_verify_addr(sk, daddr, len);
19108c2ecf20Sopenharmony_ci		if (err)
19118c2ecf20Sopenharmony_ci			return ERR_PTR(err);
19128c2ecf20Sopenharmony_ci	}
19138c2ecf20Sopenharmony_ci
19148c2ecf20Sopenharmony_ci	return daddr;
19158c2ecf20Sopenharmony_ci}
19168c2ecf20Sopenharmony_ci
19178c2ecf20Sopenharmony_cistatic void sctp_sendmsg_update_sinfo(struct sctp_association *asoc,
19188c2ecf20Sopenharmony_ci				      struct sctp_sndrcvinfo *sinfo,
19198c2ecf20Sopenharmony_ci				      struct sctp_cmsgs *cmsgs)
19208c2ecf20Sopenharmony_ci{
19218c2ecf20Sopenharmony_ci	if (!cmsgs->srinfo && !cmsgs->sinfo) {
19228c2ecf20Sopenharmony_ci		sinfo->sinfo_stream = asoc->default_stream;
19238c2ecf20Sopenharmony_ci		sinfo->sinfo_ppid = asoc->default_ppid;
19248c2ecf20Sopenharmony_ci		sinfo->sinfo_context = asoc->default_context;
19258c2ecf20Sopenharmony_ci		sinfo->sinfo_assoc_id = sctp_assoc2id(asoc);
19268c2ecf20Sopenharmony_ci
19278c2ecf20Sopenharmony_ci		if (!cmsgs->prinfo)
19288c2ecf20Sopenharmony_ci			sinfo->sinfo_flags = asoc->default_flags;
19298c2ecf20Sopenharmony_ci	}
19308c2ecf20Sopenharmony_ci
19318c2ecf20Sopenharmony_ci	if (!cmsgs->srinfo && !cmsgs->prinfo)
19328c2ecf20Sopenharmony_ci		sinfo->sinfo_timetolive = asoc->default_timetolive;
19338c2ecf20Sopenharmony_ci
19348c2ecf20Sopenharmony_ci	if (cmsgs->authinfo) {
19358c2ecf20Sopenharmony_ci		/* Reuse sinfo_tsn to indicate that authinfo was set and
19368c2ecf20Sopenharmony_ci		 * sinfo_ssn to save the keyid on tx path.
19378c2ecf20Sopenharmony_ci		 */
19388c2ecf20Sopenharmony_ci		sinfo->sinfo_tsn = 1;
19398c2ecf20Sopenharmony_ci		sinfo->sinfo_ssn = cmsgs->authinfo->auth_keynumber;
19408c2ecf20Sopenharmony_ci	}
19418c2ecf20Sopenharmony_ci}
19428c2ecf20Sopenharmony_ci
19438c2ecf20Sopenharmony_cistatic int sctp_sendmsg(struct sock *sk, struct msghdr *msg, size_t msg_len)
19448c2ecf20Sopenharmony_ci{
19458c2ecf20Sopenharmony_ci	struct sctp_endpoint *ep = sctp_sk(sk)->ep;
19468c2ecf20Sopenharmony_ci	struct sctp_transport *transport = NULL;
19478c2ecf20Sopenharmony_ci	struct sctp_sndrcvinfo _sinfo, *sinfo;
19488c2ecf20Sopenharmony_ci	struct sctp_association *asoc, *tmp;
19498c2ecf20Sopenharmony_ci	struct sctp_cmsgs cmsgs;
19508c2ecf20Sopenharmony_ci	union sctp_addr *daddr;
19518c2ecf20Sopenharmony_ci	bool new = false;
19528c2ecf20Sopenharmony_ci	__u16 sflags;
19538c2ecf20Sopenharmony_ci	int err;
19548c2ecf20Sopenharmony_ci
19558c2ecf20Sopenharmony_ci	/* Parse and get snd_info */
19568c2ecf20Sopenharmony_ci	err = sctp_sendmsg_parse(sk, &cmsgs, &_sinfo, msg, msg_len);
19578c2ecf20Sopenharmony_ci	if (err)
19588c2ecf20Sopenharmony_ci		goto out;
19598c2ecf20Sopenharmony_ci
19608c2ecf20Sopenharmony_ci	sinfo  = &_sinfo;
19618c2ecf20Sopenharmony_ci	sflags = sinfo->sinfo_flags;
19628c2ecf20Sopenharmony_ci
19638c2ecf20Sopenharmony_ci	/* Get daddr from msg */
19648c2ecf20Sopenharmony_ci	daddr = sctp_sendmsg_get_daddr(sk, msg, &cmsgs);
19658c2ecf20Sopenharmony_ci	if (IS_ERR(daddr)) {
19668c2ecf20Sopenharmony_ci		err = PTR_ERR(daddr);
19678c2ecf20Sopenharmony_ci		goto out;
19688c2ecf20Sopenharmony_ci	}
19698c2ecf20Sopenharmony_ci
19708c2ecf20Sopenharmony_ci	lock_sock(sk);
19718c2ecf20Sopenharmony_ci
19728c2ecf20Sopenharmony_ci	/* SCTP_SENDALL process */
19738c2ecf20Sopenharmony_ci	if ((sflags & SCTP_SENDALL) && sctp_style(sk, UDP)) {
19748c2ecf20Sopenharmony_ci		list_for_each_entry_safe(asoc, tmp, &ep->asocs, asocs) {
19758c2ecf20Sopenharmony_ci			err = sctp_sendmsg_check_sflags(asoc, sflags, msg,
19768c2ecf20Sopenharmony_ci							msg_len);
19778c2ecf20Sopenharmony_ci			if (err == 0)
19788c2ecf20Sopenharmony_ci				continue;
19798c2ecf20Sopenharmony_ci			if (err < 0)
19808c2ecf20Sopenharmony_ci				goto out_unlock;
19818c2ecf20Sopenharmony_ci
19828c2ecf20Sopenharmony_ci			sctp_sendmsg_update_sinfo(asoc, sinfo, &cmsgs);
19838c2ecf20Sopenharmony_ci
19848c2ecf20Sopenharmony_ci			err = sctp_sendmsg_to_asoc(asoc, msg, msg_len,
19858c2ecf20Sopenharmony_ci						   NULL, sinfo);
19868c2ecf20Sopenharmony_ci			if (err < 0)
19878c2ecf20Sopenharmony_ci				goto out_unlock;
19888c2ecf20Sopenharmony_ci
19898c2ecf20Sopenharmony_ci			iov_iter_revert(&msg->msg_iter, err);
19908c2ecf20Sopenharmony_ci		}
19918c2ecf20Sopenharmony_ci
19928c2ecf20Sopenharmony_ci		goto out_unlock;
19938c2ecf20Sopenharmony_ci	}
19948c2ecf20Sopenharmony_ci
19958c2ecf20Sopenharmony_ci	/* Get and check or create asoc */
19968c2ecf20Sopenharmony_ci	if (daddr) {
19978c2ecf20Sopenharmony_ci		asoc = sctp_endpoint_lookup_assoc(ep, daddr, &transport);
19988c2ecf20Sopenharmony_ci		if (asoc) {
19998c2ecf20Sopenharmony_ci			err = sctp_sendmsg_check_sflags(asoc, sflags, msg,
20008c2ecf20Sopenharmony_ci							msg_len);
20018c2ecf20Sopenharmony_ci			if (err <= 0)
20028c2ecf20Sopenharmony_ci				goto out_unlock;
20038c2ecf20Sopenharmony_ci		} else {
20048c2ecf20Sopenharmony_ci			err = sctp_sendmsg_new_asoc(sk, sflags, &cmsgs, daddr,
20058c2ecf20Sopenharmony_ci						    &transport);
20068c2ecf20Sopenharmony_ci			if (err)
20078c2ecf20Sopenharmony_ci				goto out_unlock;
20088c2ecf20Sopenharmony_ci
20098c2ecf20Sopenharmony_ci			asoc = transport->asoc;
20108c2ecf20Sopenharmony_ci			new = true;
20118c2ecf20Sopenharmony_ci		}
20128c2ecf20Sopenharmony_ci
20138c2ecf20Sopenharmony_ci		if (!sctp_style(sk, TCP) && !(sflags & SCTP_ADDR_OVER))
20148c2ecf20Sopenharmony_ci			transport = NULL;
20158c2ecf20Sopenharmony_ci	} else {
20168c2ecf20Sopenharmony_ci		asoc = sctp_id2assoc(sk, sinfo->sinfo_assoc_id);
20178c2ecf20Sopenharmony_ci		if (!asoc) {
20188c2ecf20Sopenharmony_ci			err = -EPIPE;
20198c2ecf20Sopenharmony_ci			goto out_unlock;
20208c2ecf20Sopenharmony_ci		}
20218c2ecf20Sopenharmony_ci
20228c2ecf20Sopenharmony_ci		err = sctp_sendmsg_check_sflags(asoc, sflags, msg, msg_len);
20238c2ecf20Sopenharmony_ci		if (err <= 0)
20248c2ecf20Sopenharmony_ci			goto out_unlock;
20258c2ecf20Sopenharmony_ci	}
20268c2ecf20Sopenharmony_ci
20278c2ecf20Sopenharmony_ci	/* Update snd_info with the asoc */
20288c2ecf20Sopenharmony_ci	sctp_sendmsg_update_sinfo(asoc, sinfo, &cmsgs);
20298c2ecf20Sopenharmony_ci
20308c2ecf20Sopenharmony_ci	/* Send msg to the asoc */
20318c2ecf20Sopenharmony_ci	err = sctp_sendmsg_to_asoc(asoc, msg, msg_len, transport, sinfo);
20328c2ecf20Sopenharmony_ci	if (err < 0 && err != -ESRCH && new)
20338c2ecf20Sopenharmony_ci		sctp_association_free(asoc);
20348c2ecf20Sopenharmony_ci
20358c2ecf20Sopenharmony_ciout_unlock:
20368c2ecf20Sopenharmony_ci	release_sock(sk);
20378c2ecf20Sopenharmony_ciout:
20388c2ecf20Sopenharmony_ci	return sctp_error(sk, msg->msg_flags, err);
20398c2ecf20Sopenharmony_ci}
20408c2ecf20Sopenharmony_ci
20418c2ecf20Sopenharmony_ci/* This is an extended version of skb_pull() that removes the data from the
20428c2ecf20Sopenharmony_ci * start of a skb even when data is spread across the list of skb's in the
20438c2ecf20Sopenharmony_ci * frag_list. len specifies the total amount of data that needs to be removed.
20448c2ecf20Sopenharmony_ci * when 'len' bytes could be removed from the skb, it returns 0.
20458c2ecf20Sopenharmony_ci * If 'len' exceeds the total skb length,  it returns the no. of bytes that
20468c2ecf20Sopenharmony_ci * could not be removed.
20478c2ecf20Sopenharmony_ci */
20488c2ecf20Sopenharmony_cistatic int sctp_skb_pull(struct sk_buff *skb, int len)
20498c2ecf20Sopenharmony_ci{
20508c2ecf20Sopenharmony_ci	struct sk_buff *list;
20518c2ecf20Sopenharmony_ci	int skb_len = skb_headlen(skb);
20528c2ecf20Sopenharmony_ci	int rlen;
20538c2ecf20Sopenharmony_ci
20548c2ecf20Sopenharmony_ci	if (len <= skb_len) {
20558c2ecf20Sopenharmony_ci		__skb_pull(skb, len);
20568c2ecf20Sopenharmony_ci		return 0;
20578c2ecf20Sopenharmony_ci	}
20588c2ecf20Sopenharmony_ci	len -= skb_len;
20598c2ecf20Sopenharmony_ci	__skb_pull(skb, skb_len);
20608c2ecf20Sopenharmony_ci
20618c2ecf20Sopenharmony_ci	skb_walk_frags(skb, list) {
20628c2ecf20Sopenharmony_ci		rlen = sctp_skb_pull(list, len);
20638c2ecf20Sopenharmony_ci		skb->len -= (len-rlen);
20648c2ecf20Sopenharmony_ci		skb->data_len -= (len-rlen);
20658c2ecf20Sopenharmony_ci
20668c2ecf20Sopenharmony_ci		if (!rlen)
20678c2ecf20Sopenharmony_ci			return 0;
20688c2ecf20Sopenharmony_ci
20698c2ecf20Sopenharmony_ci		len = rlen;
20708c2ecf20Sopenharmony_ci	}
20718c2ecf20Sopenharmony_ci
20728c2ecf20Sopenharmony_ci	return len;
20738c2ecf20Sopenharmony_ci}
20748c2ecf20Sopenharmony_ci
20758c2ecf20Sopenharmony_ci/* API 3.1.3  recvmsg() - UDP Style Syntax
20768c2ecf20Sopenharmony_ci *
20778c2ecf20Sopenharmony_ci *  ssize_t recvmsg(int socket, struct msghdr *message,
20788c2ecf20Sopenharmony_ci *                    int flags);
20798c2ecf20Sopenharmony_ci *
20808c2ecf20Sopenharmony_ci *  socket  - the socket descriptor of the endpoint.
20818c2ecf20Sopenharmony_ci *  message - pointer to the msghdr structure which contains a single
20828c2ecf20Sopenharmony_ci *            user message and possibly some ancillary data.
20838c2ecf20Sopenharmony_ci *
20848c2ecf20Sopenharmony_ci *            See Section 5 for complete description of the data
20858c2ecf20Sopenharmony_ci *            structures.
20868c2ecf20Sopenharmony_ci *
20878c2ecf20Sopenharmony_ci *  flags   - flags sent or received with the user message, see Section
20888c2ecf20Sopenharmony_ci *            5 for complete description of the flags.
20898c2ecf20Sopenharmony_ci */
20908c2ecf20Sopenharmony_cistatic int sctp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
20918c2ecf20Sopenharmony_ci			int noblock, int flags, int *addr_len)
20928c2ecf20Sopenharmony_ci{
20938c2ecf20Sopenharmony_ci	struct sctp_ulpevent *event = NULL;
20948c2ecf20Sopenharmony_ci	struct sctp_sock *sp = sctp_sk(sk);
20958c2ecf20Sopenharmony_ci	struct sk_buff *skb, *head_skb;
20968c2ecf20Sopenharmony_ci	int copied;
20978c2ecf20Sopenharmony_ci	int err = 0;
20988c2ecf20Sopenharmony_ci	int skb_len;
20998c2ecf20Sopenharmony_ci
21008c2ecf20Sopenharmony_ci	pr_debug("%s: sk:%p, msghdr:%p, len:%zd, noblock:%d, flags:0x%x, "
21018c2ecf20Sopenharmony_ci		 "addr_len:%p)\n", __func__, sk, msg, len, noblock, flags,
21028c2ecf20Sopenharmony_ci		 addr_len);
21038c2ecf20Sopenharmony_ci
21048c2ecf20Sopenharmony_ci	lock_sock(sk);
21058c2ecf20Sopenharmony_ci
21068c2ecf20Sopenharmony_ci	if (sctp_style(sk, TCP) && !sctp_sstate(sk, ESTABLISHED) &&
21078c2ecf20Sopenharmony_ci	    !sctp_sstate(sk, CLOSING) && !sctp_sstate(sk, CLOSED)) {
21088c2ecf20Sopenharmony_ci		err = -ENOTCONN;
21098c2ecf20Sopenharmony_ci		goto out;
21108c2ecf20Sopenharmony_ci	}
21118c2ecf20Sopenharmony_ci
21128c2ecf20Sopenharmony_ci	skb = sctp_skb_recv_datagram(sk, flags, noblock, &err);
21138c2ecf20Sopenharmony_ci	if (!skb)
21148c2ecf20Sopenharmony_ci		goto out;
21158c2ecf20Sopenharmony_ci
21168c2ecf20Sopenharmony_ci	/* Get the total length of the skb including any skb's in the
21178c2ecf20Sopenharmony_ci	 * frag_list.
21188c2ecf20Sopenharmony_ci	 */
21198c2ecf20Sopenharmony_ci	skb_len = skb->len;
21208c2ecf20Sopenharmony_ci
21218c2ecf20Sopenharmony_ci	copied = skb_len;
21228c2ecf20Sopenharmony_ci	if (copied > len)
21238c2ecf20Sopenharmony_ci		copied = len;
21248c2ecf20Sopenharmony_ci
21258c2ecf20Sopenharmony_ci	err = skb_copy_datagram_msg(skb, 0, msg, copied);
21268c2ecf20Sopenharmony_ci
21278c2ecf20Sopenharmony_ci	event = sctp_skb2event(skb);
21288c2ecf20Sopenharmony_ci
21298c2ecf20Sopenharmony_ci	if (err)
21308c2ecf20Sopenharmony_ci		goto out_free;
21318c2ecf20Sopenharmony_ci
21328c2ecf20Sopenharmony_ci	if (event->chunk && event->chunk->head_skb)
21338c2ecf20Sopenharmony_ci		head_skb = event->chunk->head_skb;
21348c2ecf20Sopenharmony_ci	else
21358c2ecf20Sopenharmony_ci		head_skb = skb;
21368c2ecf20Sopenharmony_ci	sock_recv_ts_and_drops(msg, sk, head_skb);
21378c2ecf20Sopenharmony_ci	if (sctp_ulpevent_is_notification(event)) {
21388c2ecf20Sopenharmony_ci		msg->msg_flags |= MSG_NOTIFICATION;
21398c2ecf20Sopenharmony_ci		sp->pf->event_msgname(event, msg->msg_name, addr_len);
21408c2ecf20Sopenharmony_ci	} else {
21418c2ecf20Sopenharmony_ci		sp->pf->skb_msgname(head_skb, msg->msg_name, addr_len);
21428c2ecf20Sopenharmony_ci	}
21438c2ecf20Sopenharmony_ci
21448c2ecf20Sopenharmony_ci	/* Check if we allow SCTP_NXTINFO. */
21458c2ecf20Sopenharmony_ci	if (sp->recvnxtinfo)
21468c2ecf20Sopenharmony_ci		sctp_ulpevent_read_nxtinfo(event, msg, sk);
21478c2ecf20Sopenharmony_ci	/* Check if we allow SCTP_RCVINFO. */
21488c2ecf20Sopenharmony_ci	if (sp->recvrcvinfo)
21498c2ecf20Sopenharmony_ci		sctp_ulpevent_read_rcvinfo(event, msg);
21508c2ecf20Sopenharmony_ci	/* Check if we allow SCTP_SNDRCVINFO. */
21518c2ecf20Sopenharmony_ci	if (sctp_ulpevent_type_enabled(sp->subscribe, SCTP_DATA_IO_EVENT))
21528c2ecf20Sopenharmony_ci		sctp_ulpevent_read_sndrcvinfo(event, msg);
21538c2ecf20Sopenharmony_ci
21548c2ecf20Sopenharmony_ci	err = copied;
21558c2ecf20Sopenharmony_ci
21568c2ecf20Sopenharmony_ci	/* If skb's length exceeds the user's buffer, update the skb and
21578c2ecf20Sopenharmony_ci	 * push it back to the receive_queue so that the next call to
21588c2ecf20Sopenharmony_ci	 * recvmsg() will return the remaining data. Don't set MSG_EOR.
21598c2ecf20Sopenharmony_ci	 */
21608c2ecf20Sopenharmony_ci	if (skb_len > copied) {
21618c2ecf20Sopenharmony_ci		msg->msg_flags &= ~MSG_EOR;
21628c2ecf20Sopenharmony_ci		if (flags & MSG_PEEK)
21638c2ecf20Sopenharmony_ci			goto out_free;
21648c2ecf20Sopenharmony_ci		sctp_skb_pull(skb, copied);
21658c2ecf20Sopenharmony_ci		skb_queue_head(&sk->sk_receive_queue, skb);
21668c2ecf20Sopenharmony_ci
21678c2ecf20Sopenharmony_ci		/* When only partial message is copied to the user, increase
21688c2ecf20Sopenharmony_ci		 * rwnd by that amount. If all the data in the skb is read,
21698c2ecf20Sopenharmony_ci		 * rwnd is updated when the event is freed.
21708c2ecf20Sopenharmony_ci		 */
21718c2ecf20Sopenharmony_ci		if (!sctp_ulpevent_is_notification(event))
21728c2ecf20Sopenharmony_ci			sctp_assoc_rwnd_increase(event->asoc, copied);
21738c2ecf20Sopenharmony_ci		goto out;
21748c2ecf20Sopenharmony_ci	} else if ((event->msg_flags & MSG_NOTIFICATION) ||
21758c2ecf20Sopenharmony_ci		   (event->msg_flags & MSG_EOR))
21768c2ecf20Sopenharmony_ci		msg->msg_flags |= MSG_EOR;
21778c2ecf20Sopenharmony_ci	else
21788c2ecf20Sopenharmony_ci		msg->msg_flags &= ~MSG_EOR;
21798c2ecf20Sopenharmony_ci
21808c2ecf20Sopenharmony_ciout_free:
21818c2ecf20Sopenharmony_ci	if (flags & MSG_PEEK) {
21828c2ecf20Sopenharmony_ci		/* Release the skb reference acquired after peeking the skb in
21838c2ecf20Sopenharmony_ci		 * sctp_skb_recv_datagram().
21848c2ecf20Sopenharmony_ci		 */
21858c2ecf20Sopenharmony_ci		kfree_skb(skb);
21868c2ecf20Sopenharmony_ci	} else {
21878c2ecf20Sopenharmony_ci		/* Free the event which includes releasing the reference to
21888c2ecf20Sopenharmony_ci		 * the owner of the skb, freeing the skb and updating the
21898c2ecf20Sopenharmony_ci		 * rwnd.
21908c2ecf20Sopenharmony_ci		 */
21918c2ecf20Sopenharmony_ci		sctp_ulpevent_free(event);
21928c2ecf20Sopenharmony_ci	}
21938c2ecf20Sopenharmony_ciout:
21948c2ecf20Sopenharmony_ci	release_sock(sk);
21958c2ecf20Sopenharmony_ci	return err;
21968c2ecf20Sopenharmony_ci}
21978c2ecf20Sopenharmony_ci
21988c2ecf20Sopenharmony_ci/* 7.1.12 Enable/Disable message fragmentation (SCTP_DISABLE_FRAGMENTS)
21998c2ecf20Sopenharmony_ci *
22008c2ecf20Sopenharmony_ci * This option is a on/off flag.  If enabled no SCTP message
22018c2ecf20Sopenharmony_ci * fragmentation will be performed.  Instead if a message being sent
22028c2ecf20Sopenharmony_ci * exceeds the current PMTU size, the message will NOT be sent and
22038c2ecf20Sopenharmony_ci * instead a error will be indicated to the user.
22048c2ecf20Sopenharmony_ci */
22058c2ecf20Sopenharmony_cistatic int sctp_setsockopt_disable_fragments(struct sock *sk, int *val,
22068c2ecf20Sopenharmony_ci					     unsigned int optlen)
22078c2ecf20Sopenharmony_ci{
22088c2ecf20Sopenharmony_ci	if (optlen < sizeof(int))
22098c2ecf20Sopenharmony_ci		return -EINVAL;
22108c2ecf20Sopenharmony_ci	sctp_sk(sk)->disable_fragments = (*val == 0) ? 0 : 1;
22118c2ecf20Sopenharmony_ci	return 0;
22128c2ecf20Sopenharmony_ci}
22138c2ecf20Sopenharmony_ci
22148c2ecf20Sopenharmony_cistatic int sctp_setsockopt_events(struct sock *sk, __u8 *sn_type,
22158c2ecf20Sopenharmony_ci				  unsigned int optlen)
22168c2ecf20Sopenharmony_ci{
22178c2ecf20Sopenharmony_ci	struct sctp_sock *sp = sctp_sk(sk);
22188c2ecf20Sopenharmony_ci	struct sctp_association *asoc;
22198c2ecf20Sopenharmony_ci	int i;
22208c2ecf20Sopenharmony_ci
22218c2ecf20Sopenharmony_ci	if (optlen > sizeof(struct sctp_event_subscribe))
22228c2ecf20Sopenharmony_ci		return -EINVAL;
22238c2ecf20Sopenharmony_ci
22248c2ecf20Sopenharmony_ci	for (i = 0; i < optlen; i++)
22258c2ecf20Sopenharmony_ci		sctp_ulpevent_type_set(&sp->subscribe, SCTP_SN_TYPE_BASE + i,
22268c2ecf20Sopenharmony_ci				       sn_type[i]);
22278c2ecf20Sopenharmony_ci
22288c2ecf20Sopenharmony_ci	list_for_each_entry(asoc, &sp->ep->asocs, asocs)
22298c2ecf20Sopenharmony_ci		asoc->subscribe = sctp_sk(sk)->subscribe;
22308c2ecf20Sopenharmony_ci
22318c2ecf20Sopenharmony_ci	/* At the time when a user app subscribes to SCTP_SENDER_DRY_EVENT,
22328c2ecf20Sopenharmony_ci	 * if there is no data to be sent or retransmit, the stack will
22338c2ecf20Sopenharmony_ci	 * immediately send up this notification.
22348c2ecf20Sopenharmony_ci	 */
22358c2ecf20Sopenharmony_ci	if (sctp_ulpevent_type_enabled(sp->subscribe, SCTP_SENDER_DRY_EVENT)) {
22368c2ecf20Sopenharmony_ci		struct sctp_ulpevent *event;
22378c2ecf20Sopenharmony_ci
22388c2ecf20Sopenharmony_ci		asoc = sctp_id2assoc(sk, 0);
22398c2ecf20Sopenharmony_ci		if (asoc && sctp_outq_is_empty(&asoc->outqueue)) {
22408c2ecf20Sopenharmony_ci			event = sctp_ulpevent_make_sender_dry_event(asoc,
22418c2ecf20Sopenharmony_ci					GFP_USER | __GFP_NOWARN);
22428c2ecf20Sopenharmony_ci			if (!event)
22438c2ecf20Sopenharmony_ci				return -ENOMEM;
22448c2ecf20Sopenharmony_ci
22458c2ecf20Sopenharmony_ci			asoc->stream.si->enqueue_event(&asoc->ulpq, event);
22468c2ecf20Sopenharmony_ci		}
22478c2ecf20Sopenharmony_ci	}
22488c2ecf20Sopenharmony_ci
22498c2ecf20Sopenharmony_ci	return 0;
22508c2ecf20Sopenharmony_ci}
22518c2ecf20Sopenharmony_ci
22528c2ecf20Sopenharmony_ci/* 7.1.8 Automatic Close of associations (SCTP_AUTOCLOSE)
22538c2ecf20Sopenharmony_ci *
22548c2ecf20Sopenharmony_ci * This socket option is applicable to the UDP-style socket only.  When
22558c2ecf20Sopenharmony_ci * set it will cause associations that are idle for more than the
22568c2ecf20Sopenharmony_ci * specified number of seconds to automatically close.  An association
22578c2ecf20Sopenharmony_ci * being idle is defined an association that has NOT sent or received
22588c2ecf20Sopenharmony_ci * user data.  The special value of '0' indicates that no automatic
22598c2ecf20Sopenharmony_ci * close of any associations should be performed.  The option expects an
22608c2ecf20Sopenharmony_ci * integer defining the number of seconds of idle time before an
22618c2ecf20Sopenharmony_ci * association is closed.
22628c2ecf20Sopenharmony_ci */
22638c2ecf20Sopenharmony_cistatic int sctp_setsockopt_autoclose(struct sock *sk, u32 *optval,
22648c2ecf20Sopenharmony_ci				     unsigned int optlen)
22658c2ecf20Sopenharmony_ci{
22668c2ecf20Sopenharmony_ci	struct sctp_sock *sp = sctp_sk(sk);
22678c2ecf20Sopenharmony_ci	struct net *net = sock_net(sk);
22688c2ecf20Sopenharmony_ci
22698c2ecf20Sopenharmony_ci	/* Applicable to UDP-style socket only */
22708c2ecf20Sopenharmony_ci	if (sctp_style(sk, TCP))
22718c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
22728c2ecf20Sopenharmony_ci	if (optlen != sizeof(int))
22738c2ecf20Sopenharmony_ci		return -EINVAL;
22748c2ecf20Sopenharmony_ci
22758c2ecf20Sopenharmony_ci	sp->autoclose = *optval;
22768c2ecf20Sopenharmony_ci	if (sp->autoclose > net->sctp.max_autoclose)
22778c2ecf20Sopenharmony_ci		sp->autoclose = net->sctp.max_autoclose;
22788c2ecf20Sopenharmony_ci
22798c2ecf20Sopenharmony_ci	return 0;
22808c2ecf20Sopenharmony_ci}
22818c2ecf20Sopenharmony_ci
22828c2ecf20Sopenharmony_ci/* 7.1.13 Peer Address Parameters (SCTP_PEER_ADDR_PARAMS)
22838c2ecf20Sopenharmony_ci *
22848c2ecf20Sopenharmony_ci * Applications can enable or disable heartbeats for any peer address of
22858c2ecf20Sopenharmony_ci * an association, modify an address's heartbeat interval, force a
22868c2ecf20Sopenharmony_ci * heartbeat to be sent immediately, and adjust the address's maximum
22878c2ecf20Sopenharmony_ci * number of retransmissions sent before an address is considered
22888c2ecf20Sopenharmony_ci * unreachable.  The following structure is used to access and modify an
22898c2ecf20Sopenharmony_ci * address's parameters:
22908c2ecf20Sopenharmony_ci *
22918c2ecf20Sopenharmony_ci *  struct sctp_paddrparams {
22928c2ecf20Sopenharmony_ci *     sctp_assoc_t            spp_assoc_id;
22938c2ecf20Sopenharmony_ci *     struct sockaddr_storage spp_address;
22948c2ecf20Sopenharmony_ci *     uint32_t                spp_hbinterval;
22958c2ecf20Sopenharmony_ci *     uint16_t                spp_pathmaxrxt;
22968c2ecf20Sopenharmony_ci *     uint32_t                spp_pathmtu;
22978c2ecf20Sopenharmony_ci *     uint32_t                spp_sackdelay;
22988c2ecf20Sopenharmony_ci *     uint32_t                spp_flags;
22998c2ecf20Sopenharmony_ci *     uint32_t                spp_ipv6_flowlabel;
23008c2ecf20Sopenharmony_ci *     uint8_t                 spp_dscp;
23018c2ecf20Sopenharmony_ci * };
23028c2ecf20Sopenharmony_ci *
23038c2ecf20Sopenharmony_ci *   spp_assoc_id    - (one-to-many style socket) This is filled in the
23048c2ecf20Sopenharmony_ci *                     application, and identifies the association for
23058c2ecf20Sopenharmony_ci *                     this query.
23068c2ecf20Sopenharmony_ci *   spp_address     - This specifies which address is of interest.
23078c2ecf20Sopenharmony_ci *   spp_hbinterval  - This contains the value of the heartbeat interval,
23088c2ecf20Sopenharmony_ci *                     in milliseconds.  If a  value of zero
23098c2ecf20Sopenharmony_ci *                     is present in this field then no changes are to
23108c2ecf20Sopenharmony_ci *                     be made to this parameter.
23118c2ecf20Sopenharmony_ci *   spp_pathmaxrxt  - This contains the maximum number of
23128c2ecf20Sopenharmony_ci *                     retransmissions before this address shall be
23138c2ecf20Sopenharmony_ci *                     considered unreachable. If a  value of zero
23148c2ecf20Sopenharmony_ci *                     is present in this field then no changes are to
23158c2ecf20Sopenharmony_ci *                     be made to this parameter.
23168c2ecf20Sopenharmony_ci *   spp_pathmtu     - When Path MTU discovery is disabled the value
23178c2ecf20Sopenharmony_ci *                     specified here will be the "fixed" path mtu.
23188c2ecf20Sopenharmony_ci *                     Note that if the spp_address field is empty
23198c2ecf20Sopenharmony_ci *                     then all associations on this address will
23208c2ecf20Sopenharmony_ci *                     have this fixed path mtu set upon them.
23218c2ecf20Sopenharmony_ci *
23228c2ecf20Sopenharmony_ci *   spp_sackdelay   - When delayed sack is enabled, this value specifies
23238c2ecf20Sopenharmony_ci *                     the number of milliseconds that sacks will be delayed
23248c2ecf20Sopenharmony_ci *                     for. This value will apply to all addresses of an
23258c2ecf20Sopenharmony_ci *                     association if the spp_address field is empty. Note
23268c2ecf20Sopenharmony_ci *                     also, that if delayed sack is enabled and this
23278c2ecf20Sopenharmony_ci *                     value is set to 0, no change is made to the last
23288c2ecf20Sopenharmony_ci *                     recorded delayed sack timer value.
23298c2ecf20Sopenharmony_ci *
23308c2ecf20Sopenharmony_ci *   spp_flags       - These flags are used to control various features
23318c2ecf20Sopenharmony_ci *                     on an association. The flag field may contain
23328c2ecf20Sopenharmony_ci *                     zero or more of the following options.
23338c2ecf20Sopenharmony_ci *
23348c2ecf20Sopenharmony_ci *                     SPP_HB_ENABLE  - Enable heartbeats on the
23358c2ecf20Sopenharmony_ci *                     specified address. Note that if the address
23368c2ecf20Sopenharmony_ci *                     field is empty all addresses for the association
23378c2ecf20Sopenharmony_ci *                     have heartbeats enabled upon them.
23388c2ecf20Sopenharmony_ci *
23398c2ecf20Sopenharmony_ci *                     SPP_HB_DISABLE - Disable heartbeats on the
23408c2ecf20Sopenharmony_ci *                     speicifed address. Note that if the address
23418c2ecf20Sopenharmony_ci *                     field is empty all addresses for the association
23428c2ecf20Sopenharmony_ci *                     will have their heartbeats disabled. Note also
23438c2ecf20Sopenharmony_ci *                     that SPP_HB_ENABLE and SPP_HB_DISABLE are
23448c2ecf20Sopenharmony_ci *                     mutually exclusive, only one of these two should
23458c2ecf20Sopenharmony_ci *                     be specified. Enabling both fields will have
23468c2ecf20Sopenharmony_ci *                     undetermined results.
23478c2ecf20Sopenharmony_ci *
23488c2ecf20Sopenharmony_ci *                     SPP_HB_DEMAND - Request a user initiated heartbeat
23498c2ecf20Sopenharmony_ci *                     to be made immediately.
23508c2ecf20Sopenharmony_ci *
23518c2ecf20Sopenharmony_ci *                     SPP_HB_TIME_IS_ZERO - Specify's that the time for
23528c2ecf20Sopenharmony_ci *                     heartbeat delayis to be set to the value of 0
23538c2ecf20Sopenharmony_ci *                     milliseconds.
23548c2ecf20Sopenharmony_ci *
23558c2ecf20Sopenharmony_ci *                     SPP_PMTUD_ENABLE - This field will enable PMTU
23568c2ecf20Sopenharmony_ci *                     discovery upon the specified address. Note that
23578c2ecf20Sopenharmony_ci *                     if the address feild is empty then all addresses
23588c2ecf20Sopenharmony_ci *                     on the association are effected.
23598c2ecf20Sopenharmony_ci *
23608c2ecf20Sopenharmony_ci *                     SPP_PMTUD_DISABLE - This field will disable PMTU
23618c2ecf20Sopenharmony_ci *                     discovery upon the specified address. Note that
23628c2ecf20Sopenharmony_ci *                     if the address feild is empty then all addresses
23638c2ecf20Sopenharmony_ci *                     on the association are effected. Not also that
23648c2ecf20Sopenharmony_ci *                     SPP_PMTUD_ENABLE and SPP_PMTUD_DISABLE are mutually
23658c2ecf20Sopenharmony_ci *                     exclusive. Enabling both will have undetermined
23668c2ecf20Sopenharmony_ci *                     results.
23678c2ecf20Sopenharmony_ci *
23688c2ecf20Sopenharmony_ci *                     SPP_SACKDELAY_ENABLE - Setting this flag turns
23698c2ecf20Sopenharmony_ci *                     on delayed sack. The time specified in spp_sackdelay
23708c2ecf20Sopenharmony_ci *                     is used to specify the sack delay for this address. Note
23718c2ecf20Sopenharmony_ci *                     that if spp_address is empty then all addresses will
23728c2ecf20Sopenharmony_ci *                     enable delayed sack and take on the sack delay
23738c2ecf20Sopenharmony_ci *                     value specified in spp_sackdelay.
23748c2ecf20Sopenharmony_ci *                     SPP_SACKDELAY_DISABLE - Setting this flag turns
23758c2ecf20Sopenharmony_ci *                     off delayed sack. If the spp_address field is blank then
23768c2ecf20Sopenharmony_ci *                     delayed sack is disabled for the entire association. Note
23778c2ecf20Sopenharmony_ci *                     also that this field is mutually exclusive to
23788c2ecf20Sopenharmony_ci *                     SPP_SACKDELAY_ENABLE, setting both will have undefined
23798c2ecf20Sopenharmony_ci *                     results.
23808c2ecf20Sopenharmony_ci *
23818c2ecf20Sopenharmony_ci *                     SPP_IPV6_FLOWLABEL:  Setting this flag enables the
23828c2ecf20Sopenharmony_ci *                     setting of the IPV6 flow label value.  The value is
23838c2ecf20Sopenharmony_ci *                     contained in the spp_ipv6_flowlabel field.
23848c2ecf20Sopenharmony_ci *                     Upon retrieval, this flag will be set to indicate that
23858c2ecf20Sopenharmony_ci *                     the spp_ipv6_flowlabel field has a valid value returned.
23868c2ecf20Sopenharmony_ci *                     If a specific destination address is set (in the
23878c2ecf20Sopenharmony_ci *                     spp_address field), then the value returned is that of
23888c2ecf20Sopenharmony_ci *                     the address.  If just an association is specified (and
23898c2ecf20Sopenharmony_ci *                     no address), then the association's default flow label
23908c2ecf20Sopenharmony_ci *                     is returned.  If neither an association nor a destination
23918c2ecf20Sopenharmony_ci *                     is specified, then the socket's default flow label is
23928c2ecf20Sopenharmony_ci *                     returned.  For non-IPv6 sockets, this flag will be left
23938c2ecf20Sopenharmony_ci *                     cleared.
23948c2ecf20Sopenharmony_ci *
23958c2ecf20Sopenharmony_ci *                     SPP_DSCP:  Setting this flag enables the setting of the
23968c2ecf20Sopenharmony_ci *                     Differentiated Services Code Point (DSCP) value
23978c2ecf20Sopenharmony_ci *                     associated with either the association or a specific
23988c2ecf20Sopenharmony_ci *                     address.  The value is obtained in the spp_dscp field.
23998c2ecf20Sopenharmony_ci *                     Upon retrieval, this flag will be set to indicate that
24008c2ecf20Sopenharmony_ci *                     the spp_dscp field has a valid value returned.  If a
24018c2ecf20Sopenharmony_ci *                     specific destination address is set when called (in the
24028c2ecf20Sopenharmony_ci *                     spp_address field), then that specific destination
24038c2ecf20Sopenharmony_ci *                     address's DSCP value is returned.  If just an association
24048c2ecf20Sopenharmony_ci *                     is specified, then the association's default DSCP is
24058c2ecf20Sopenharmony_ci *                     returned.  If neither an association nor a destination is
24068c2ecf20Sopenharmony_ci *                     specified, then the socket's default DSCP is returned.
24078c2ecf20Sopenharmony_ci *
24088c2ecf20Sopenharmony_ci *   spp_ipv6_flowlabel
24098c2ecf20Sopenharmony_ci *                   - This field is used in conjunction with the
24108c2ecf20Sopenharmony_ci *                     SPP_IPV6_FLOWLABEL flag and contains the IPv6 flow label.
24118c2ecf20Sopenharmony_ci *                     The 20 least significant bits are used for the flow
24128c2ecf20Sopenharmony_ci *                     label.  This setting has precedence over any IPv6-layer
24138c2ecf20Sopenharmony_ci *                     setting.
24148c2ecf20Sopenharmony_ci *
24158c2ecf20Sopenharmony_ci *   spp_dscp        - This field is used in conjunction with the SPP_DSCP flag
24168c2ecf20Sopenharmony_ci *                     and contains the DSCP.  The 6 most significant bits are
24178c2ecf20Sopenharmony_ci *                     used for the DSCP.  This setting has precedence over any
24188c2ecf20Sopenharmony_ci *                     IPv4- or IPv6- layer setting.
24198c2ecf20Sopenharmony_ci */
24208c2ecf20Sopenharmony_cistatic int sctp_apply_peer_addr_params(struct sctp_paddrparams *params,
24218c2ecf20Sopenharmony_ci				       struct sctp_transport   *trans,
24228c2ecf20Sopenharmony_ci				       struct sctp_association *asoc,
24238c2ecf20Sopenharmony_ci				       struct sctp_sock        *sp,
24248c2ecf20Sopenharmony_ci				       int                      hb_change,
24258c2ecf20Sopenharmony_ci				       int                      pmtud_change,
24268c2ecf20Sopenharmony_ci				       int                      sackdelay_change)
24278c2ecf20Sopenharmony_ci{
24288c2ecf20Sopenharmony_ci	int error;
24298c2ecf20Sopenharmony_ci
24308c2ecf20Sopenharmony_ci	if (params->spp_flags & SPP_HB_DEMAND && trans) {
24318c2ecf20Sopenharmony_ci		error = sctp_primitive_REQUESTHEARTBEAT(trans->asoc->base.net,
24328c2ecf20Sopenharmony_ci							trans->asoc, trans);
24338c2ecf20Sopenharmony_ci		if (error)
24348c2ecf20Sopenharmony_ci			return error;
24358c2ecf20Sopenharmony_ci	}
24368c2ecf20Sopenharmony_ci
24378c2ecf20Sopenharmony_ci	/* Note that unless the spp_flag is set to SPP_HB_ENABLE the value of
24388c2ecf20Sopenharmony_ci	 * this field is ignored.  Note also that a value of zero indicates
24398c2ecf20Sopenharmony_ci	 * the current setting should be left unchanged.
24408c2ecf20Sopenharmony_ci	 */
24418c2ecf20Sopenharmony_ci	if (params->spp_flags & SPP_HB_ENABLE) {
24428c2ecf20Sopenharmony_ci
24438c2ecf20Sopenharmony_ci		/* Re-zero the interval if the SPP_HB_TIME_IS_ZERO is
24448c2ecf20Sopenharmony_ci		 * set.  This lets us use 0 value when this flag
24458c2ecf20Sopenharmony_ci		 * is set.
24468c2ecf20Sopenharmony_ci		 */
24478c2ecf20Sopenharmony_ci		if (params->spp_flags & SPP_HB_TIME_IS_ZERO)
24488c2ecf20Sopenharmony_ci			params->spp_hbinterval = 0;
24498c2ecf20Sopenharmony_ci
24508c2ecf20Sopenharmony_ci		if (params->spp_hbinterval ||
24518c2ecf20Sopenharmony_ci		    (params->spp_flags & SPP_HB_TIME_IS_ZERO)) {
24528c2ecf20Sopenharmony_ci			if (trans) {
24538c2ecf20Sopenharmony_ci				trans->hbinterval =
24548c2ecf20Sopenharmony_ci				    msecs_to_jiffies(params->spp_hbinterval);
24558c2ecf20Sopenharmony_ci				sctp_transport_reset_hb_timer(trans);
24568c2ecf20Sopenharmony_ci			} else if (asoc) {
24578c2ecf20Sopenharmony_ci				asoc->hbinterval =
24588c2ecf20Sopenharmony_ci				    msecs_to_jiffies(params->spp_hbinterval);
24598c2ecf20Sopenharmony_ci			} else {
24608c2ecf20Sopenharmony_ci				sp->hbinterval = params->spp_hbinterval;
24618c2ecf20Sopenharmony_ci			}
24628c2ecf20Sopenharmony_ci		}
24638c2ecf20Sopenharmony_ci	}
24648c2ecf20Sopenharmony_ci
24658c2ecf20Sopenharmony_ci	if (hb_change) {
24668c2ecf20Sopenharmony_ci		if (trans) {
24678c2ecf20Sopenharmony_ci			trans->param_flags =
24688c2ecf20Sopenharmony_ci				(trans->param_flags & ~SPP_HB) | hb_change;
24698c2ecf20Sopenharmony_ci		} else if (asoc) {
24708c2ecf20Sopenharmony_ci			asoc->param_flags =
24718c2ecf20Sopenharmony_ci				(asoc->param_flags & ~SPP_HB) | hb_change;
24728c2ecf20Sopenharmony_ci		} else {
24738c2ecf20Sopenharmony_ci			sp->param_flags =
24748c2ecf20Sopenharmony_ci				(sp->param_flags & ~SPP_HB) | hb_change;
24758c2ecf20Sopenharmony_ci		}
24768c2ecf20Sopenharmony_ci	}
24778c2ecf20Sopenharmony_ci
24788c2ecf20Sopenharmony_ci	/* When Path MTU discovery is disabled the value specified here will
24798c2ecf20Sopenharmony_ci	 * be the "fixed" path mtu (i.e. the value of the spp_flags field must
24808c2ecf20Sopenharmony_ci	 * include the flag SPP_PMTUD_DISABLE for this field to have any
24818c2ecf20Sopenharmony_ci	 * effect).
24828c2ecf20Sopenharmony_ci	 */
24838c2ecf20Sopenharmony_ci	if ((params->spp_flags & SPP_PMTUD_DISABLE) && params->spp_pathmtu) {
24848c2ecf20Sopenharmony_ci		if (trans) {
24858c2ecf20Sopenharmony_ci			trans->pathmtu = params->spp_pathmtu;
24868c2ecf20Sopenharmony_ci			sctp_assoc_sync_pmtu(asoc);
24878c2ecf20Sopenharmony_ci		} else if (asoc) {
24888c2ecf20Sopenharmony_ci			sctp_assoc_set_pmtu(asoc, params->spp_pathmtu);
24898c2ecf20Sopenharmony_ci		} else {
24908c2ecf20Sopenharmony_ci			sp->pathmtu = params->spp_pathmtu;
24918c2ecf20Sopenharmony_ci		}
24928c2ecf20Sopenharmony_ci	}
24938c2ecf20Sopenharmony_ci
24948c2ecf20Sopenharmony_ci	if (pmtud_change) {
24958c2ecf20Sopenharmony_ci		if (trans) {
24968c2ecf20Sopenharmony_ci			int update = (trans->param_flags & SPP_PMTUD_DISABLE) &&
24978c2ecf20Sopenharmony_ci				(params->spp_flags & SPP_PMTUD_ENABLE);
24988c2ecf20Sopenharmony_ci			trans->param_flags =
24998c2ecf20Sopenharmony_ci				(trans->param_flags & ~SPP_PMTUD) | pmtud_change;
25008c2ecf20Sopenharmony_ci			if (update) {
25018c2ecf20Sopenharmony_ci				sctp_transport_pmtu(trans, sctp_opt2sk(sp));
25028c2ecf20Sopenharmony_ci				sctp_assoc_sync_pmtu(asoc);
25038c2ecf20Sopenharmony_ci			}
25048c2ecf20Sopenharmony_ci		} else if (asoc) {
25058c2ecf20Sopenharmony_ci			asoc->param_flags =
25068c2ecf20Sopenharmony_ci				(asoc->param_flags & ~SPP_PMTUD) | pmtud_change;
25078c2ecf20Sopenharmony_ci		} else {
25088c2ecf20Sopenharmony_ci			sp->param_flags =
25098c2ecf20Sopenharmony_ci				(sp->param_flags & ~SPP_PMTUD) | pmtud_change;
25108c2ecf20Sopenharmony_ci		}
25118c2ecf20Sopenharmony_ci	}
25128c2ecf20Sopenharmony_ci
25138c2ecf20Sopenharmony_ci	/* Note that unless the spp_flag is set to SPP_SACKDELAY_ENABLE the
25148c2ecf20Sopenharmony_ci	 * value of this field is ignored.  Note also that a value of zero
25158c2ecf20Sopenharmony_ci	 * indicates the current setting should be left unchanged.
25168c2ecf20Sopenharmony_ci	 */
25178c2ecf20Sopenharmony_ci	if ((params->spp_flags & SPP_SACKDELAY_ENABLE) && params->spp_sackdelay) {
25188c2ecf20Sopenharmony_ci		if (trans) {
25198c2ecf20Sopenharmony_ci			trans->sackdelay =
25208c2ecf20Sopenharmony_ci				msecs_to_jiffies(params->spp_sackdelay);
25218c2ecf20Sopenharmony_ci		} else if (asoc) {
25228c2ecf20Sopenharmony_ci			asoc->sackdelay =
25238c2ecf20Sopenharmony_ci				msecs_to_jiffies(params->spp_sackdelay);
25248c2ecf20Sopenharmony_ci		} else {
25258c2ecf20Sopenharmony_ci			sp->sackdelay = params->spp_sackdelay;
25268c2ecf20Sopenharmony_ci		}
25278c2ecf20Sopenharmony_ci	}
25288c2ecf20Sopenharmony_ci
25298c2ecf20Sopenharmony_ci	if (sackdelay_change) {
25308c2ecf20Sopenharmony_ci		if (trans) {
25318c2ecf20Sopenharmony_ci			trans->param_flags =
25328c2ecf20Sopenharmony_ci				(trans->param_flags & ~SPP_SACKDELAY) |
25338c2ecf20Sopenharmony_ci				sackdelay_change;
25348c2ecf20Sopenharmony_ci		} else if (asoc) {
25358c2ecf20Sopenharmony_ci			asoc->param_flags =
25368c2ecf20Sopenharmony_ci				(asoc->param_flags & ~SPP_SACKDELAY) |
25378c2ecf20Sopenharmony_ci				sackdelay_change;
25388c2ecf20Sopenharmony_ci		} else {
25398c2ecf20Sopenharmony_ci			sp->param_flags =
25408c2ecf20Sopenharmony_ci				(sp->param_flags & ~SPP_SACKDELAY) |
25418c2ecf20Sopenharmony_ci				sackdelay_change;
25428c2ecf20Sopenharmony_ci		}
25438c2ecf20Sopenharmony_ci	}
25448c2ecf20Sopenharmony_ci
25458c2ecf20Sopenharmony_ci	/* Note that a value of zero indicates the current setting should be
25468c2ecf20Sopenharmony_ci	   left unchanged.
25478c2ecf20Sopenharmony_ci	 */
25488c2ecf20Sopenharmony_ci	if (params->spp_pathmaxrxt) {
25498c2ecf20Sopenharmony_ci		if (trans) {
25508c2ecf20Sopenharmony_ci			trans->pathmaxrxt = params->spp_pathmaxrxt;
25518c2ecf20Sopenharmony_ci		} else if (asoc) {
25528c2ecf20Sopenharmony_ci			asoc->pathmaxrxt = params->spp_pathmaxrxt;
25538c2ecf20Sopenharmony_ci		} else {
25548c2ecf20Sopenharmony_ci			sp->pathmaxrxt = params->spp_pathmaxrxt;
25558c2ecf20Sopenharmony_ci		}
25568c2ecf20Sopenharmony_ci	}
25578c2ecf20Sopenharmony_ci
25588c2ecf20Sopenharmony_ci	if (params->spp_flags & SPP_IPV6_FLOWLABEL) {
25598c2ecf20Sopenharmony_ci		if (trans) {
25608c2ecf20Sopenharmony_ci			if (trans->ipaddr.sa.sa_family == AF_INET6) {
25618c2ecf20Sopenharmony_ci				trans->flowlabel = params->spp_ipv6_flowlabel &
25628c2ecf20Sopenharmony_ci						   SCTP_FLOWLABEL_VAL_MASK;
25638c2ecf20Sopenharmony_ci				trans->flowlabel |= SCTP_FLOWLABEL_SET_MASK;
25648c2ecf20Sopenharmony_ci			}
25658c2ecf20Sopenharmony_ci		} else if (asoc) {
25668c2ecf20Sopenharmony_ci			struct sctp_transport *t;
25678c2ecf20Sopenharmony_ci
25688c2ecf20Sopenharmony_ci			list_for_each_entry(t, &asoc->peer.transport_addr_list,
25698c2ecf20Sopenharmony_ci					    transports) {
25708c2ecf20Sopenharmony_ci				if (t->ipaddr.sa.sa_family != AF_INET6)
25718c2ecf20Sopenharmony_ci					continue;
25728c2ecf20Sopenharmony_ci				t->flowlabel = params->spp_ipv6_flowlabel &
25738c2ecf20Sopenharmony_ci					       SCTP_FLOWLABEL_VAL_MASK;
25748c2ecf20Sopenharmony_ci				t->flowlabel |= SCTP_FLOWLABEL_SET_MASK;
25758c2ecf20Sopenharmony_ci			}
25768c2ecf20Sopenharmony_ci			asoc->flowlabel = params->spp_ipv6_flowlabel &
25778c2ecf20Sopenharmony_ci					  SCTP_FLOWLABEL_VAL_MASK;
25788c2ecf20Sopenharmony_ci			asoc->flowlabel |= SCTP_FLOWLABEL_SET_MASK;
25798c2ecf20Sopenharmony_ci		} else if (sctp_opt2sk(sp)->sk_family == AF_INET6) {
25808c2ecf20Sopenharmony_ci			sp->flowlabel = params->spp_ipv6_flowlabel &
25818c2ecf20Sopenharmony_ci					SCTP_FLOWLABEL_VAL_MASK;
25828c2ecf20Sopenharmony_ci			sp->flowlabel |= SCTP_FLOWLABEL_SET_MASK;
25838c2ecf20Sopenharmony_ci		}
25848c2ecf20Sopenharmony_ci	}
25858c2ecf20Sopenharmony_ci
25868c2ecf20Sopenharmony_ci	if (params->spp_flags & SPP_DSCP) {
25878c2ecf20Sopenharmony_ci		if (trans) {
25888c2ecf20Sopenharmony_ci			trans->dscp = params->spp_dscp & SCTP_DSCP_VAL_MASK;
25898c2ecf20Sopenharmony_ci			trans->dscp |= SCTP_DSCP_SET_MASK;
25908c2ecf20Sopenharmony_ci		} else if (asoc) {
25918c2ecf20Sopenharmony_ci			struct sctp_transport *t;
25928c2ecf20Sopenharmony_ci
25938c2ecf20Sopenharmony_ci			list_for_each_entry(t, &asoc->peer.transport_addr_list,
25948c2ecf20Sopenharmony_ci					    transports) {
25958c2ecf20Sopenharmony_ci				t->dscp = params->spp_dscp &
25968c2ecf20Sopenharmony_ci					  SCTP_DSCP_VAL_MASK;
25978c2ecf20Sopenharmony_ci				t->dscp |= SCTP_DSCP_SET_MASK;
25988c2ecf20Sopenharmony_ci			}
25998c2ecf20Sopenharmony_ci			asoc->dscp = params->spp_dscp & SCTP_DSCP_VAL_MASK;
26008c2ecf20Sopenharmony_ci			asoc->dscp |= SCTP_DSCP_SET_MASK;
26018c2ecf20Sopenharmony_ci		} else {
26028c2ecf20Sopenharmony_ci			sp->dscp = params->spp_dscp & SCTP_DSCP_VAL_MASK;
26038c2ecf20Sopenharmony_ci			sp->dscp |= SCTP_DSCP_SET_MASK;
26048c2ecf20Sopenharmony_ci		}
26058c2ecf20Sopenharmony_ci	}
26068c2ecf20Sopenharmony_ci
26078c2ecf20Sopenharmony_ci	return 0;
26088c2ecf20Sopenharmony_ci}
26098c2ecf20Sopenharmony_ci
26108c2ecf20Sopenharmony_cistatic int sctp_setsockopt_peer_addr_params(struct sock *sk,
26118c2ecf20Sopenharmony_ci					    struct sctp_paddrparams *params,
26128c2ecf20Sopenharmony_ci					    unsigned int optlen)
26138c2ecf20Sopenharmony_ci{
26148c2ecf20Sopenharmony_ci	struct sctp_transport   *trans = NULL;
26158c2ecf20Sopenharmony_ci	struct sctp_association *asoc = NULL;
26168c2ecf20Sopenharmony_ci	struct sctp_sock        *sp = sctp_sk(sk);
26178c2ecf20Sopenharmony_ci	int error;
26188c2ecf20Sopenharmony_ci	int hb_change, pmtud_change, sackdelay_change;
26198c2ecf20Sopenharmony_ci
26208c2ecf20Sopenharmony_ci	if (optlen == ALIGN(offsetof(struct sctp_paddrparams,
26218c2ecf20Sopenharmony_ci					    spp_ipv6_flowlabel), 4)) {
26228c2ecf20Sopenharmony_ci		if (params->spp_flags & (SPP_DSCP | SPP_IPV6_FLOWLABEL))
26238c2ecf20Sopenharmony_ci			return -EINVAL;
26248c2ecf20Sopenharmony_ci	} else if (optlen != sizeof(*params)) {
26258c2ecf20Sopenharmony_ci		return -EINVAL;
26268c2ecf20Sopenharmony_ci	}
26278c2ecf20Sopenharmony_ci
26288c2ecf20Sopenharmony_ci	/* Validate flags and value parameters. */
26298c2ecf20Sopenharmony_ci	hb_change        = params->spp_flags & SPP_HB;
26308c2ecf20Sopenharmony_ci	pmtud_change     = params->spp_flags & SPP_PMTUD;
26318c2ecf20Sopenharmony_ci	sackdelay_change = params->spp_flags & SPP_SACKDELAY;
26328c2ecf20Sopenharmony_ci
26338c2ecf20Sopenharmony_ci	if (hb_change        == SPP_HB ||
26348c2ecf20Sopenharmony_ci	    pmtud_change     == SPP_PMTUD ||
26358c2ecf20Sopenharmony_ci	    sackdelay_change == SPP_SACKDELAY ||
26368c2ecf20Sopenharmony_ci	    params->spp_sackdelay > 500 ||
26378c2ecf20Sopenharmony_ci	    (params->spp_pathmtu &&
26388c2ecf20Sopenharmony_ci	     params->spp_pathmtu < SCTP_DEFAULT_MINSEGMENT))
26398c2ecf20Sopenharmony_ci		return -EINVAL;
26408c2ecf20Sopenharmony_ci
26418c2ecf20Sopenharmony_ci	/* If an address other than INADDR_ANY is specified, and
26428c2ecf20Sopenharmony_ci	 * no transport is found, then the request is invalid.
26438c2ecf20Sopenharmony_ci	 */
26448c2ecf20Sopenharmony_ci	if (!sctp_is_any(sk, (union sctp_addr *)&params->spp_address)) {
26458c2ecf20Sopenharmony_ci		trans = sctp_addr_id2transport(sk, &params->spp_address,
26468c2ecf20Sopenharmony_ci					       params->spp_assoc_id);
26478c2ecf20Sopenharmony_ci		if (!trans)
26488c2ecf20Sopenharmony_ci			return -EINVAL;
26498c2ecf20Sopenharmony_ci	}
26508c2ecf20Sopenharmony_ci
26518c2ecf20Sopenharmony_ci	/* Get association, if assoc_id != SCTP_FUTURE_ASSOC and the
26528c2ecf20Sopenharmony_ci	 * socket is a one to many style socket, and an association
26538c2ecf20Sopenharmony_ci	 * was not found, then the id was invalid.
26548c2ecf20Sopenharmony_ci	 */
26558c2ecf20Sopenharmony_ci	asoc = sctp_id2assoc(sk, params->spp_assoc_id);
26568c2ecf20Sopenharmony_ci	if (!asoc && params->spp_assoc_id != SCTP_FUTURE_ASSOC &&
26578c2ecf20Sopenharmony_ci	    sctp_style(sk, UDP))
26588c2ecf20Sopenharmony_ci		return -EINVAL;
26598c2ecf20Sopenharmony_ci
26608c2ecf20Sopenharmony_ci	/* Heartbeat demand can only be sent on a transport or
26618c2ecf20Sopenharmony_ci	 * association, but not a socket.
26628c2ecf20Sopenharmony_ci	 */
26638c2ecf20Sopenharmony_ci	if (params->spp_flags & SPP_HB_DEMAND && !trans && !asoc)
26648c2ecf20Sopenharmony_ci		return -EINVAL;
26658c2ecf20Sopenharmony_ci
26668c2ecf20Sopenharmony_ci	/* Process parameters. */
26678c2ecf20Sopenharmony_ci	error = sctp_apply_peer_addr_params(params, trans, asoc, sp,
26688c2ecf20Sopenharmony_ci					    hb_change, pmtud_change,
26698c2ecf20Sopenharmony_ci					    sackdelay_change);
26708c2ecf20Sopenharmony_ci
26718c2ecf20Sopenharmony_ci	if (error)
26728c2ecf20Sopenharmony_ci		return error;
26738c2ecf20Sopenharmony_ci
26748c2ecf20Sopenharmony_ci	/* If changes are for association, also apply parameters to each
26758c2ecf20Sopenharmony_ci	 * transport.
26768c2ecf20Sopenharmony_ci	 */
26778c2ecf20Sopenharmony_ci	if (!trans && asoc) {
26788c2ecf20Sopenharmony_ci		list_for_each_entry(trans, &asoc->peer.transport_addr_list,
26798c2ecf20Sopenharmony_ci				transports) {
26808c2ecf20Sopenharmony_ci			sctp_apply_peer_addr_params(params, trans, asoc, sp,
26818c2ecf20Sopenharmony_ci						    hb_change, pmtud_change,
26828c2ecf20Sopenharmony_ci						    sackdelay_change);
26838c2ecf20Sopenharmony_ci		}
26848c2ecf20Sopenharmony_ci	}
26858c2ecf20Sopenharmony_ci
26868c2ecf20Sopenharmony_ci	return 0;
26878c2ecf20Sopenharmony_ci}
26888c2ecf20Sopenharmony_ci
26898c2ecf20Sopenharmony_cistatic inline __u32 sctp_spp_sackdelay_enable(__u32 param_flags)
26908c2ecf20Sopenharmony_ci{
26918c2ecf20Sopenharmony_ci	return (param_flags & ~SPP_SACKDELAY) | SPP_SACKDELAY_ENABLE;
26928c2ecf20Sopenharmony_ci}
26938c2ecf20Sopenharmony_ci
26948c2ecf20Sopenharmony_cistatic inline __u32 sctp_spp_sackdelay_disable(__u32 param_flags)
26958c2ecf20Sopenharmony_ci{
26968c2ecf20Sopenharmony_ci	return (param_flags & ~SPP_SACKDELAY) | SPP_SACKDELAY_DISABLE;
26978c2ecf20Sopenharmony_ci}
26988c2ecf20Sopenharmony_ci
26998c2ecf20Sopenharmony_cistatic void sctp_apply_asoc_delayed_ack(struct sctp_sack_info *params,
27008c2ecf20Sopenharmony_ci					struct sctp_association *asoc)
27018c2ecf20Sopenharmony_ci{
27028c2ecf20Sopenharmony_ci	struct sctp_transport *trans;
27038c2ecf20Sopenharmony_ci
27048c2ecf20Sopenharmony_ci	if (params->sack_delay) {
27058c2ecf20Sopenharmony_ci		asoc->sackdelay = msecs_to_jiffies(params->sack_delay);
27068c2ecf20Sopenharmony_ci		asoc->param_flags =
27078c2ecf20Sopenharmony_ci			sctp_spp_sackdelay_enable(asoc->param_flags);
27088c2ecf20Sopenharmony_ci	}
27098c2ecf20Sopenharmony_ci	if (params->sack_freq == 1) {
27108c2ecf20Sopenharmony_ci		asoc->param_flags =
27118c2ecf20Sopenharmony_ci			sctp_spp_sackdelay_disable(asoc->param_flags);
27128c2ecf20Sopenharmony_ci	} else if (params->sack_freq > 1) {
27138c2ecf20Sopenharmony_ci		asoc->sackfreq = params->sack_freq;
27148c2ecf20Sopenharmony_ci		asoc->param_flags =
27158c2ecf20Sopenharmony_ci			sctp_spp_sackdelay_enable(asoc->param_flags);
27168c2ecf20Sopenharmony_ci	}
27178c2ecf20Sopenharmony_ci
27188c2ecf20Sopenharmony_ci	list_for_each_entry(trans, &asoc->peer.transport_addr_list,
27198c2ecf20Sopenharmony_ci			    transports) {
27208c2ecf20Sopenharmony_ci		if (params->sack_delay) {
27218c2ecf20Sopenharmony_ci			trans->sackdelay = msecs_to_jiffies(params->sack_delay);
27228c2ecf20Sopenharmony_ci			trans->param_flags =
27238c2ecf20Sopenharmony_ci				sctp_spp_sackdelay_enable(trans->param_flags);
27248c2ecf20Sopenharmony_ci		}
27258c2ecf20Sopenharmony_ci		if (params->sack_freq == 1) {
27268c2ecf20Sopenharmony_ci			trans->param_flags =
27278c2ecf20Sopenharmony_ci				sctp_spp_sackdelay_disable(trans->param_flags);
27288c2ecf20Sopenharmony_ci		} else if (params->sack_freq > 1) {
27298c2ecf20Sopenharmony_ci			trans->sackfreq = params->sack_freq;
27308c2ecf20Sopenharmony_ci			trans->param_flags =
27318c2ecf20Sopenharmony_ci				sctp_spp_sackdelay_enable(trans->param_flags);
27328c2ecf20Sopenharmony_ci		}
27338c2ecf20Sopenharmony_ci	}
27348c2ecf20Sopenharmony_ci}
27358c2ecf20Sopenharmony_ci
27368c2ecf20Sopenharmony_ci/*
27378c2ecf20Sopenharmony_ci * 7.1.23.  Get or set delayed ack timer (SCTP_DELAYED_SACK)
27388c2ecf20Sopenharmony_ci *
27398c2ecf20Sopenharmony_ci * This option will effect the way delayed acks are performed.  This
27408c2ecf20Sopenharmony_ci * option allows you to get or set the delayed ack time, in
27418c2ecf20Sopenharmony_ci * milliseconds.  It also allows changing the delayed ack frequency.
27428c2ecf20Sopenharmony_ci * Changing the frequency to 1 disables the delayed sack algorithm.  If
27438c2ecf20Sopenharmony_ci * the assoc_id is 0, then this sets or gets the endpoints default
27448c2ecf20Sopenharmony_ci * values.  If the assoc_id field is non-zero, then the set or get
27458c2ecf20Sopenharmony_ci * effects the specified association for the one to many model (the
27468c2ecf20Sopenharmony_ci * assoc_id field is ignored by the one to one model).  Note that if
27478c2ecf20Sopenharmony_ci * sack_delay or sack_freq are 0 when setting this option, then the
27488c2ecf20Sopenharmony_ci * current values will remain unchanged.
27498c2ecf20Sopenharmony_ci *
27508c2ecf20Sopenharmony_ci * struct sctp_sack_info {
27518c2ecf20Sopenharmony_ci *     sctp_assoc_t            sack_assoc_id;
27528c2ecf20Sopenharmony_ci *     uint32_t                sack_delay;
27538c2ecf20Sopenharmony_ci *     uint32_t                sack_freq;
27548c2ecf20Sopenharmony_ci * };
27558c2ecf20Sopenharmony_ci *
27568c2ecf20Sopenharmony_ci * sack_assoc_id -  This parameter, indicates which association the user
27578c2ecf20Sopenharmony_ci *    is performing an action upon.  Note that if this field's value is
27588c2ecf20Sopenharmony_ci *    zero then the endpoints default value is changed (effecting future
27598c2ecf20Sopenharmony_ci *    associations only).
27608c2ecf20Sopenharmony_ci *
27618c2ecf20Sopenharmony_ci * sack_delay -  This parameter contains the number of milliseconds that
27628c2ecf20Sopenharmony_ci *    the user is requesting the delayed ACK timer be set to.  Note that
27638c2ecf20Sopenharmony_ci *    this value is defined in the standard to be between 200 and 500
27648c2ecf20Sopenharmony_ci *    milliseconds.
27658c2ecf20Sopenharmony_ci *
27668c2ecf20Sopenharmony_ci * sack_freq -  This parameter contains the number of packets that must
27678c2ecf20Sopenharmony_ci *    be received before a sack is sent without waiting for the delay
27688c2ecf20Sopenharmony_ci *    timer to expire.  The default value for this is 2, setting this
27698c2ecf20Sopenharmony_ci *    value to 1 will disable the delayed sack algorithm.
27708c2ecf20Sopenharmony_ci */
27718c2ecf20Sopenharmony_cistatic int __sctp_setsockopt_delayed_ack(struct sock *sk,
27728c2ecf20Sopenharmony_ci					 struct sctp_sack_info *params)
27738c2ecf20Sopenharmony_ci{
27748c2ecf20Sopenharmony_ci	struct sctp_sock *sp = sctp_sk(sk);
27758c2ecf20Sopenharmony_ci	struct sctp_association *asoc;
27768c2ecf20Sopenharmony_ci
27778c2ecf20Sopenharmony_ci	/* Validate value parameter. */
27788c2ecf20Sopenharmony_ci	if (params->sack_delay > 500)
27798c2ecf20Sopenharmony_ci		return -EINVAL;
27808c2ecf20Sopenharmony_ci
27818c2ecf20Sopenharmony_ci	/* Get association, if sack_assoc_id != SCTP_FUTURE_ASSOC and the
27828c2ecf20Sopenharmony_ci	 * socket is a one to many style socket, and an association
27838c2ecf20Sopenharmony_ci	 * was not found, then the id was invalid.
27848c2ecf20Sopenharmony_ci	 */
27858c2ecf20Sopenharmony_ci	asoc = sctp_id2assoc(sk, params->sack_assoc_id);
27868c2ecf20Sopenharmony_ci	if (!asoc && params->sack_assoc_id > SCTP_ALL_ASSOC &&
27878c2ecf20Sopenharmony_ci	    sctp_style(sk, UDP))
27888c2ecf20Sopenharmony_ci		return -EINVAL;
27898c2ecf20Sopenharmony_ci
27908c2ecf20Sopenharmony_ci	if (asoc) {
27918c2ecf20Sopenharmony_ci		sctp_apply_asoc_delayed_ack(params, asoc);
27928c2ecf20Sopenharmony_ci
27938c2ecf20Sopenharmony_ci		return 0;
27948c2ecf20Sopenharmony_ci	}
27958c2ecf20Sopenharmony_ci
27968c2ecf20Sopenharmony_ci	if (sctp_style(sk, TCP))
27978c2ecf20Sopenharmony_ci		params->sack_assoc_id = SCTP_FUTURE_ASSOC;
27988c2ecf20Sopenharmony_ci
27998c2ecf20Sopenharmony_ci	if (params->sack_assoc_id == SCTP_FUTURE_ASSOC ||
28008c2ecf20Sopenharmony_ci	    params->sack_assoc_id == SCTP_ALL_ASSOC) {
28018c2ecf20Sopenharmony_ci		if (params->sack_delay) {
28028c2ecf20Sopenharmony_ci			sp->sackdelay = params->sack_delay;
28038c2ecf20Sopenharmony_ci			sp->param_flags =
28048c2ecf20Sopenharmony_ci				sctp_spp_sackdelay_enable(sp->param_flags);
28058c2ecf20Sopenharmony_ci		}
28068c2ecf20Sopenharmony_ci		if (params->sack_freq == 1) {
28078c2ecf20Sopenharmony_ci			sp->param_flags =
28088c2ecf20Sopenharmony_ci				sctp_spp_sackdelay_disable(sp->param_flags);
28098c2ecf20Sopenharmony_ci		} else if (params->sack_freq > 1) {
28108c2ecf20Sopenharmony_ci			sp->sackfreq = params->sack_freq;
28118c2ecf20Sopenharmony_ci			sp->param_flags =
28128c2ecf20Sopenharmony_ci				sctp_spp_sackdelay_enable(sp->param_flags);
28138c2ecf20Sopenharmony_ci		}
28148c2ecf20Sopenharmony_ci	}
28158c2ecf20Sopenharmony_ci
28168c2ecf20Sopenharmony_ci	if (params->sack_assoc_id == SCTP_CURRENT_ASSOC ||
28178c2ecf20Sopenharmony_ci	    params->sack_assoc_id == SCTP_ALL_ASSOC)
28188c2ecf20Sopenharmony_ci		list_for_each_entry(asoc, &sp->ep->asocs, asocs)
28198c2ecf20Sopenharmony_ci			sctp_apply_asoc_delayed_ack(params, asoc);
28208c2ecf20Sopenharmony_ci
28218c2ecf20Sopenharmony_ci	return 0;
28228c2ecf20Sopenharmony_ci}
28238c2ecf20Sopenharmony_ci
28248c2ecf20Sopenharmony_cistatic int sctp_setsockopt_delayed_ack(struct sock *sk,
28258c2ecf20Sopenharmony_ci				       struct sctp_sack_info *params,
28268c2ecf20Sopenharmony_ci				       unsigned int optlen)
28278c2ecf20Sopenharmony_ci{
28288c2ecf20Sopenharmony_ci	if (optlen == sizeof(struct sctp_assoc_value)) {
28298c2ecf20Sopenharmony_ci		struct sctp_assoc_value *v = (struct sctp_assoc_value *)params;
28308c2ecf20Sopenharmony_ci		struct sctp_sack_info p;
28318c2ecf20Sopenharmony_ci
28328c2ecf20Sopenharmony_ci		pr_warn_ratelimited(DEPRECATED
28338c2ecf20Sopenharmony_ci				    "%s (pid %d) "
28348c2ecf20Sopenharmony_ci				    "Use of struct sctp_assoc_value in delayed_ack socket option.\n"
28358c2ecf20Sopenharmony_ci				    "Use struct sctp_sack_info instead\n",
28368c2ecf20Sopenharmony_ci				    current->comm, task_pid_nr(current));
28378c2ecf20Sopenharmony_ci
28388c2ecf20Sopenharmony_ci		p.sack_assoc_id = v->assoc_id;
28398c2ecf20Sopenharmony_ci		p.sack_delay = v->assoc_value;
28408c2ecf20Sopenharmony_ci		p.sack_freq = v->assoc_value ? 0 : 1;
28418c2ecf20Sopenharmony_ci		return __sctp_setsockopt_delayed_ack(sk, &p);
28428c2ecf20Sopenharmony_ci	}
28438c2ecf20Sopenharmony_ci
28448c2ecf20Sopenharmony_ci	if (optlen != sizeof(struct sctp_sack_info))
28458c2ecf20Sopenharmony_ci		return -EINVAL;
28468c2ecf20Sopenharmony_ci	if (params->sack_delay == 0 && params->sack_freq == 0)
28478c2ecf20Sopenharmony_ci		return 0;
28488c2ecf20Sopenharmony_ci	return __sctp_setsockopt_delayed_ack(sk, params);
28498c2ecf20Sopenharmony_ci}
28508c2ecf20Sopenharmony_ci
28518c2ecf20Sopenharmony_ci/* 7.1.3 Initialization Parameters (SCTP_INITMSG)
28528c2ecf20Sopenharmony_ci *
28538c2ecf20Sopenharmony_ci * Applications can specify protocol parameters for the default association
28548c2ecf20Sopenharmony_ci * initialization.  The option name argument to setsockopt() and getsockopt()
28558c2ecf20Sopenharmony_ci * is SCTP_INITMSG.
28568c2ecf20Sopenharmony_ci *
28578c2ecf20Sopenharmony_ci * Setting initialization parameters is effective only on an unconnected
28588c2ecf20Sopenharmony_ci * socket (for UDP-style sockets only future associations are effected
28598c2ecf20Sopenharmony_ci * by the change).  With TCP-style sockets, this option is inherited by
28608c2ecf20Sopenharmony_ci * sockets derived from a listener socket.
28618c2ecf20Sopenharmony_ci */
28628c2ecf20Sopenharmony_cistatic int sctp_setsockopt_initmsg(struct sock *sk, struct sctp_initmsg *sinit,
28638c2ecf20Sopenharmony_ci				   unsigned int optlen)
28648c2ecf20Sopenharmony_ci{
28658c2ecf20Sopenharmony_ci	struct sctp_sock *sp = sctp_sk(sk);
28668c2ecf20Sopenharmony_ci
28678c2ecf20Sopenharmony_ci	if (optlen != sizeof(struct sctp_initmsg))
28688c2ecf20Sopenharmony_ci		return -EINVAL;
28698c2ecf20Sopenharmony_ci
28708c2ecf20Sopenharmony_ci	if (sinit->sinit_num_ostreams)
28718c2ecf20Sopenharmony_ci		sp->initmsg.sinit_num_ostreams = sinit->sinit_num_ostreams;
28728c2ecf20Sopenharmony_ci	if (sinit->sinit_max_instreams)
28738c2ecf20Sopenharmony_ci		sp->initmsg.sinit_max_instreams = sinit->sinit_max_instreams;
28748c2ecf20Sopenharmony_ci	if (sinit->sinit_max_attempts)
28758c2ecf20Sopenharmony_ci		sp->initmsg.sinit_max_attempts = sinit->sinit_max_attempts;
28768c2ecf20Sopenharmony_ci	if (sinit->sinit_max_init_timeo)
28778c2ecf20Sopenharmony_ci		sp->initmsg.sinit_max_init_timeo = sinit->sinit_max_init_timeo;
28788c2ecf20Sopenharmony_ci
28798c2ecf20Sopenharmony_ci	return 0;
28808c2ecf20Sopenharmony_ci}
28818c2ecf20Sopenharmony_ci
28828c2ecf20Sopenharmony_ci/*
28838c2ecf20Sopenharmony_ci * 7.1.14 Set default send parameters (SCTP_DEFAULT_SEND_PARAM)
28848c2ecf20Sopenharmony_ci *
28858c2ecf20Sopenharmony_ci *   Applications that wish to use the sendto() system call may wish to
28868c2ecf20Sopenharmony_ci *   specify a default set of parameters that would normally be supplied
28878c2ecf20Sopenharmony_ci *   through the inclusion of ancillary data.  This socket option allows
28888c2ecf20Sopenharmony_ci *   such an application to set the default sctp_sndrcvinfo structure.
28898c2ecf20Sopenharmony_ci *   The application that wishes to use this socket option simply passes
28908c2ecf20Sopenharmony_ci *   in to this call the sctp_sndrcvinfo structure defined in Section
28918c2ecf20Sopenharmony_ci *   5.2.2) The input parameters accepted by this call include
28928c2ecf20Sopenharmony_ci *   sinfo_stream, sinfo_flags, sinfo_ppid, sinfo_context,
28938c2ecf20Sopenharmony_ci *   sinfo_timetolive.  The user must provide the sinfo_assoc_id field in
28948c2ecf20Sopenharmony_ci *   to this call if the caller is using the UDP model.
28958c2ecf20Sopenharmony_ci */
28968c2ecf20Sopenharmony_cistatic int sctp_setsockopt_default_send_param(struct sock *sk,
28978c2ecf20Sopenharmony_ci					      struct sctp_sndrcvinfo *info,
28988c2ecf20Sopenharmony_ci					      unsigned int optlen)
28998c2ecf20Sopenharmony_ci{
29008c2ecf20Sopenharmony_ci	struct sctp_sock *sp = sctp_sk(sk);
29018c2ecf20Sopenharmony_ci	struct sctp_association *asoc;
29028c2ecf20Sopenharmony_ci
29038c2ecf20Sopenharmony_ci	if (optlen != sizeof(*info))
29048c2ecf20Sopenharmony_ci		return -EINVAL;
29058c2ecf20Sopenharmony_ci	if (info->sinfo_flags &
29068c2ecf20Sopenharmony_ci	    ~(SCTP_UNORDERED | SCTP_ADDR_OVER |
29078c2ecf20Sopenharmony_ci	      SCTP_ABORT | SCTP_EOF))
29088c2ecf20Sopenharmony_ci		return -EINVAL;
29098c2ecf20Sopenharmony_ci
29108c2ecf20Sopenharmony_ci	asoc = sctp_id2assoc(sk, info->sinfo_assoc_id);
29118c2ecf20Sopenharmony_ci	if (!asoc && info->sinfo_assoc_id > SCTP_ALL_ASSOC &&
29128c2ecf20Sopenharmony_ci	    sctp_style(sk, UDP))
29138c2ecf20Sopenharmony_ci		return -EINVAL;
29148c2ecf20Sopenharmony_ci
29158c2ecf20Sopenharmony_ci	if (asoc) {
29168c2ecf20Sopenharmony_ci		asoc->default_stream = info->sinfo_stream;
29178c2ecf20Sopenharmony_ci		asoc->default_flags = info->sinfo_flags;
29188c2ecf20Sopenharmony_ci		asoc->default_ppid = info->sinfo_ppid;
29198c2ecf20Sopenharmony_ci		asoc->default_context = info->sinfo_context;
29208c2ecf20Sopenharmony_ci		asoc->default_timetolive = info->sinfo_timetolive;
29218c2ecf20Sopenharmony_ci
29228c2ecf20Sopenharmony_ci		return 0;
29238c2ecf20Sopenharmony_ci	}
29248c2ecf20Sopenharmony_ci
29258c2ecf20Sopenharmony_ci	if (sctp_style(sk, TCP))
29268c2ecf20Sopenharmony_ci		info->sinfo_assoc_id = SCTP_FUTURE_ASSOC;
29278c2ecf20Sopenharmony_ci
29288c2ecf20Sopenharmony_ci	if (info->sinfo_assoc_id == SCTP_FUTURE_ASSOC ||
29298c2ecf20Sopenharmony_ci	    info->sinfo_assoc_id == SCTP_ALL_ASSOC) {
29308c2ecf20Sopenharmony_ci		sp->default_stream = info->sinfo_stream;
29318c2ecf20Sopenharmony_ci		sp->default_flags = info->sinfo_flags;
29328c2ecf20Sopenharmony_ci		sp->default_ppid = info->sinfo_ppid;
29338c2ecf20Sopenharmony_ci		sp->default_context = info->sinfo_context;
29348c2ecf20Sopenharmony_ci		sp->default_timetolive = info->sinfo_timetolive;
29358c2ecf20Sopenharmony_ci	}
29368c2ecf20Sopenharmony_ci
29378c2ecf20Sopenharmony_ci	if (info->sinfo_assoc_id == SCTP_CURRENT_ASSOC ||
29388c2ecf20Sopenharmony_ci	    info->sinfo_assoc_id == SCTP_ALL_ASSOC) {
29398c2ecf20Sopenharmony_ci		list_for_each_entry(asoc, &sp->ep->asocs, asocs) {
29408c2ecf20Sopenharmony_ci			asoc->default_stream = info->sinfo_stream;
29418c2ecf20Sopenharmony_ci			asoc->default_flags = info->sinfo_flags;
29428c2ecf20Sopenharmony_ci			asoc->default_ppid = info->sinfo_ppid;
29438c2ecf20Sopenharmony_ci			asoc->default_context = info->sinfo_context;
29448c2ecf20Sopenharmony_ci			asoc->default_timetolive = info->sinfo_timetolive;
29458c2ecf20Sopenharmony_ci		}
29468c2ecf20Sopenharmony_ci	}
29478c2ecf20Sopenharmony_ci
29488c2ecf20Sopenharmony_ci	return 0;
29498c2ecf20Sopenharmony_ci}
29508c2ecf20Sopenharmony_ci
29518c2ecf20Sopenharmony_ci/* RFC6458, Section 8.1.31. Set/get Default Send Parameters
29528c2ecf20Sopenharmony_ci * (SCTP_DEFAULT_SNDINFO)
29538c2ecf20Sopenharmony_ci */
29548c2ecf20Sopenharmony_cistatic int sctp_setsockopt_default_sndinfo(struct sock *sk,
29558c2ecf20Sopenharmony_ci					   struct sctp_sndinfo *info,
29568c2ecf20Sopenharmony_ci					   unsigned int optlen)
29578c2ecf20Sopenharmony_ci{
29588c2ecf20Sopenharmony_ci	struct sctp_sock *sp = sctp_sk(sk);
29598c2ecf20Sopenharmony_ci	struct sctp_association *asoc;
29608c2ecf20Sopenharmony_ci
29618c2ecf20Sopenharmony_ci	if (optlen != sizeof(*info))
29628c2ecf20Sopenharmony_ci		return -EINVAL;
29638c2ecf20Sopenharmony_ci	if (info->snd_flags &
29648c2ecf20Sopenharmony_ci	    ~(SCTP_UNORDERED | SCTP_ADDR_OVER |
29658c2ecf20Sopenharmony_ci	      SCTP_ABORT | SCTP_EOF))
29668c2ecf20Sopenharmony_ci		return -EINVAL;
29678c2ecf20Sopenharmony_ci
29688c2ecf20Sopenharmony_ci	asoc = sctp_id2assoc(sk, info->snd_assoc_id);
29698c2ecf20Sopenharmony_ci	if (!asoc && info->snd_assoc_id > SCTP_ALL_ASSOC &&
29708c2ecf20Sopenharmony_ci	    sctp_style(sk, UDP))
29718c2ecf20Sopenharmony_ci		return -EINVAL;
29728c2ecf20Sopenharmony_ci
29738c2ecf20Sopenharmony_ci	if (asoc) {
29748c2ecf20Sopenharmony_ci		asoc->default_stream = info->snd_sid;
29758c2ecf20Sopenharmony_ci		asoc->default_flags = info->snd_flags;
29768c2ecf20Sopenharmony_ci		asoc->default_ppid = info->snd_ppid;
29778c2ecf20Sopenharmony_ci		asoc->default_context = info->snd_context;
29788c2ecf20Sopenharmony_ci
29798c2ecf20Sopenharmony_ci		return 0;
29808c2ecf20Sopenharmony_ci	}
29818c2ecf20Sopenharmony_ci
29828c2ecf20Sopenharmony_ci	if (sctp_style(sk, TCP))
29838c2ecf20Sopenharmony_ci		info->snd_assoc_id = SCTP_FUTURE_ASSOC;
29848c2ecf20Sopenharmony_ci
29858c2ecf20Sopenharmony_ci	if (info->snd_assoc_id == SCTP_FUTURE_ASSOC ||
29868c2ecf20Sopenharmony_ci	    info->snd_assoc_id == SCTP_ALL_ASSOC) {
29878c2ecf20Sopenharmony_ci		sp->default_stream = info->snd_sid;
29888c2ecf20Sopenharmony_ci		sp->default_flags = info->snd_flags;
29898c2ecf20Sopenharmony_ci		sp->default_ppid = info->snd_ppid;
29908c2ecf20Sopenharmony_ci		sp->default_context = info->snd_context;
29918c2ecf20Sopenharmony_ci	}
29928c2ecf20Sopenharmony_ci
29938c2ecf20Sopenharmony_ci	if (info->snd_assoc_id == SCTP_CURRENT_ASSOC ||
29948c2ecf20Sopenharmony_ci	    info->snd_assoc_id == SCTP_ALL_ASSOC) {
29958c2ecf20Sopenharmony_ci		list_for_each_entry(asoc, &sp->ep->asocs, asocs) {
29968c2ecf20Sopenharmony_ci			asoc->default_stream = info->snd_sid;
29978c2ecf20Sopenharmony_ci			asoc->default_flags = info->snd_flags;
29988c2ecf20Sopenharmony_ci			asoc->default_ppid = info->snd_ppid;
29998c2ecf20Sopenharmony_ci			asoc->default_context = info->snd_context;
30008c2ecf20Sopenharmony_ci		}
30018c2ecf20Sopenharmony_ci	}
30028c2ecf20Sopenharmony_ci
30038c2ecf20Sopenharmony_ci	return 0;
30048c2ecf20Sopenharmony_ci}
30058c2ecf20Sopenharmony_ci
30068c2ecf20Sopenharmony_ci/* 7.1.10 Set Primary Address (SCTP_PRIMARY_ADDR)
30078c2ecf20Sopenharmony_ci *
30088c2ecf20Sopenharmony_ci * Requests that the local SCTP stack use the enclosed peer address as
30098c2ecf20Sopenharmony_ci * the association primary.  The enclosed address must be one of the
30108c2ecf20Sopenharmony_ci * association peer's addresses.
30118c2ecf20Sopenharmony_ci */
30128c2ecf20Sopenharmony_cistatic int sctp_setsockopt_primary_addr(struct sock *sk, struct sctp_prim *prim,
30138c2ecf20Sopenharmony_ci					unsigned int optlen)
30148c2ecf20Sopenharmony_ci{
30158c2ecf20Sopenharmony_ci	struct sctp_transport *trans;
30168c2ecf20Sopenharmony_ci	struct sctp_af *af;
30178c2ecf20Sopenharmony_ci	int err;
30188c2ecf20Sopenharmony_ci
30198c2ecf20Sopenharmony_ci	if (optlen != sizeof(struct sctp_prim))
30208c2ecf20Sopenharmony_ci		return -EINVAL;
30218c2ecf20Sopenharmony_ci
30228c2ecf20Sopenharmony_ci	/* Allow security module to validate address but need address len. */
30238c2ecf20Sopenharmony_ci	af = sctp_get_af_specific(prim->ssp_addr.ss_family);
30248c2ecf20Sopenharmony_ci	if (!af)
30258c2ecf20Sopenharmony_ci		return -EINVAL;
30268c2ecf20Sopenharmony_ci
30278c2ecf20Sopenharmony_ci	err = security_sctp_bind_connect(sk, SCTP_PRIMARY_ADDR,
30288c2ecf20Sopenharmony_ci					 (struct sockaddr *)&prim->ssp_addr,
30298c2ecf20Sopenharmony_ci					 af->sockaddr_len);
30308c2ecf20Sopenharmony_ci	if (err)
30318c2ecf20Sopenharmony_ci		return err;
30328c2ecf20Sopenharmony_ci
30338c2ecf20Sopenharmony_ci	trans = sctp_addr_id2transport(sk, &prim->ssp_addr, prim->ssp_assoc_id);
30348c2ecf20Sopenharmony_ci	if (!trans)
30358c2ecf20Sopenharmony_ci		return -EINVAL;
30368c2ecf20Sopenharmony_ci
30378c2ecf20Sopenharmony_ci	sctp_assoc_set_primary(trans->asoc, trans);
30388c2ecf20Sopenharmony_ci
30398c2ecf20Sopenharmony_ci	return 0;
30408c2ecf20Sopenharmony_ci}
30418c2ecf20Sopenharmony_ci
30428c2ecf20Sopenharmony_ci/*
30438c2ecf20Sopenharmony_ci * 7.1.5 SCTP_NODELAY
30448c2ecf20Sopenharmony_ci *
30458c2ecf20Sopenharmony_ci * Turn on/off any Nagle-like algorithm.  This means that packets are
30468c2ecf20Sopenharmony_ci * generally sent as soon as possible and no unnecessary delays are
30478c2ecf20Sopenharmony_ci * introduced, at the cost of more packets in the network.  Expects an
30488c2ecf20Sopenharmony_ci *  integer boolean flag.
30498c2ecf20Sopenharmony_ci */
30508c2ecf20Sopenharmony_cistatic int sctp_setsockopt_nodelay(struct sock *sk, int *val,
30518c2ecf20Sopenharmony_ci				   unsigned int optlen)
30528c2ecf20Sopenharmony_ci{
30538c2ecf20Sopenharmony_ci	if (optlen < sizeof(int))
30548c2ecf20Sopenharmony_ci		return -EINVAL;
30558c2ecf20Sopenharmony_ci	sctp_sk(sk)->nodelay = (*val == 0) ? 0 : 1;
30568c2ecf20Sopenharmony_ci	return 0;
30578c2ecf20Sopenharmony_ci}
30588c2ecf20Sopenharmony_ci
30598c2ecf20Sopenharmony_ci/*
30608c2ecf20Sopenharmony_ci *
30618c2ecf20Sopenharmony_ci * 7.1.1 SCTP_RTOINFO
30628c2ecf20Sopenharmony_ci *
30638c2ecf20Sopenharmony_ci * The protocol parameters used to initialize and bound retransmission
30648c2ecf20Sopenharmony_ci * timeout (RTO) are tunable. sctp_rtoinfo structure is used to access
30658c2ecf20Sopenharmony_ci * and modify these parameters.
30668c2ecf20Sopenharmony_ci * All parameters are time values, in milliseconds.  A value of 0, when
30678c2ecf20Sopenharmony_ci * modifying the parameters, indicates that the current value should not
30688c2ecf20Sopenharmony_ci * be changed.
30698c2ecf20Sopenharmony_ci *
30708c2ecf20Sopenharmony_ci */
30718c2ecf20Sopenharmony_cistatic int sctp_setsockopt_rtoinfo(struct sock *sk,
30728c2ecf20Sopenharmony_ci				   struct sctp_rtoinfo *rtoinfo,
30738c2ecf20Sopenharmony_ci				   unsigned int optlen)
30748c2ecf20Sopenharmony_ci{
30758c2ecf20Sopenharmony_ci	struct sctp_association *asoc;
30768c2ecf20Sopenharmony_ci	unsigned long rto_min, rto_max;
30778c2ecf20Sopenharmony_ci	struct sctp_sock *sp = sctp_sk(sk);
30788c2ecf20Sopenharmony_ci
30798c2ecf20Sopenharmony_ci	if (optlen != sizeof (struct sctp_rtoinfo))
30808c2ecf20Sopenharmony_ci		return -EINVAL;
30818c2ecf20Sopenharmony_ci
30828c2ecf20Sopenharmony_ci	asoc = sctp_id2assoc(sk, rtoinfo->srto_assoc_id);
30838c2ecf20Sopenharmony_ci
30848c2ecf20Sopenharmony_ci	/* Set the values to the specific association */
30858c2ecf20Sopenharmony_ci	if (!asoc && rtoinfo->srto_assoc_id != SCTP_FUTURE_ASSOC &&
30868c2ecf20Sopenharmony_ci	    sctp_style(sk, UDP))
30878c2ecf20Sopenharmony_ci		return -EINVAL;
30888c2ecf20Sopenharmony_ci
30898c2ecf20Sopenharmony_ci	rto_max = rtoinfo->srto_max;
30908c2ecf20Sopenharmony_ci	rto_min = rtoinfo->srto_min;
30918c2ecf20Sopenharmony_ci
30928c2ecf20Sopenharmony_ci	if (rto_max)
30938c2ecf20Sopenharmony_ci		rto_max = asoc ? msecs_to_jiffies(rto_max) : rto_max;
30948c2ecf20Sopenharmony_ci	else
30958c2ecf20Sopenharmony_ci		rto_max = asoc ? asoc->rto_max : sp->rtoinfo.srto_max;
30968c2ecf20Sopenharmony_ci
30978c2ecf20Sopenharmony_ci	if (rto_min)
30988c2ecf20Sopenharmony_ci		rto_min = asoc ? msecs_to_jiffies(rto_min) : rto_min;
30998c2ecf20Sopenharmony_ci	else
31008c2ecf20Sopenharmony_ci		rto_min = asoc ? asoc->rto_min : sp->rtoinfo.srto_min;
31018c2ecf20Sopenharmony_ci
31028c2ecf20Sopenharmony_ci	if (rto_min > rto_max)
31038c2ecf20Sopenharmony_ci		return -EINVAL;
31048c2ecf20Sopenharmony_ci
31058c2ecf20Sopenharmony_ci	if (asoc) {
31068c2ecf20Sopenharmony_ci		if (rtoinfo->srto_initial != 0)
31078c2ecf20Sopenharmony_ci			asoc->rto_initial =
31088c2ecf20Sopenharmony_ci				msecs_to_jiffies(rtoinfo->srto_initial);
31098c2ecf20Sopenharmony_ci		asoc->rto_max = rto_max;
31108c2ecf20Sopenharmony_ci		asoc->rto_min = rto_min;
31118c2ecf20Sopenharmony_ci	} else {
31128c2ecf20Sopenharmony_ci		/* If there is no association or the association-id = 0
31138c2ecf20Sopenharmony_ci		 * set the values to the endpoint.
31148c2ecf20Sopenharmony_ci		 */
31158c2ecf20Sopenharmony_ci		if (rtoinfo->srto_initial != 0)
31168c2ecf20Sopenharmony_ci			sp->rtoinfo.srto_initial = rtoinfo->srto_initial;
31178c2ecf20Sopenharmony_ci		sp->rtoinfo.srto_max = rto_max;
31188c2ecf20Sopenharmony_ci		sp->rtoinfo.srto_min = rto_min;
31198c2ecf20Sopenharmony_ci	}
31208c2ecf20Sopenharmony_ci
31218c2ecf20Sopenharmony_ci	return 0;
31228c2ecf20Sopenharmony_ci}
31238c2ecf20Sopenharmony_ci
31248c2ecf20Sopenharmony_ci/*
31258c2ecf20Sopenharmony_ci *
31268c2ecf20Sopenharmony_ci * 7.1.2 SCTP_ASSOCINFO
31278c2ecf20Sopenharmony_ci *
31288c2ecf20Sopenharmony_ci * This option is used to tune the maximum retransmission attempts
31298c2ecf20Sopenharmony_ci * of the association.
31308c2ecf20Sopenharmony_ci * Returns an error if the new association retransmission value is
31318c2ecf20Sopenharmony_ci * greater than the sum of the retransmission value  of the peer.
31328c2ecf20Sopenharmony_ci * See [SCTP] for more information.
31338c2ecf20Sopenharmony_ci *
31348c2ecf20Sopenharmony_ci */
31358c2ecf20Sopenharmony_cistatic int sctp_setsockopt_associnfo(struct sock *sk,
31368c2ecf20Sopenharmony_ci				     struct sctp_assocparams *assocparams,
31378c2ecf20Sopenharmony_ci				     unsigned int optlen)
31388c2ecf20Sopenharmony_ci{
31398c2ecf20Sopenharmony_ci
31408c2ecf20Sopenharmony_ci	struct sctp_association *asoc;
31418c2ecf20Sopenharmony_ci
31428c2ecf20Sopenharmony_ci	if (optlen != sizeof(struct sctp_assocparams))
31438c2ecf20Sopenharmony_ci		return -EINVAL;
31448c2ecf20Sopenharmony_ci
31458c2ecf20Sopenharmony_ci	asoc = sctp_id2assoc(sk, assocparams->sasoc_assoc_id);
31468c2ecf20Sopenharmony_ci
31478c2ecf20Sopenharmony_ci	if (!asoc && assocparams->sasoc_assoc_id != SCTP_FUTURE_ASSOC &&
31488c2ecf20Sopenharmony_ci	    sctp_style(sk, UDP))
31498c2ecf20Sopenharmony_ci		return -EINVAL;
31508c2ecf20Sopenharmony_ci
31518c2ecf20Sopenharmony_ci	/* Set the values to the specific association */
31528c2ecf20Sopenharmony_ci	if (asoc) {
31538c2ecf20Sopenharmony_ci		if (assocparams->sasoc_asocmaxrxt != 0) {
31548c2ecf20Sopenharmony_ci			__u32 path_sum = 0;
31558c2ecf20Sopenharmony_ci			int   paths = 0;
31568c2ecf20Sopenharmony_ci			struct sctp_transport *peer_addr;
31578c2ecf20Sopenharmony_ci
31588c2ecf20Sopenharmony_ci			list_for_each_entry(peer_addr, &asoc->peer.transport_addr_list,
31598c2ecf20Sopenharmony_ci					transports) {
31608c2ecf20Sopenharmony_ci				path_sum += peer_addr->pathmaxrxt;
31618c2ecf20Sopenharmony_ci				paths++;
31628c2ecf20Sopenharmony_ci			}
31638c2ecf20Sopenharmony_ci
31648c2ecf20Sopenharmony_ci			/* Only validate asocmaxrxt if we have more than
31658c2ecf20Sopenharmony_ci			 * one path/transport.  We do this because path
31668c2ecf20Sopenharmony_ci			 * retransmissions are only counted when we have more
31678c2ecf20Sopenharmony_ci			 * then one path.
31688c2ecf20Sopenharmony_ci			 */
31698c2ecf20Sopenharmony_ci			if (paths > 1 &&
31708c2ecf20Sopenharmony_ci			    assocparams->sasoc_asocmaxrxt > path_sum)
31718c2ecf20Sopenharmony_ci				return -EINVAL;
31728c2ecf20Sopenharmony_ci
31738c2ecf20Sopenharmony_ci			asoc->max_retrans = assocparams->sasoc_asocmaxrxt;
31748c2ecf20Sopenharmony_ci		}
31758c2ecf20Sopenharmony_ci
31768c2ecf20Sopenharmony_ci		if (assocparams->sasoc_cookie_life != 0)
31778c2ecf20Sopenharmony_ci			asoc->cookie_life =
31788c2ecf20Sopenharmony_ci				ms_to_ktime(assocparams->sasoc_cookie_life);
31798c2ecf20Sopenharmony_ci	} else {
31808c2ecf20Sopenharmony_ci		/* Set the values to the endpoint */
31818c2ecf20Sopenharmony_ci		struct sctp_sock *sp = sctp_sk(sk);
31828c2ecf20Sopenharmony_ci
31838c2ecf20Sopenharmony_ci		if (assocparams->sasoc_asocmaxrxt != 0)
31848c2ecf20Sopenharmony_ci			sp->assocparams.sasoc_asocmaxrxt =
31858c2ecf20Sopenharmony_ci						assocparams->sasoc_asocmaxrxt;
31868c2ecf20Sopenharmony_ci		if (assocparams->sasoc_cookie_life != 0)
31878c2ecf20Sopenharmony_ci			sp->assocparams.sasoc_cookie_life =
31888c2ecf20Sopenharmony_ci						assocparams->sasoc_cookie_life;
31898c2ecf20Sopenharmony_ci	}
31908c2ecf20Sopenharmony_ci	return 0;
31918c2ecf20Sopenharmony_ci}
31928c2ecf20Sopenharmony_ci
31938c2ecf20Sopenharmony_ci/*
31948c2ecf20Sopenharmony_ci * 7.1.16 Set/clear IPv4 mapped addresses (SCTP_I_WANT_MAPPED_V4_ADDR)
31958c2ecf20Sopenharmony_ci *
31968c2ecf20Sopenharmony_ci * This socket option is a boolean flag which turns on or off mapped V4
31978c2ecf20Sopenharmony_ci * addresses.  If this option is turned on and the socket is type
31988c2ecf20Sopenharmony_ci * PF_INET6, then IPv4 addresses will be mapped to V6 representation.
31998c2ecf20Sopenharmony_ci * If this option is turned off, then no mapping will be done of V4
32008c2ecf20Sopenharmony_ci * addresses and a user will receive both PF_INET6 and PF_INET type
32018c2ecf20Sopenharmony_ci * addresses on the socket.
32028c2ecf20Sopenharmony_ci */
32038c2ecf20Sopenharmony_cistatic int sctp_setsockopt_mappedv4(struct sock *sk, int *val,
32048c2ecf20Sopenharmony_ci				    unsigned int optlen)
32058c2ecf20Sopenharmony_ci{
32068c2ecf20Sopenharmony_ci	struct sctp_sock *sp = sctp_sk(sk);
32078c2ecf20Sopenharmony_ci
32088c2ecf20Sopenharmony_ci	if (optlen < sizeof(int))
32098c2ecf20Sopenharmony_ci		return -EINVAL;
32108c2ecf20Sopenharmony_ci	if (*val)
32118c2ecf20Sopenharmony_ci		sp->v4mapped = 1;
32128c2ecf20Sopenharmony_ci	else
32138c2ecf20Sopenharmony_ci		sp->v4mapped = 0;
32148c2ecf20Sopenharmony_ci
32158c2ecf20Sopenharmony_ci	return 0;
32168c2ecf20Sopenharmony_ci}
32178c2ecf20Sopenharmony_ci
32188c2ecf20Sopenharmony_ci/*
32198c2ecf20Sopenharmony_ci * 8.1.16.  Get or Set the Maximum Fragmentation Size (SCTP_MAXSEG)
32208c2ecf20Sopenharmony_ci * This option will get or set the maximum size to put in any outgoing
32218c2ecf20Sopenharmony_ci * SCTP DATA chunk.  If a message is larger than this size it will be
32228c2ecf20Sopenharmony_ci * fragmented by SCTP into the specified size.  Note that the underlying
32238c2ecf20Sopenharmony_ci * SCTP implementation may fragment into smaller sized chunks when the
32248c2ecf20Sopenharmony_ci * PMTU of the underlying association is smaller than the value set by
32258c2ecf20Sopenharmony_ci * the user.  The default value for this option is '0' which indicates
32268c2ecf20Sopenharmony_ci * the user is NOT limiting fragmentation and only the PMTU will effect
32278c2ecf20Sopenharmony_ci * SCTP's choice of DATA chunk size.  Note also that values set larger
32288c2ecf20Sopenharmony_ci * than the maximum size of an IP datagram will effectively let SCTP
32298c2ecf20Sopenharmony_ci * control fragmentation (i.e. the same as setting this option to 0).
32308c2ecf20Sopenharmony_ci *
32318c2ecf20Sopenharmony_ci * The following structure is used to access and modify this parameter:
32328c2ecf20Sopenharmony_ci *
32338c2ecf20Sopenharmony_ci * struct sctp_assoc_value {
32348c2ecf20Sopenharmony_ci *   sctp_assoc_t assoc_id;
32358c2ecf20Sopenharmony_ci *   uint32_t assoc_value;
32368c2ecf20Sopenharmony_ci * };
32378c2ecf20Sopenharmony_ci *
32388c2ecf20Sopenharmony_ci * assoc_id:  This parameter is ignored for one-to-one style sockets.
32398c2ecf20Sopenharmony_ci *    For one-to-many style sockets this parameter indicates which
32408c2ecf20Sopenharmony_ci *    association the user is performing an action upon.  Note that if
32418c2ecf20Sopenharmony_ci *    this field's value is zero then the endpoints default value is
32428c2ecf20Sopenharmony_ci *    changed (effecting future associations only).
32438c2ecf20Sopenharmony_ci * assoc_value:  This parameter specifies the maximum size in bytes.
32448c2ecf20Sopenharmony_ci */
32458c2ecf20Sopenharmony_cistatic int sctp_setsockopt_maxseg(struct sock *sk,
32468c2ecf20Sopenharmony_ci				  struct sctp_assoc_value *params,
32478c2ecf20Sopenharmony_ci				  unsigned int optlen)
32488c2ecf20Sopenharmony_ci{
32498c2ecf20Sopenharmony_ci	struct sctp_sock *sp = sctp_sk(sk);
32508c2ecf20Sopenharmony_ci	struct sctp_association *asoc;
32518c2ecf20Sopenharmony_ci	sctp_assoc_t assoc_id;
32528c2ecf20Sopenharmony_ci	int val;
32538c2ecf20Sopenharmony_ci
32548c2ecf20Sopenharmony_ci	if (optlen == sizeof(int)) {
32558c2ecf20Sopenharmony_ci		pr_warn_ratelimited(DEPRECATED
32568c2ecf20Sopenharmony_ci				    "%s (pid %d) "
32578c2ecf20Sopenharmony_ci				    "Use of int in maxseg socket option.\n"
32588c2ecf20Sopenharmony_ci				    "Use struct sctp_assoc_value instead\n",
32598c2ecf20Sopenharmony_ci				    current->comm, task_pid_nr(current));
32608c2ecf20Sopenharmony_ci		assoc_id = SCTP_FUTURE_ASSOC;
32618c2ecf20Sopenharmony_ci		val = *(int *)params;
32628c2ecf20Sopenharmony_ci	} else if (optlen == sizeof(struct sctp_assoc_value)) {
32638c2ecf20Sopenharmony_ci		assoc_id = params->assoc_id;
32648c2ecf20Sopenharmony_ci		val = params->assoc_value;
32658c2ecf20Sopenharmony_ci	} else {
32668c2ecf20Sopenharmony_ci		return -EINVAL;
32678c2ecf20Sopenharmony_ci	}
32688c2ecf20Sopenharmony_ci
32698c2ecf20Sopenharmony_ci	asoc = sctp_id2assoc(sk, assoc_id);
32708c2ecf20Sopenharmony_ci	if (!asoc && assoc_id != SCTP_FUTURE_ASSOC &&
32718c2ecf20Sopenharmony_ci	    sctp_style(sk, UDP))
32728c2ecf20Sopenharmony_ci		return -EINVAL;
32738c2ecf20Sopenharmony_ci
32748c2ecf20Sopenharmony_ci	if (val) {
32758c2ecf20Sopenharmony_ci		int min_len, max_len;
32768c2ecf20Sopenharmony_ci		__u16 datasize = asoc ? sctp_datachk_len(&asoc->stream) :
32778c2ecf20Sopenharmony_ci				 sizeof(struct sctp_data_chunk);
32788c2ecf20Sopenharmony_ci
32798c2ecf20Sopenharmony_ci		min_len = sctp_min_frag_point(sp, datasize);
32808c2ecf20Sopenharmony_ci		max_len = SCTP_MAX_CHUNK_LEN - datasize;
32818c2ecf20Sopenharmony_ci
32828c2ecf20Sopenharmony_ci		if (val < min_len || val > max_len)
32838c2ecf20Sopenharmony_ci			return -EINVAL;
32848c2ecf20Sopenharmony_ci	}
32858c2ecf20Sopenharmony_ci
32868c2ecf20Sopenharmony_ci	if (asoc) {
32878c2ecf20Sopenharmony_ci		asoc->user_frag = val;
32888c2ecf20Sopenharmony_ci		sctp_assoc_update_frag_point(asoc);
32898c2ecf20Sopenharmony_ci	} else {
32908c2ecf20Sopenharmony_ci		sp->user_frag = val;
32918c2ecf20Sopenharmony_ci	}
32928c2ecf20Sopenharmony_ci
32938c2ecf20Sopenharmony_ci	return 0;
32948c2ecf20Sopenharmony_ci}
32958c2ecf20Sopenharmony_ci
32968c2ecf20Sopenharmony_ci
32978c2ecf20Sopenharmony_ci/*
32988c2ecf20Sopenharmony_ci *  7.1.9 Set Peer Primary Address (SCTP_SET_PEER_PRIMARY_ADDR)
32998c2ecf20Sopenharmony_ci *
33008c2ecf20Sopenharmony_ci *   Requests that the peer mark the enclosed address as the association
33018c2ecf20Sopenharmony_ci *   primary. The enclosed address must be one of the association's
33028c2ecf20Sopenharmony_ci *   locally bound addresses. The following structure is used to make a
33038c2ecf20Sopenharmony_ci *   set primary request:
33048c2ecf20Sopenharmony_ci */
33058c2ecf20Sopenharmony_cistatic int sctp_setsockopt_peer_primary_addr(struct sock *sk,
33068c2ecf20Sopenharmony_ci					     struct sctp_setpeerprim *prim,
33078c2ecf20Sopenharmony_ci					     unsigned int optlen)
33088c2ecf20Sopenharmony_ci{
33098c2ecf20Sopenharmony_ci	struct sctp_sock	*sp;
33108c2ecf20Sopenharmony_ci	struct sctp_association	*asoc = NULL;
33118c2ecf20Sopenharmony_ci	struct sctp_chunk	*chunk;
33128c2ecf20Sopenharmony_ci	struct sctp_af		*af;
33138c2ecf20Sopenharmony_ci	int 			err;
33148c2ecf20Sopenharmony_ci
33158c2ecf20Sopenharmony_ci	sp = sctp_sk(sk);
33168c2ecf20Sopenharmony_ci
33178c2ecf20Sopenharmony_ci	if (!sp->ep->asconf_enable)
33188c2ecf20Sopenharmony_ci		return -EPERM;
33198c2ecf20Sopenharmony_ci
33208c2ecf20Sopenharmony_ci	if (optlen != sizeof(struct sctp_setpeerprim))
33218c2ecf20Sopenharmony_ci		return -EINVAL;
33228c2ecf20Sopenharmony_ci
33238c2ecf20Sopenharmony_ci	asoc = sctp_id2assoc(sk, prim->sspp_assoc_id);
33248c2ecf20Sopenharmony_ci	if (!asoc)
33258c2ecf20Sopenharmony_ci		return -EINVAL;
33268c2ecf20Sopenharmony_ci
33278c2ecf20Sopenharmony_ci	if (!asoc->peer.asconf_capable)
33288c2ecf20Sopenharmony_ci		return -EPERM;
33298c2ecf20Sopenharmony_ci
33308c2ecf20Sopenharmony_ci	if (asoc->peer.addip_disabled_mask & SCTP_PARAM_SET_PRIMARY)
33318c2ecf20Sopenharmony_ci		return -EPERM;
33328c2ecf20Sopenharmony_ci
33338c2ecf20Sopenharmony_ci	if (!sctp_state(asoc, ESTABLISHED))
33348c2ecf20Sopenharmony_ci		return -ENOTCONN;
33358c2ecf20Sopenharmony_ci
33368c2ecf20Sopenharmony_ci	af = sctp_get_af_specific(prim->sspp_addr.ss_family);
33378c2ecf20Sopenharmony_ci	if (!af)
33388c2ecf20Sopenharmony_ci		return -EINVAL;
33398c2ecf20Sopenharmony_ci
33408c2ecf20Sopenharmony_ci	if (!af->addr_valid((union sctp_addr *)&prim->sspp_addr, sp, NULL))
33418c2ecf20Sopenharmony_ci		return -EADDRNOTAVAIL;
33428c2ecf20Sopenharmony_ci
33438c2ecf20Sopenharmony_ci	if (!sctp_assoc_lookup_laddr(asoc, (union sctp_addr *)&prim->sspp_addr))
33448c2ecf20Sopenharmony_ci		return -EADDRNOTAVAIL;
33458c2ecf20Sopenharmony_ci
33468c2ecf20Sopenharmony_ci	/* Allow security module to validate address. */
33478c2ecf20Sopenharmony_ci	err = security_sctp_bind_connect(sk, SCTP_SET_PEER_PRIMARY_ADDR,
33488c2ecf20Sopenharmony_ci					 (struct sockaddr *)&prim->sspp_addr,
33498c2ecf20Sopenharmony_ci					 af->sockaddr_len);
33508c2ecf20Sopenharmony_ci	if (err)
33518c2ecf20Sopenharmony_ci		return err;
33528c2ecf20Sopenharmony_ci
33538c2ecf20Sopenharmony_ci	/* Create an ASCONF chunk with SET_PRIMARY parameter	*/
33548c2ecf20Sopenharmony_ci	chunk = sctp_make_asconf_set_prim(asoc,
33558c2ecf20Sopenharmony_ci					  (union sctp_addr *)&prim->sspp_addr);
33568c2ecf20Sopenharmony_ci	if (!chunk)
33578c2ecf20Sopenharmony_ci		return -ENOMEM;
33588c2ecf20Sopenharmony_ci
33598c2ecf20Sopenharmony_ci	err = sctp_send_asconf(asoc, chunk);
33608c2ecf20Sopenharmony_ci
33618c2ecf20Sopenharmony_ci	pr_debug("%s: we set peer primary addr primitively\n", __func__);
33628c2ecf20Sopenharmony_ci
33638c2ecf20Sopenharmony_ci	return err;
33648c2ecf20Sopenharmony_ci}
33658c2ecf20Sopenharmony_ci
33668c2ecf20Sopenharmony_cistatic int sctp_setsockopt_adaptation_layer(struct sock *sk,
33678c2ecf20Sopenharmony_ci					    struct sctp_setadaptation *adapt,
33688c2ecf20Sopenharmony_ci					    unsigned int optlen)
33698c2ecf20Sopenharmony_ci{
33708c2ecf20Sopenharmony_ci	if (optlen != sizeof(struct sctp_setadaptation))
33718c2ecf20Sopenharmony_ci		return -EINVAL;
33728c2ecf20Sopenharmony_ci
33738c2ecf20Sopenharmony_ci	sctp_sk(sk)->adaptation_ind = adapt->ssb_adaptation_ind;
33748c2ecf20Sopenharmony_ci
33758c2ecf20Sopenharmony_ci	return 0;
33768c2ecf20Sopenharmony_ci}
33778c2ecf20Sopenharmony_ci
33788c2ecf20Sopenharmony_ci/*
33798c2ecf20Sopenharmony_ci * 7.1.29.  Set or Get the default context (SCTP_CONTEXT)
33808c2ecf20Sopenharmony_ci *
33818c2ecf20Sopenharmony_ci * The context field in the sctp_sndrcvinfo structure is normally only
33828c2ecf20Sopenharmony_ci * used when a failed message is retrieved holding the value that was
33838c2ecf20Sopenharmony_ci * sent down on the actual send call.  This option allows the setting of
33848c2ecf20Sopenharmony_ci * a default context on an association basis that will be received on
33858c2ecf20Sopenharmony_ci * reading messages from the peer.  This is especially helpful in the
33868c2ecf20Sopenharmony_ci * one-2-many model for an application to keep some reference to an
33878c2ecf20Sopenharmony_ci * internal state machine that is processing messages on the
33888c2ecf20Sopenharmony_ci * association.  Note that the setting of this value only effects
33898c2ecf20Sopenharmony_ci * received messages from the peer and does not effect the value that is
33908c2ecf20Sopenharmony_ci * saved with outbound messages.
33918c2ecf20Sopenharmony_ci */
33928c2ecf20Sopenharmony_cistatic int sctp_setsockopt_context(struct sock *sk,
33938c2ecf20Sopenharmony_ci				   struct sctp_assoc_value *params,
33948c2ecf20Sopenharmony_ci				   unsigned int optlen)
33958c2ecf20Sopenharmony_ci{
33968c2ecf20Sopenharmony_ci	struct sctp_sock *sp = sctp_sk(sk);
33978c2ecf20Sopenharmony_ci	struct sctp_association *asoc;
33988c2ecf20Sopenharmony_ci
33998c2ecf20Sopenharmony_ci	if (optlen != sizeof(struct sctp_assoc_value))
34008c2ecf20Sopenharmony_ci		return -EINVAL;
34018c2ecf20Sopenharmony_ci
34028c2ecf20Sopenharmony_ci	asoc = sctp_id2assoc(sk, params->assoc_id);
34038c2ecf20Sopenharmony_ci	if (!asoc && params->assoc_id > SCTP_ALL_ASSOC &&
34048c2ecf20Sopenharmony_ci	    sctp_style(sk, UDP))
34058c2ecf20Sopenharmony_ci		return -EINVAL;
34068c2ecf20Sopenharmony_ci
34078c2ecf20Sopenharmony_ci	if (asoc) {
34088c2ecf20Sopenharmony_ci		asoc->default_rcv_context = params->assoc_value;
34098c2ecf20Sopenharmony_ci
34108c2ecf20Sopenharmony_ci		return 0;
34118c2ecf20Sopenharmony_ci	}
34128c2ecf20Sopenharmony_ci
34138c2ecf20Sopenharmony_ci	if (sctp_style(sk, TCP))
34148c2ecf20Sopenharmony_ci		params->assoc_id = SCTP_FUTURE_ASSOC;
34158c2ecf20Sopenharmony_ci
34168c2ecf20Sopenharmony_ci	if (params->assoc_id == SCTP_FUTURE_ASSOC ||
34178c2ecf20Sopenharmony_ci	    params->assoc_id == SCTP_ALL_ASSOC)
34188c2ecf20Sopenharmony_ci		sp->default_rcv_context = params->assoc_value;
34198c2ecf20Sopenharmony_ci
34208c2ecf20Sopenharmony_ci	if (params->assoc_id == SCTP_CURRENT_ASSOC ||
34218c2ecf20Sopenharmony_ci	    params->assoc_id == SCTP_ALL_ASSOC)
34228c2ecf20Sopenharmony_ci		list_for_each_entry(asoc, &sp->ep->asocs, asocs)
34238c2ecf20Sopenharmony_ci			asoc->default_rcv_context = params->assoc_value;
34248c2ecf20Sopenharmony_ci
34258c2ecf20Sopenharmony_ci	return 0;
34268c2ecf20Sopenharmony_ci}
34278c2ecf20Sopenharmony_ci
34288c2ecf20Sopenharmony_ci/*
34298c2ecf20Sopenharmony_ci * 7.1.24.  Get or set fragmented interleave (SCTP_FRAGMENT_INTERLEAVE)
34308c2ecf20Sopenharmony_ci *
34318c2ecf20Sopenharmony_ci * This options will at a minimum specify if the implementation is doing
34328c2ecf20Sopenharmony_ci * fragmented interleave.  Fragmented interleave, for a one to many
34338c2ecf20Sopenharmony_ci * socket, is when subsequent calls to receive a message may return
34348c2ecf20Sopenharmony_ci * parts of messages from different associations.  Some implementations
34358c2ecf20Sopenharmony_ci * may allow you to turn this value on or off.  If so, when turned off,
34368c2ecf20Sopenharmony_ci * no fragment interleave will occur (which will cause a head of line
34378c2ecf20Sopenharmony_ci * blocking amongst multiple associations sharing the same one to many
34388c2ecf20Sopenharmony_ci * socket).  When this option is turned on, then each receive call may
34398c2ecf20Sopenharmony_ci * come from a different association (thus the user must receive data
34408c2ecf20Sopenharmony_ci * with the extended calls (e.g. sctp_recvmsg) to keep track of which
34418c2ecf20Sopenharmony_ci * association each receive belongs to.
34428c2ecf20Sopenharmony_ci *
34438c2ecf20Sopenharmony_ci * This option takes a boolean value.  A non-zero value indicates that
34448c2ecf20Sopenharmony_ci * fragmented interleave is on.  A value of zero indicates that
34458c2ecf20Sopenharmony_ci * fragmented interleave is off.
34468c2ecf20Sopenharmony_ci *
34478c2ecf20Sopenharmony_ci * Note that it is important that an implementation that allows this
34488c2ecf20Sopenharmony_ci * option to be turned on, have it off by default.  Otherwise an unaware
34498c2ecf20Sopenharmony_ci * application using the one to many model may become confused and act
34508c2ecf20Sopenharmony_ci * incorrectly.
34518c2ecf20Sopenharmony_ci */
34528c2ecf20Sopenharmony_cistatic int sctp_setsockopt_fragment_interleave(struct sock *sk, int *val,
34538c2ecf20Sopenharmony_ci					       unsigned int optlen)
34548c2ecf20Sopenharmony_ci{
34558c2ecf20Sopenharmony_ci	if (optlen != sizeof(int))
34568c2ecf20Sopenharmony_ci		return -EINVAL;
34578c2ecf20Sopenharmony_ci
34588c2ecf20Sopenharmony_ci	sctp_sk(sk)->frag_interleave = !!*val;
34598c2ecf20Sopenharmony_ci
34608c2ecf20Sopenharmony_ci	if (!sctp_sk(sk)->frag_interleave)
34618c2ecf20Sopenharmony_ci		sctp_sk(sk)->ep->intl_enable = 0;
34628c2ecf20Sopenharmony_ci
34638c2ecf20Sopenharmony_ci	return 0;
34648c2ecf20Sopenharmony_ci}
34658c2ecf20Sopenharmony_ci
34668c2ecf20Sopenharmony_ci/*
34678c2ecf20Sopenharmony_ci * 8.1.21.  Set or Get the SCTP Partial Delivery Point
34688c2ecf20Sopenharmony_ci *       (SCTP_PARTIAL_DELIVERY_POINT)
34698c2ecf20Sopenharmony_ci *
34708c2ecf20Sopenharmony_ci * This option will set or get the SCTP partial delivery point.  This
34718c2ecf20Sopenharmony_ci * point is the size of a message where the partial delivery API will be
34728c2ecf20Sopenharmony_ci * invoked to help free up rwnd space for the peer.  Setting this to a
34738c2ecf20Sopenharmony_ci * lower value will cause partial deliveries to happen more often.  The
34748c2ecf20Sopenharmony_ci * calls argument is an integer that sets or gets the partial delivery
34758c2ecf20Sopenharmony_ci * point.  Note also that the call will fail if the user attempts to set
34768c2ecf20Sopenharmony_ci * this value larger than the socket receive buffer size.
34778c2ecf20Sopenharmony_ci *
34788c2ecf20Sopenharmony_ci * Note that any single message having a length smaller than or equal to
34798c2ecf20Sopenharmony_ci * the SCTP partial delivery point will be delivered in one single read
34808c2ecf20Sopenharmony_ci * call as long as the user provided buffer is large enough to hold the
34818c2ecf20Sopenharmony_ci * message.
34828c2ecf20Sopenharmony_ci */
34838c2ecf20Sopenharmony_cistatic int sctp_setsockopt_partial_delivery_point(struct sock *sk, u32 *val,
34848c2ecf20Sopenharmony_ci						  unsigned int optlen)
34858c2ecf20Sopenharmony_ci{
34868c2ecf20Sopenharmony_ci	if (optlen != sizeof(u32))
34878c2ecf20Sopenharmony_ci		return -EINVAL;
34888c2ecf20Sopenharmony_ci
34898c2ecf20Sopenharmony_ci	/* Note: We double the receive buffer from what the user sets
34908c2ecf20Sopenharmony_ci	 * it to be, also initial rwnd is based on rcvbuf/2.
34918c2ecf20Sopenharmony_ci	 */
34928c2ecf20Sopenharmony_ci	if (*val > (sk->sk_rcvbuf >> 1))
34938c2ecf20Sopenharmony_ci		return -EINVAL;
34948c2ecf20Sopenharmony_ci
34958c2ecf20Sopenharmony_ci	sctp_sk(sk)->pd_point = *val;
34968c2ecf20Sopenharmony_ci
34978c2ecf20Sopenharmony_ci	return 0; /* is this the right error code? */
34988c2ecf20Sopenharmony_ci}
34998c2ecf20Sopenharmony_ci
35008c2ecf20Sopenharmony_ci/*
35018c2ecf20Sopenharmony_ci * 7.1.28.  Set or Get the maximum burst (SCTP_MAX_BURST)
35028c2ecf20Sopenharmony_ci *
35038c2ecf20Sopenharmony_ci * This option will allow a user to change the maximum burst of packets
35048c2ecf20Sopenharmony_ci * that can be emitted by this association.  Note that the default value
35058c2ecf20Sopenharmony_ci * is 4, and some implementations may restrict this setting so that it
35068c2ecf20Sopenharmony_ci * can only be lowered.
35078c2ecf20Sopenharmony_ci *
35088c2ecf20Sopenharmony_ci * NOTE: This text doesn't seem right.  Do this on a socket basis with
35098c2ecf20Sopenharmony_ci * future associations inheriting the socket value.
35108c2ecf20Sopenharmony_ci */
35118c2ecf20Sopenharmony_cistatic int sctp_setsockopt_maxburst(struct sock *sk,
35128c2ecf20Sopenharmony_ci				    struct sctp_assoc_value *params,
35138c2ecf20Sopenharmony_ci				    unsigned int optlen)
35148c2ecf20Sopenharmony_ci{
35158c2ecf20Sopenharmony_ci	struct sctp_sock *sp = sctp_sk(sk);
35168c2ecf20Sopenharmony_ci	struct sctp_association *asoc;
35178c2ecf20Sopenharmony_ci	sctp_assoc_t assoc_id;
35188c2ecf20Sopenharmony_ci	u32 assoc_value;
35198c2ecf20Sopenharmony_ci
35208c2ecf20Sopenharmony_ci	if (optlen == sizeof(int)) {
35218c2ecf20Sopenharmony_ci		pr_warn_ratelimited(DEPRECATED
35228c2ecf20Sopenharmony_ci				    "%s (pid %d) "
35238c2ecf20Sopenharmony_ci				    "Use of int in max_burst socket option deprecated.\n"
35248c2ecf20Sopenharmony_ci				    "Use struct sctp_assoc_value instead\n",
35258c2ecf20Sopenharmony_ci				    current->comm, task_pid_nr(current));
35268c2ecf20Sopenharmony_ci		assoc_id = SCTP_FUTURE_ASSOC;
35278c2ecf20Sopenharmony_ci		assoc_value = *((int *)params);
35288c2ecf20Sopenharmony_ci	} else if (optlen == sizeof(struct sctp_assoc_value)) {
35298c2ecf20Sopenharmony_ci		assoc_id = params->assoc_id;
35308c2ecf20Sopenharmony_ci		assoc_value = params->assoc_value;
35318c2ecf20Sopenharmony_ci	} else
35328c2ecf20Sopenharmony_ci		return -EINVAL;
35338c2ecf20Sopenharmony_ci
35348c2ecf20Sopenharmony_ci	asoc = sctp_id2assoc(sk, assoc_id);
35358c2ecf20Sopenharmony_ci	if (!asoc && assoc_id > SCTP_ALL_ASSOC && sctp_style(sk, UDP))
35368c2ecf20Sopenharmony_ci		return -EINVAL;
35378c2ecf20Sopenharmony_ci
35388c2ecf20Sopenharmony_ci	if (asoc) {
35398c2ecf20Sopenharmony_ci		asoc->max_burst = assoc_value;
35408c2ecf20Sopenharmony_ci
35418c2ecf20Sopenharmony_ci		return 0;
35428c2ecf20Sopenharmony_ci	}
35438c2ecf20Sopenharmony_ci
35448c2ecf20Sopenharmony_ci	if (sctp_style(sk, TCP))
35458c2ecf20Sopenharmony_ci		assoc_id = SCTP_FUTURE_ASSOC;
35468c2ecf20Sopenharmony_ci
35478c2ecf20Sopenharmony_ci	if (assoc_id == SCTP_FUTURE_ASSOC || assoc_id == SCTP_ALL_ASSOC)
35488c2ecf20Sopenharmony_ci		sp->max_burst = assoc_value;
35498c2ecf20Sopenharmony_ci
35508c2ecf20Sopenharmony_ci	if (assoc_id == SCTP_CURRENT_ASSOC || assoc_id == SCTP_ALL_ASSOC)
35518c2ecf20Sopenharmony_ci		list_for_each_entry(asoc, &sp->ep->asocs, asocs)
35528c2ecf20Sopenharmony_ci			asoc->max_burst = assoc_value;
35538c2ecf20Sopenharmony_ci
35548c2ecf20Sopenharmony_ci	return 0;
35558c2ecf20Sopenharmony_ci}
35568c2ecf20Sopenharmony_ci
35578c2ecf20Sopenharmony_ci/*
35588c2ecf20Sopenharmony_ci * 7.1.18.  Add a chunk that must be authenticated (SCTP_AUTH_CHUNK)
35598c2ecf20Sopenharmony_ci *
35608c2ecf20Sopenharmony_ci * This set option adds a chunk type that the user is requesting to be
35618c2ecf20Sopenharmony_ci * received only in an authenticated way.  Changes to the list of chunks
35628c2ecf20Sopenharmony_ci * will only effect future associations on the socket.
35638c2ecf20Sopenharmony_ci */
35648c2ecf20Sopenharmony_cistatic int sctp_setsockopt_auth_chunk(struct sock *sk,
35658c2ecf20Sopenharmony_ci				      struct sctp_authchunk *val,
35668c2ecf20Sopenharmony_ci				      unsigned int optlen)
35678c2ecf20Sopenharmony_ci{
35688c2ecf20Sopenharmony_ci	struct sctp_endpoint *ep = sctp_sk(sk)->ep;
35698c2ecf20Sopenharmony_ci
35708c2ecf20Sopenharmony_ci	if (!ep->auth_enable)
35718c2ecf20Sopenharmony_ci		return -EACCES;
35728c2ecf20Sopenharmony_ci
35738c2ecf20Sopenharmony_ci	if (optlen != sizeof(struct sctp_authchunk))
35748c2ecf20Sopenharmony_ci		return -EINVAL;
35758c2ecf20Sopenharmony_ci
35768c2ecf20Sopenharmony_ci	switch (val->sauth_chunk) {
35778c2ecf20Sopenharmony_ci	case SCTP_CID_INIT:
35788c2ecf20Sopenharmony_ci	case SCTP_CID_INIT_ACK:
35798c2ecf20Sopenharmony_ci	case SCTP_CID_SHUTDOWN_COMPLETE:
35808c2ecf20Sopenharmony_ci	case SCTP_CID_AUTH:
35818c2ecf20Sopenharmony_ci		return -EINVAL;
35828c2ecf20Sopenharmony_ci	}
35838c2ecf20Sopenharmony_ci
35848c2ecf20Sopenharmony_ci	/* add this chunk id to the endpoint */
35858c2ecf20Sopenharmony_ci	return sctp_auth_ep_add_chunkid(ep, val->sauth_chunk);
35868c2ecf20Sopenharmony_ci}
35878c2ecf20Sopenharmony_ci
35888c2ecf20Sopenharmony_ci/*
35898c2ecf20Sopenharmony_ci * 7.1.19.  Get or set the list of supported HMAC Identifiers (SCTP_HMAC_IDENT)
35908c2ecf20Sopenharmony_ci *
35918c2ecf20Sopenharmony_ci * This option gets or sets the list of HMAC algorithms that the local
35928c2ecf20Sopenharmony_ci * endpoint requires the peer to use.
35938c2ecf20Sopenharmony_ci */
35948c2ecf20Sopenharmony_cistatic int sctp_setsockopt_hmac_ident(struct sock *sk,
35958c2ecf20Sopenharmony_ci				      struct sctp_hmacalgo *hmacs,
35968c2ecf20Sopenharmony_ci				      unsigned int optlen)
35978c2ecf20Sopenharmony_ci{
35988c2ecf20Sopenharmony_ci	struct sctp_endpoint *ep = sctp_sk(sk)->ep;
35998c2ecf20Sopenharmony_ci	u32 idents;
36008c2ecf20Sopenharmony_ci
36018c2ecf20Sopenharmony_ci	if (!ep->auth_enable)
36028c2ecf20Sopenharmony_ci		return -EACCES;
36038c2ecf20Sopenharmony_ci
36048c2ecf20Sopenharmony_ci	if (optlen < sizeof(struct sctp_hmacalgo))
36058c2ecf20Sopenharmony_ci		return -EINVAL;
36068c2ecf20Sopenharmony_ci	optlen = min_t(unsigned int, optlen, sizeof(struct sctp_hmacalgo) +
36078c2ecf20Sopenharmony_ci					     SCTP_AUTH_NUM_HMACS * sizeof(u16));
36088c2ecf20Sopenharmony_ci
36098c2ecf20Sopenharmony_ci	idents = hmacs->shmac_num_idents;
36108c2ecf20Sopenharmony_ci	if (idents == 0 || idents > SCTP_AUTH_NUM_HMACS ||
36118c2ecf20Sopenharmony_ci	    (idents * sizeof(u16)) > (optlen - sizeof(struct sctp_hmacalgo)))
36128c2ecf20Sopenharmony_ci		return -EINVAL;
36138c2ecf20Sopenharmony_ci
36148c2ecf20Sopenharmony_ci	return sctp_auth_ep_set_hmacs(ep, hmacs);
36158c2ecf20Sopenharmony_ci}
36168c2ecf20Sopenharmony_ci
36178c2ecf20Sopenharmony_ci/*
36188c2ecf20Sopenharmony_ci * 7.1.20.  Set a shared key (SCTP_AUTH_KEY)
36198c2ecf20Sopenharmony_ci *
36208c2ecf20Sopenharmony_ci * This option will set a shared secret key which is used to build an
36218c2ecf20Sopenharmony_ci * association shared key.
36228c2ecf20Sopenharmony_ci */
36238c2ecf20Sopenharmony_cistatic int sctp_setsockopt_auth_key(struct sock *sk,
36248c2ecf20Sopenharmony_ci				    struct sctp_authkey *authkey,
36258c2ecf20Sopenharmony_ci				    unsigned int optlen)
36268c2ecf20Sopenharmony_ci{
36278c2ecf20Sopenharmony_ci	struct sctp_endpoint *ep = sctp_sk(sk)->ep;
36288c2ecf20Sopenharmony_ci	struct sctp_association *asoc;
36298c2ecf20Sopenharmony_ci	int ret = -EINVAL;
36308c2ecf20Sopenharmony_ci
36318c2ecf20Sopenharmony_ci	if (optlen <= sizeof(struct sctp_authkey))
36328c2ecf20Sopenharmony_ci		return -EINVAL;
36338c2ecf20Sopenharmony_ci	/* authkey->sca_keylength is u16, so optlen can't be bigger than
36348c2ecf20Sopenharmony_ci	 * this.
36358c2ecf20Sopenharmony_ci	 */
36368c2ecf20Sopenharmony_ci	optlen = min_t(unsigned int, optlen, USHRT_MAX + sizeof(*authkey));
36378c2ecf20Sopenharmony_ci
36388c2ecf20Sopenharmony_ci	if (authkey->sca_keylength > optlen - sizeof(*authkey))
36398c2ecf20Sopenharmony_ci		goto out;
36408c2ecf20Sopenharmony_ci
36418c2ecf20Sopenharmony_ci	asoc = sctp_id2assoc(sk, authkey->sca_assoc_id);
36428c2ecf20Sopenharmony_ci	if (!asoc && authkey->sca_assoc_id > SCTP_ALL_ASSOC &&
36438c2ecf20Sopenharmony_ci	    sctp_style(sk, UDP))
36448c2ecf20Sopenharmony_ci		goto out;
36458c2ecf20Sopenharmony_ci
36468c2ecf20Sopenharmony_ci	if (asoc) {
36478c2ecf20Sopenharmony_ci		ret = sctp_auth_set_key(ep, asoc, authkey);
36488c2ecf20Sopenharmony_ci		goto out;
36498c2ecf20Sopenharmony_ci	}
36508c2ecf20Sopenharmony_ci
36518c2ecf20Sopenharmony_ci	if (sctp_style(sk, TCP))
36528c2ecf20Sopenharmony_ci		authkey->sca_assoc_id = SCTP_FUTURE_ASSOC;
36538c2ecf20Sopenharmony_ci
36548c2ecf20Sopenharmony_ci	if (authkey->sca_assoc_id == SCTP_FUTURE_ASSOC ||
36558c2ecf20Sopenharmony_ci	    authkey->sca_assoc_id == SCTP_ALL_ASSOC) {
36568c2ecf20Sopenharmony_ci		ret = sctp_auth_set_key(ep, asoc, authkey);
36578c2ecf20Sopenharmony_ci		if (ret)
36588c2ecf20Sopenharmony_ci			goto out;
36598c2ecf20Sopenharmony_ci	}
36608c2ecf20Sopenharmony_ci
36618c2ecf20Sopenharmony_ci	ret = 0;
36628c2ecf20Sopenharmony_ci
36638c2ecf20Sopenharmony_ci	if (authkey->sca_assoc_id == SCTP_CURRENT_ASSOC ||
36648c2ecf20Sopenharmony_ci	    authkey->sca_assoc_id == SCTP_ALL_ASSOC) {
36658c2ecf20Sopenharmony_ci		list_for_each_entry(asoc, &ep->asocs, asocs) {
36668c2ecf20Sopenharmony_ci			int res = sctp_auth_set_key(ep, asoc, authkey);
36678c2ecf20Sopenharmony_ci
36688c2ecf20Sopenharmony_ci			if (res && !ret)
36698c2ecf20Sopenharmony_ci				ret = res;
36708c2ecf20Sopenharmony_ci		}
36718c2ecf20Sopenharmony_ci	}
36728c2ecf20Sopenharmony_ci
36738c2ecf20Sopenharmony_ciout:
36748c2ecf20Sopenharmony_ci	memzero_explicit(authkey, optlen);
36758c2ecf20Sopenharmony_ci	return ret;
36768c2ecf20Sopenharmony_ci}
36778c2ecf20Sopenharmony_ci
36788c2ecf20Sopenharmony_ci/*
36798c2ecf20Sopenharmony_ci * 7.1.21.  Get or set the active shared key (SCTP_AUTH_ACTIVE_KEY)
36808c2ecf20Sopenharmony_ci *
36818c2ecf20Sopenharmony_ci * This option will get or set the active shared key to be used to build
36828c2ecf20Sopenharmony_ci * the association shared key.
36838c2ecf20Sopenharmony_ci */
36848c2ecf20Sopenharmony_cistatic int sctp_setsockopt_active_key(struct sock *sk,
36858c2ecf20Sopenharmony_ci				      struct sctp_authkeyid *val,
36868c2ecf20Sopenharmony_ci				      unsigned int optlen)
36878c2ecf20Sopenharmony_ci{
36888c2ecf20Sopenharmony_ci	struct sctp_endpoint *ep = sctp_sk(sk)->ep;
36898c2ecf20Sopenharmony_ci	struct sctp_association *asoc;
36908c2ecf20Sopenharmony_ci	int ret = 0;
36918c2ecf20Sopenharmony_ci
36928c2ecf20Sopenharmony_ci	if (optlen != sizeof(struct sctp_authkeyid))
36938c2ecf20Sopenharmony_ci		return -EINVAL;
36948c2ecf20Sopenharmony_ci
36958c2ecf20Sopenharmony_ci	asoc = sctp_id2assoc(sk, val->scact_assoc_id);
36968c2ecf20Sopenharmony_ci	if (!asoc && val->scact_assoc_id > SCTP_ALL_ASSOC &&
36978c2ecf20Sopenharmony_ci	    sctp_style(sk, UDP))
36988c2ecf20Sopenharmony_ci		return -EINVAL;
36998c2ecf20Sopenharmony_ci
37008c2ecf20Sopenharmony_ci	if (asoc)
37018c2ecf20Sopenharmony_ci		return sctp_auth_set_active_key(ep, asoc, val->scact_keynumber);
37028c2ecf20Sopenharmony_ci
37038c2ecf20Sopenharmony_ci	if (sctp_style(sk, TCP))
37048c2ecf20Sopenharmony_ci		val->scact_assoc_id = SCTP_FUTURE_ASSOC;
37058c2ecf20Sopenharmony_ci
37068c2ecf20Sopenharmony_ci	if (val->scact_assoc_id == SCTP_FUTURE_ASSOC ||
37078c2ecf20Sopenharmony_ci	    val->scact_assoc_id == SCTP_ALL_ASSOC) {
37088c2ecf20Sopenharmony_ci		ret = sctp_auth_set_active_key(ep, asoc, val->scact_keynumber);
37098c2ecf20Sopenharmony_ci		if (ret)
37108c2ecf20Sopenharmony_ci			return ret;
37118c2ecf20Sopenharmony_ci	}
37128c2ecf20Sopenharmony_ci
37138c2ecf20Sopenharmony_ci	if (val->scact_assoc_id == SCTP_CURRENT_ASSOC ||
37148c2ecf20Sopenharmony_ci	    val->scact_assoc_id == SCTP_ALL_ASSOC) {
37158c2ecf20Sopenharmony_ci		list_for_each_entry(asoc, &ep->asocs, asocs) {
37168c2ecf20Sopenharmony_ci			int res = sctp_auth_set_active_key(ep, asoc,
37178c2ecf20Sopenharmony_ci							   val->scact_keynumber);
37188c2ecf20Sopenharmony_ci
37198c2ecf20Sopenharmony_ci			if (res && !ret)
37208c2ecf20Sopenharmony_ci				ret = res;
37218c2ecf20Sopenharmony_ci		}
37228c2ecf20Sopenharmony_ci	}
37238c2ecf20Sopenharmony_ci
37248c2ecf20Sopenharmony_ci	return ret;
37258c2ecf20Sopenharmony_ci}
37268c2ecf20Sopenharmony_ci
37278c2ecf20Sopenharmony_ci/*
37288c2ecf20Sopenharmony_ci * 7.1.22.  Delete a shared key (SCTP_AUTH_DELETE_KEY)
37298c2ecf20Sopenharmony_ci *
37308c2ecf20Sopenharmony_ci * This set option will delete a shared secret key from use.
37318c2ecf20Sopenharmony_ci */
37328c2ecf20Sopenharmony_cistatic int sctp_setsockopt_del_key(struct sock *sk,
37338c2ecf20Sopenharmony_ci				   struct sctp_authkeyid *val,
37348c2ecf20Sopenharmony_ci				   unsigned int optlen)
37358c2ecf20Sopenharmony_ci{
37368c2ecf20Sopenharmony_ci	struct sctp_endpoint *ep = sctp_sk(sk)->ep;
37378c2ecf20Sopenharmony_ci	struct sctp_association *asoc;
37388c2ecf20Sopenharmony_ci	int ret = 0;
37398c2ecf20Sopenharmony_ci
37408c2ecf20Sopenharmony_ci	if (optlen != sizeof(struct sctp_authkeyid))
37418c2ecf20Sopenharmony_ci		return -EINVAL;
37428c2ecf20Sopenharmony_ci
37438c2ecf20Sopenharmony_ci	asoc = sctp_id2assoc(sk, val->scact_assoc_id);
37448c2ecf20Sopenharmony_ci	if (!asoc && val->scact_assoc_id > SCTP_ALL_ASSOC &&
37458c2ecf20Sopenharmony_ci	    sctp_style(sk, UDP))
37468c2ecf20Sopenharmony_ci		return -EINVAL;
37478c2ecf20Sopenharmony_ci
37488c2ecf20Sopenharmony_ci	if (asoc)
37498c2ecf20Sopenharmony_ci		return sctp_auth_del_key_id(ep, asoc, val->scact_keynumber);
37508c2ecf20Sopenharmony_ci
37518c2ecf20Sopenharmony_ci	if (sctp_style(sk, TCP))
37528c2ecf20Sopenharmony_ci		val->scact_assoc_id = SCTP_FUTURE_ASSOC;
37538c2ecf20Sopenharmony_ci
37548c2ecf20Sopenharmony_ci	if (val->scact_assoc_id == SCTP_FUTURE_ASSOC ||
37558c2ecf20Sopenharmony_ci	    val->scact_assoc_id == SCTP_ALL_ASSOC) {
37568c2ecf20Sopenharmony_ci		ret = sctp_auth_del_key_id(ep, asoc, val->scact_keynumber);
37578c2ecf20Sopenharmony_ci		if (ret)
37588c2ecf20Sopenharmony_ci			return ret;
37598c2ecf20Sopenharmony_ci	}
37608c2ecf20Sopenharmony_ci
37618c2ecf20Sopenharmony_ci	if (val->scact_assoc_id == SCTP_CURRENT_ASSOC ||
37628c2ecf20Sopenharmony_ci	    val->scact_assoc_id == SCTP_ALL_ASSOC) {
37638c2ecf20Sopenharmony_ci		list_for_each_entry(asoc, &ep->asocs, asocs) {
37648c2ecf20Sopenharmony_ci			int res = sctp_auth_del_key_id(ep, asoc,
37658c2ecf20Sopenharmony_ci						       val->scact_keynumber);
37668c2ecf20Sopenharmony_ci
37678c2ecf20Sopenharmony_ci			if (res && !ret)
37688c2ecf20Sopenharmony_ci				ret = res;
37698c2ecf20Sopenharmony_ci		}
37708c2ecf20Sopenharmony_ci	}
37718c2ecf20Sopenharmony_ci
37728c2ecf20Sopenharmony_ci	return ret;
37738c2ecf20Sopenharmony_ci}
37748c2ecf20Sopenharmony_ci
37758c2ecf20Sopenharmony_ci/*
37768c2ecf20Sopenharmony_ci * 8.3.4  Deactivate a Shared Key (SCTP_AUTH_DEACTIVATE_KEY)
37778c2ecf20Sopenharmony_ci *
37788c2ecf20Sopenharmony_ci * This set option will deactivate a shared secret key.
37798c2ecf20Sopenharmony_ci */
37808c2ecf20Sopenharmony_cistatic int sctp_setsockopt_deactivate_key(struct sock *sk,
37818c2ecf20Sopenharmony_ci					  struct sctp_authkeyid *val,
37828c2ecf20Sopenharmony_ci					  unsigned int optlen)
37838c2ecf20Sopenharmony_ci{
37848c2ecf20Sopenharmony_ci	struct sctp_endpoint *ep = sctp_sk(sk)->ep;
37858c2ecf20Sopenharmony_ci	struct sctp_association *asoc;
37868c2ecf20Sopenharmony_ci	int ret = 0;
37878c2ecf20Sopenharmony_ci
37888c2ecf20Sopenharmony_ci	if (optlen != sizeof(struct sctp_authkeyid))
37898c2ecf20Sopenharmony_ci		return -EINVAL;
37908c2ecf20Sopenharmony_ci
37918c2ecf20Sopenharmony_ci	asoc = sctp_id2assoc(sk, val->scact_assoc_id);
37928c2ecf20Sopenharmony_ci	if (!asoc && val->scact_assoc_id > SCTP_ALL_ASSOC &&
37938c2ecf20Sopenharmony_ci	    sctp_style(sk, UDP))
37948c2ecf20Sopenharmony_ci		return -EINVAL;
37958c2ecf20Sopenharmony_ci
37968c2ecf20Sopenharmony_ci	if (asoc)
37978c2ecf20Sopenharmony_ci		return sctp_auth_deact_key_id(ep, asoc, val->scact_keynumber);
37988c2ecf20Sopenharmony_ci
37998c2ecf20Sopenharmony_ci	if (sctp_style(sk, TCP))
38008c2ecf20Sopenharmony_ci		val->scact_assoc_id = SCTP_FUTURE_ASSOC;
38018c2ecf20Sopenharmony_ci
38028c2ecf20Sopenharmony_ci	if (val->scact_assoc_id == SCTP_FUTURE_ASSOC ||
38038c2ecf20Sopenharmony_ci	    val->scact_assoc_id == SCTP_ALL_ASSOC) {
38048c2ecf20Sopenharmony_ci		ret = sctp_auth_deact_key_id(ep, asoc, val->scact_keynumber);
38058c2ecf20Sopenharmony_ci		if (ret)
38068c2ecf20Sopenharmony_ci			return ret;
38078c2ecf20Sopenharmony_ci	}
38088c2ecf20Sopenharmony_ci
38098c2ecf20Sopenharmony_ci	if (val->scact_assoc_id == SCTP_CURRENT_ASSOC ||
38108c2ecf20Sopenharmony_ci	    val->scact_assoc_id == SCTP_ALL_ASSOC) {
38118c2ecf20Sopenharmony_ci		list_for_each_entry(asoc, &ep->asocs, asocs) {
38128c2ecf20Sopenharmony_ci			int res = sctp_auth_deact_key_id(ep, asoc,
38138c2ecf20Sopenharmony_ci							 val->scact_keynumber);
38148c2ecf20Sopenharmony_ci
38158c2ecf20Sopenharmony_ci			if (res && !ret)
38168c2ecf20Sopenharmony_ci				ret = res;
38178c2ecf20Sopenharmony_ci		}
38188c2ecf20Sopenharmony_ci	}
38198c2ecf20Sopenharmony_ci
38208c2ecf20Sopenharmony_ci	return ret;
38218c2ecf20Sopenharmony_ci}
38228c2ecf20Sopenharmony_ci
38238c2ecf20Sopenharmony_ci/*
38248c2ecf20Sopenharmony_ci * 8.1.23 SCTP_AUTO_ASCONF
38258c2ecf20Sopenharmony_ci *
38268c2ecf20Sopenharmony_ci * This option will enable or disable the use of the automatic generation of
38278c2ecf20Sopenharmony_ci * ASCONF chunks to add and delete addresses to an existing association.  Note
38288c2ecf20Sopenharmony_ci * that this option has two caveats namely: a) it only affects sockets that
38298c2ecf20Sopenharmony_ci * are bound to all addresses available to the SCTP stack, and b) the system
38308c2ecf20Sopenharmony_ci * administrator may have an overriding control that turns the ASCONF feature
38318c2ecf20Sopenharmony_ci * off no matter what setting the socket option may have.
38328c2ecf20Sopenharmony_ci * This option expects an integer boolean flag, where a non-zero value turns on
38338c2ecf20Sopenharmony_ci * the option, and a zero value turns off the option.
38348c2ecf20Sopenharmony_ci * Note. In this implementation, socket operation overrides default parameter
38358c2ecf20Sopenharmony_ci * being set by sysctl as well as FreeBSD implementation
38368c2ecf20Sopenharmony_ci */
38378c2ecf20Sopenharmony_cistatic int sctp_setsockopt_auto_asconf(struct sock *sk, int *val,
38388c2ecf20Sopenharmony_ci					unsigned int optlen)
38398c2ecf20Sopenharmony_ci{
38408c2ecf20Sopenharmony_ci	struct sctp_sock *sp = sctp_sk(sk);
38418c2ecf20Sopenharmony_ci
38428c2ecf20Sopenharmony_ci	if (optlen < sizeof(int))
38438c2ecf20Sopenharmony_ci		return -EINVAL;
38448c2ecf20Sopenharmony_ci	if (!sctp_is_ep_boundall(sk) && *val)
38458c2ecf20Sopenharmony_ci		return -EINVAL;
38468c2ecf20Sopenharmony_ci	if ((*val && sp->do_auto_asconf) || (!*val && !sp->do_auto_asconf))
38478c2ecf20Sopenharmony_ci		return 0;
38488c2ecf20Sopenharmony_ci
38498c2ecf20Sopenharmony_ci	spin_lock_bh(&sock_net(sk)->sctp.addr_wq_lock);
38508c2ecf20Sopenharmony_ci	if (*val == 0 && sp->do_auto_asconf) {
38518c2ecf20Sopenharmony_ci		list_del(&sp->auto_asconf_list);
38528c2ecf20Sopenharmony_ci		sp->do_auto_asconf = 0;
38538c2ecf20Sopenharmony_ci	} else if (*val && !sp->do_auto_asconf) {
38548c2ecf20Sopenharmony_ci		list_add_tail(&sp->auto_asconf_list,
38558c2ecf20Sopenharmony_ci		    &sock_net(sk)->sctp.auto_asconf_splist);
38568c2ecf20Sopenharmony_ci		sp->do_auto_asconf = 1;
38578c2ecf20Sopenharmony_ci	}
38588c2ecf20Sopenharmony_ci	spin_unlock_bh(&sock_net(sk)->sctp.addr_wq_lock);
38598c2ecf20Sopenharmony_ci	return 0;
38608c2ecf20Sopenharmony_ci}
38618c2ecf20Sopenharmony_ci
38628c2ecf20Sopenharmony_ci/*
38638c2ecf20Sopenharmony_ci * SCTP_PEER_ADDR_THLDS
38648c2ecf20Sopenharmony_ci *
38658c2ecf20Sopenharmony_ci * This option allows us to alter the partially failed threshold for one or all
38668c2ecf20Sopenharmony_ci * transports in an association.  See Section 6.1 of:
38678c2ecf20Sopenharmony_ci * http://www.ietf.org/id/draft-nishida-tsvwg-sctp-failover-05.txt
38688c2ecf20Sopenharmony_ci */
38698c2ecf20Sopenharmony_cistatic int sctp_setsockopt_paddr_thresholds(struct sock *sk,
38708c2ecf20Sopenharmony_ci					    struct sctp_paddrthlds_v2 *val,
38718c2ecf20Sopenharmony_ci					    unsigned int optlen, bool v2)
38728c2ecf20Sopenharmony_ci{
38738c2ecf20Sopenharmony_ci	struct sctp_transport *trans;
38748c2ecf20Sopenharmony_ci	struct sctp_association *asoc;
38758c2ecf20Sopenharmony_ci	int len;
38768c2ecf20Sopenharmony_ci
38778c2ecf20Sopenharmony_ci	len = v2 ? sizeof(*val) : sizeof(struct sctp_paddrthlds);
38788c2ecf20Sopenharmony_ci	if (optlen < len)
38798c2ecf20Sopenharmony_ci		return -EINVAL;
38808c2ecf20Sopenharmony_ci
38818c2ecf20Sopenharmony_ci	if (v2 && val->spt_pathpfthld > val->spt_pathcpthld)
38828c2ecf20Sopenharmony_ci		return -EINVAL;
38838c2ecf20Sopenharmony_ci
38848c2ecf20Sopenharmony_ci	if (!sctp_is_any(sk, (const union sctp_addr *)&val->spt_address)) {
38858c2ecf20Sopenharmony_ci		trans = sctp_addr_id2transport(sk, &val->spt_address,
38868c2ecf20Sopenharmony_ci					       val->spt_assoc_id);
38878c2ecf20Sopenharmony_ci		if (!trans)
38888c2ecf20Sopenharmony_ci			return -ENOENT;
38898c2ecf20Sopenharmony_ci
38908c2ecf20Sopenharmony_ci		if (val->spt_pathmaxrxt)
38918c2ecf20Sopenharmony_ci			trans->pathmaxrxt = val->spt_pathmaxrxt;
38928c2ecf20Sopenharmony_ci		if (v2)
38938c2ecf20Sopenharmony_ci			trans->ps_retrans = val->spt_pathcpthld;
38948c2ecf20Sopenharmony_ci		trans->pf_retrans = val->spt_pathpfthld;
38958c2ecf20Sopenharmony_ci
38968c2ecf20Sopenharmony_ci		return 0;
38978c2ecf20Sopenharmony_ci	}
38988c2ecf20Sopenharmony_ci
38998c2ecf20Sopenharmony_ci	asoc = sctp_id2assoc(sk, val->spt_assoc_id);
39008c2ecf20Sopenharmony_ci	if (!asoc && val->spt_assoc_id != SCTP_FUTURE_ASSOC &&
39018c2ecf20Sopenharmony_ci	    sctp_style(sk, UDP))
39028c2ecf20Sopenharmony_ci		return -EINVAL;
39038c2ecf20Sopenharmony_ci
39048c2ecf20Sopenharmony_ci	if (asoc) {
39058c2ecf20Sopenharmony_ci		list_for_each_entry(trans, &asoc->peer.transport_addr_list,
39068c2ecf20Sopenharmony_ci				    transports) {
39078c2ecf20Sopenharmony_ci			if (val->spt_pathmaxrxt)
39088c2ecf20Sopenharmony_ci				trans->pathmaxrxt = val->spt_pathmaxrxt;
39098c2ecf20Sopenharmony_ci			if (v2)
39108c2ecf20Sopenharmony_ci				trans->ps_retrans = val->spt_pathcpthld;
39118c2ecf20Sopenharmony_ci			trans->pf_retrans = val->spt_pathpfthld;
39128c2ecf20Sopenharmony_ci		}
39138c2ecf20Sopenharmony_ci
39148c2ecf20Sopenharmony_ci		if (val->spt_pathmaxrxt)
39158c2ecf20Sopenharmony_ci			asoc->pathmaxrxt = val->spt_pathmaxrxt;
39168c2ecf20Sopenharmony_ci		if (v2)
39178c2ecf20Sopenharmony_ci			asoc->ps_retrans = val->spt_pathcpthld;
39188c2ecf20Sopenharmony_ci		asoc->pf_retrans = val->spt_pathpfthld;
39198c2ecf20Sopenharmony_ci	} else {
39208c2ecf20Sopenharmony_ci		struct sctp_sock *sp = sctp_sk(sk);
39218c2ecf20Sopenharmony_ci
39228c2ecf20Sopenharmony_ci		if (val->spt_pathmaxrxt)
39238c2ecf20Sopenharmony_ci			sp->pathmaxrxt = val->spt_pathmaxrxt;
39248c2ecf20Sopenharmony_ci		if (v2)
39258c2ecf20Sopenharmony_ci			sp->ps_retrans = val->spt_pathcpthld;
39268c2ecf20Sopenharmony_ci		sp->pf_retrans = val->spt_pathpfthld;
39278c2ecf20Sopenharmony_ci	}
39288c2ecf20Sopenharmony_ci
39298c2ecf20Sopenharmony_ci	return 0;
39308c2ecf20Sopenharmony_ci}
39318c2ecf20Sopenharmony_ci
39328c2ecf20Sopenharmony_cistatic int sctp_setsockopt_recvrcvinfo(struct sock *sk, int *val,
39338c2ecf20Sopenharmony_ci				       unsigned int optlen)
39348c2ecf20Sopenharmony_ci{
39358c2ecf20Sopenharmony_ci	if (optlen < sizeof(int))
39368c2ecf20Sopenharmony_ci		return -EINVAL;
39378c2ecf20Sopenharmony_ci
39388c2ecf20Sopenharmony_ci	sctp_sk(sk)->recvrcvinfo = (*val == 0) ? 0 : 1;
39398c2ecf20Sopenharmony_ci
39408c2ecf20Sopenharmony_ci	return 0;
39418c2ecf20Sopenharmony_ci}
39428c2ecf20Sopenharmony_ci
39438c2ecf20Sopenharmony_cistatic int sctp_setsockopt_recvnxtinfo(struct sock *sk, int *val,
39448c2ecf20Sopenharmony_ci				       unsigned int optlen)
39458c2ecf20Sopenharmony_ci{
39468c2ecf20Sopenharmony_ci	if (optlen < sizeof(int))
39478c2ecf20Sopenharmony_ci		return -EINVAL;
39488c2ecf20Sopenharmony_ci
39498c2ecf20Sopenharmony_ci	sctp_sk(sk)->recvnxtinfo = (*val == 0) ? 0 : 1;
39508c2ecf20Sopenharmony_ci
39518c2ecf20Sopenharmony_ci	return 0;
39528c2ecf20Sopenharmony_ci}
39538c2ecf20Sopenharmony_ci
39548c2ecf20Sopenharmony_cistatic int sctp_setsockopt_pr_supported(struct sock *sk,
39558c2ecf20Sopenharmony_ci					struct sctp_assoc_value *params,
39568c2ecf20Sopenharmony_ci					unsigned int optlen)
39578c2ecf20Sopenharmony_ci{
39588c2ecf20Sopenharmony_ci	struct sctp_association *asoc;
39598c2ecf20Sopenharmony_ci
39608c2ecf20Sopenharmony_ci	if (optlen != sizeof(*params))
39618c2ecf20Sopenharmony_ci		return -EINVAL;
39628c2ecf20Sopenharmony_ci
39638c2ecf20Sopenharmony_ci	asoc = sctp_id2assoc(sk, params->assoc_id);
39648c2ecf20Sopenharmony_ci	if (!asoc && params->assoc_id != SCTP_FUTURE_ASSOC &&
39658c2ecf20Sopenharmony_ci	    sctp_style(sk, UDP))
39668c2ecf20Sopenharmony_ci		return -EINVAL;
39678c2ecf20Sopenharmony_ci
39688c2ecf20Sopenharmony_ci	sctp_sk(sk)->ep->prsctp_enable = !!params->assoc_value;
39698c2ecf20Sopenharmony_ci
39708c2ecf20Sopenharmony_ci	return 0;
39718c2ecf20Sopenharmony_ci}
39728c2ecf20Sopenharmony_ci
39738c2ecf20Sopenharmony_cistatic int sctp_setsockopt_default_prinfo(struct sock *sk,
39748c2ecf20Sopenharmony_ci					  struct sctp_default_prinfo *info,
39758c2ecf20Sopenharmony_ci					  unsigned int optlen)
39768c2ecf20Sopenharmony_ci{
39778c2ecf20Sopenharmony_ci	struct sctp_sock *sp = sctp_sk(sk);
39788c2ecf20Sopenharmony_ci	struct sctp_association *asoc;
39798c2ecf20Sopenharmony_ci	int retval = -EINVAL;
39808c2ecf20Sopenharmony_ci
39818c2ecf20Sopenharmony_ci	if (optlen != sizeof(*info))
39828c2ecf20Sopenharmony_ci		goto out;
39838c2ecf20Sopenharmony_ci
39848c2ecf20Sopenharmony_ci	if (info->pr_policy & ~SCTP_PR_SCTP_MASK)
39858c2ecf20Sopenharmony_ci		goto out;
39868c2ecf20Sopenharmony_ci
39878c2ecf20Sopenharmony_ci	if (info->pr_policy == SCTP_PR_SCTP_NONE)
39888c2ecf20Sopenharmony_ci		info->pr_value = 0;
39898c2ecf20Sopenharmony_ci
39908c2ecf20Sopenharmony_ci	asoc = sctp_id2assoc(sk, info->pr_assoc_id);
39918c2ecf20Sopenharmony_ci	if (!asoc && info->pr_assoc_id > SCTP_ALL_ASSOC &&
39928c2ecf20Sopenharmony_ci	    sctp_style(sk, UDP))
39938c2ecf20Sopenharmony_ci		goto out;
39948c2ecf20Sopenharmony_ci
39958c2ecf20Sopenharmony_ci	retval = 0;
39968c2ecf20Sopenharmony_ci
39978c2ecf20Sopenharmony_ci	if (asoc) {
39988c2ecf20Sopenharmony_ci		SCTP_PR_SET_POLICY(asoc->default_flags, info->pr_policy);
39998c2ecf20Sopenharmony_ci		asoc->default_timetolive = info->pr_value;
40008c2ecf20Sopenharmony_ci		goto out;
40018c2ecf20Sopenharmony_ci	}
40028c2ecf20Sopenharmony_ci
40038c2ecf20Sopenharmony_ci	if (sctp_style(sk, TCP))
40048c2ecf20Sopenharmony_ci		info->pr_assoc_id = SCTP_FUTURE_ASSOC;
40058c2ecf20Sopenharmony_ci
40068c2ecf20Sopenharmony_ci	if (info->pr_assoc_id == SCTP_FUTURE_ASSOC ||
40078c2ecf20Sopenharmony_ci	    info->pr_assoc_id == SCTP_ALL_ASSOC) {
40088c2ecf20Sopenharmony_ci		SCTP_PR_SET_POLICY(sp->default_flags, info->pr_policy);
40098c2ecf20Sopenharmony_ci		sp->default_timetolive = info->pr_value;
40108c2ecf20Sopenharmony_ci	}
40118c2ecf20Sopenharmony_ci
40128c2ecf20Sopenharmony_ci	if (info->pr_assoc_id == SCTP_CURRENT_ASSOC ||
40138c2ecf20Sopenharmony_ci	    info->pr_assoc_id == SCTP_ALL_ASSOC) {
40148c2ecf20Sopenharmony_ci		list_for_each_entry(asoc, &sp->ep->asocs, asocs) {
40158c2ecf20Sopenharmony_ci			SCTP_PR_SET_POLICY(asoc->default_flags,
40168c2ecf20Sopenharmony_ci					   info->pr_policy);
40178c2ecf20Sopenharmony_ci			asoc->default_timetolive = info->pr_value;
40188c2ecf20Sopenharmony_ci		}
40198c2ecf20Sopenharmony_ci	}
40208c2ecf20Sopenharmony_ci
40218c2ecf20Sopenharmony_ciout:
40228c2ecf20Sopenharmony_ci	return retval;
40238c2ecf20Sopenharmony_ci}
40248c2ecf20Sopenharmony_ci
40258c2ecf20Sopenharmony_cistatic int sctp_setsockopt_reconfig_supported(struct sock *sk,
40268c2ecf20Sopenharmony_ci					      struct sctp_assoc_value *params,
40278c2ecf20Sopenharmony_ci					      unsigned int optlen)
40288c2ecf20Sopenharmony_ci{
40298c2ecf20Sopenharmony_ci	struct sctp_association *asoc;
40308c2ecf20Sopenharmony_ci	int retval = -EINVAL;
40318c2ecf20Sopenharmony_ci
40328c2ecf20Sopenharmony_ci	if (optlen != sizeof(*params))
40338c2ecf20Sopenharmony_ci		goto out;
40348c2ecf20Sopenharmony_ci
40358c2ecf20Sopenharmony_ci	asoc = sctp_id2assoc(sk, params->assoc_id);
40368c2ecf20Sopenharmony_ci	if (!asoc && params->assoc_id != SCTP_FUTURE_ASSOC &&
40378c2ecf20Sopenharmony_ci	    sctp_style(sk, UDP))
40388c2ecf20Sopenharmony_ci		goto out;
40398c2ecf20Sopenharmony_ci
40408c2ecf20Sopenharmony_ci	sctp_sk(sk)->ep->reconf_enable = !!params->assoc_value;
40418c2ecf20Sopenharmony_ci
40428c2ecf20Sopenharmony_ci	retval = 0;
40438c2ecf20Sopenharmony_ci
40448c2ecf20Sopenharmony_ciout:
40458c2ecf20Sopenharmony_ci	return retval;
40468c2ecf20Sopenharmony_ci}
40478c2ecf20Sopenharmony_ci
40488c2ecf20Sopenharmony_cistatic int sctp_setsockopt_enable_strreset(struct sock *sk,
40498c2ecf20Sopenharmony_ci					   struct sctp_assoc_value *params,
40508c2ecf20Sopenharmony_ci					   unsigned int optlen)
40518c2ecf20Sopenharmony_ci{
40528c2ecf20Sopenharmony_ci	struct sctp_endpoint *ep = sctp_sk(sk)->ep;
40538c2ecf20Sopenharmony_ci	struct sctp_association *asoc;
40548c2ecf20Sopenharmony_ci	int retval = -EINVAL;
40558c2ecf20Sopenharmony_ci
40568c2ecf20Sopenharmony_ci	if (optlen != sizeof(*params))
40578c2ecf20Sopenharmony_ci		goto out;
40588c2ecf20Sopenharmony_ci
40598c2ecf20Sopenharmony_ci	if (params->assoc_value & (~SCTP_ENABLE_STRRESET_MASK))
40608c2ecf20Sopenharmony_ci		goto out;
40618c2ecf20Sopenharmony_ci
40628c2ecf20Sopenharmony_ci	asoc = sctp_id2assoc(sk, params->assoc_id);
40638c2ecf20Sopenharmony_ci	if (!asoc && params->assoc_id > SCTP_ALL_ASSOC &&
40648c2ecf20Sopenharmony_ci	    sctp_style(sk, UDP))
40658c2ecf20Sopenharmony_ci		goto out;
40668c2ecf20Sopenharmony_ci
40678c2ecf20Sopenharmony_ci	retval = 0;
40688c2ecf20Sopenharmony_ci
40698c2ecf20Sopenharmony_ci	if (asoc) {
40708c2ecf20Sopenharmony_ci		asoc->strreset_enable = params->assoc_value;
40718c2ecf20Sopenharmony_ci		goto out;
40728c2ecf20Sopenharmony_ci	}
40738c2ecf20Sopenharmony_ci
40748c2ecf20Sopenharmony_ci	if (sctp_style(sk, TCP))
40758c2ecf20Sopenharmony_ci		params->assoc_id = SCTP_FUTURE_ASSOC;
40768c2ecf20Sopenharmony_ci
40778c2ecf20Sopenharmony_ci	if (params->assoc_id == SCTP_FUTURE_ASSOC ||
40788c2ecf20Sopenharmony_ci	    params->assoc_id == SCTP_ALL_ASSOC)
40798c2ecf20Sopenharmony_ci		ep->strreset_enable = params->assoc_value;
40808c2ecf20Sopenharmony_ci
40818c2ecf20Sopenharmony_ci	if (params->assoc_id == SCTP_CURRENT_ASSOC ||
40828c2ecf20Sopenharmony_ci	    params->assoc_id == SCTP_ALL_ASSOC)
40838c2ecf20Sopenharmony_ci		list_for_each_entry(asoc, &ep->asocs, asocs)
40848c2ecf20Sopenharmony_ci			asoc->strreset_enable = params->assoc_value;
40858c2ecf20Sopenharmony_ci
40868c2ecf20Sopenharmony_ciout:
40878c2ecf20Sopenharmony_ci	return retval;
40888c2ecf20Sopenharmony_ci}
40898c2ecf20Sopenharmony_ci
40908c2ecf20Sopenharmony_cistatic int sctp_setsockopt_reset_streams(struct sock *sk,
40918c2ecf20Sopenharmony_ci					 struct sctp_reset_streams *params,
40928c2ecf20Sopenharmony_ci					 unsigned int optlen)
40938c2ecf20Sopenharmony_ci{
40948c2ecf20Sopenharmony_ci	struct sctp_association *asoc;
40958c2ecf20Sopenharmony_ci
40968c2ecf20Sopenharmony_ci	if (optlen < sizeof(*params))
40978c2ecf20Sopenharmony_ci		return -EINVAL;
40988c2ecf20Sopenharmony_ci	/* srs_number_streams is u16, so optlen can't be bigger than this. */
40998c2ecf20Sopenharmony_ci	optlen = min_t(unsigned int, optlen, USHRT_MAX +
41008c2ecf20Sopenharmony_ci					     sizeof(__u16) * sizeof(*params));
41018c2ecf20Sopenharmony_ci
41028c2ecf20Sopenharmony_ci	if (params->srs_number_streams * sizeof(__u16) >
41038c2ecf20Sopenharmony_ci	    optlen - sizeof(*params))
41048c2ecf20Sopenharmony_ci		return -EINVAL;
41058c2ecf20Sopenharmony_ci
41068c2ecf20Sopenharmony_ci	asoc = sctp_id2assoc(sk, params->srs_assoc_id);
41078c2ecf20Sopenharmony_ci	if (!asoc)
41088c2ecf20Sopenharmony_ci		return -EINVAL;
41098c2ecf20Sopenharmony_ci
41108c2ecf20Sopenharmony_ci	return sctp_send_reset_streams(asoc, params);
41118c2ecf20Sopenharmony_ci}
41128c2ecf20Sopenharmony_ci
41138c2ecf20Sopenharmony_cistatic int sctp_setsockopt_reset_assoc(struct sock *sk, sctp_assoc_t *associd,
41148c2ecf20Sopenharmony_ci				       unsigned int optlen)
41158c2ecf20Sopenharmony_ci{
41168c2ecf20Sopenharmony_ci	struct sctp_association *asoc;
41178c2ecf20Sopenharmony_ci
41188c2ecf20Sopenharmony_ci	if (optlen != sizeof(*associd))
41198c2ecf20Sopenharmony_ci		return -EINVAL;
41208c2ecf20Sopenharmony_ci
41218c2ecf20Sopenharmony_ci	asoc = sctp_id2assoc(sk, *associd);
41228c2ecf20Sopenharmony_ci	if (!asoc)
41238c2ecf20Sopenharmony_ci		return -EINVAL;
41248c2ecf20Sopenharmony_ci
41258c2ecf20Sopenharmony_ci	return sctp_send_reset_assoc(asoc);
41268c2ecf20Sopenharmony_ci}
41278c2ecf20Sopenharmony_ci
41288c2ecf20Sopenharmony_cistatic int sctp_setsockopt_add_streams(struct sock *sk,
41298c2ecf20Sopenharmony_ci				       struct sctp_add_streams *params,
41308c2ecf20Sopenharmony_ci				       unsigned int optlen)
41318c2ecf20Sopenharmony_ci{
41328c2ecf20Sopenharmony_ci	struct sctp_association *asoc;
41338c2ecf20Sopenharmony_ci
41348c2ecf20Sopenharmony_ci	if (optlen != sizeof(*params))
41358c2ecf20Sopenharmony_ci		return -EINVAL;
41368c2ecf20Sopenharmony_ci
41378c2ecf20Sopenharmony_ci	asoc = sctp_id2assoc(sk, params->sas_assoc_id);
41388c2ecf20Sopenharmony_ci	if (!asoc)
41398c2ecf20Sopenharmony_ci		return -EINVAL;
41408c2ecf20Sopenharmony_ci
41418c2ecf20Sopenharmony_ci	return sctp_send_add_streams(asoc, params);
41428c2ecf20Sopenharmony_ci}
41438c2ecf20Sopenharmony_ci
41448c2ecf20Sopenharmony_cistatic int sctp_setsockopt_scheduler(struct sock *sk,
41458c2ecf20Sopenharmony_ci				     struct sctp_assoc_value *params,
41468c2ecf20Sopenharmony_ci				     unsigned int optlen)
41478c2ecf20Sopenharmony_ci{
41488c2ecf20Sopenharmony_ci	struct sctp_sock *sp = sctp_sk(sk);
41498c2ecf20Sopenharmony_ci	struct sctp_association *asoc;
41508c2ecf20Sopenharmony_ci	int retval = 0;
41518c2ecf20Sopenharmony_ci
41528c2ecf20Sopenharmony_ci	if (optlen < sizeof(*params))
41538c2ecf20Sopenharmony_ci		return -EINVAL;
41548c2ecf20Sopenharmony_ci
41558c2ecf20Sopenharmony_ci	if (params->assoc_value > SCTP_SS_MAX)
41568c2ecf20Sopenharmony_ci		return -EINVAL;
41578c2ecf20Sopenharmony_ci
41588c2ecf20Sopenharmony_ci	asoc = sctp_id2assoc(sk, params->assoc_id);
41598c2ecf20Sopenharmony_ci	if (!asoc && params->assoc_id > SCTP_ALL_ASSOC &&
41608c2ecf20Sopenharmony_ci	    sctp_style(sk, UDP))
41618c2ecf20Sopenharmony_ci		return -EINVAL;
41628c2ecf20Sopenharmony_ci
41638c2ecf20Sopenharmony_ci	if (asoc)
41648c2ecf20Sopenharmony_ci		return sctp_sched_set_sched(asoc, params->assoc_value);
41658c2ecf20Sopenharmony_ci
41668c2ecf20Sopenharmony_ci	if (sctp_style(sk, TCP))
41678c2ecf20Sopenharmony_ci		params->assoc_id = SCTP_FUTURE_ASSOC;
41688c2ecf20Sopenharmony_ci
41698c2ecf20Sopenharmony_ci	if (params->assoc_id == SCTP_FUTURE_ASSOC ||
41708c2ecf20Sopenharmony_ci	    params->assoc_id == SCTP_ALL_ASSOC)
41718c2ecf20Sopenharmony_ci		sp->default_ss = params->assoc_value;
41728c2ecf20Sopenharmony_ci
41738c2ecf20Sopenharmony_ci	if (params->assoc_id == SCTP_CURRENT_ASSOC ||
41748c2ecf20Sopenharmony_ci	    params->assoc_id == SCTP_ALL_ASSOC) {
41758c2ecf20Sopenharmony_ci		list_for_each_entry(asoc, &sp->ep->asocs, asocs) {
41768c2ecf20Sopenharmony_ci			int ret = sctp_sched_set_sched(asoc,
41778c2ecf20Sopenharmony_ci						       params->assoc_value);
41788c2ecf20Sopenharmony_ci
41798c2ecf20Sopenharmony_ci			if (ret && !retval)
41808c2ecf20Sopenharmony_ci				retval = ret;
41818c2ecf20Sopenharmony_ci		}
41828c2ecf20Sopenharmony_ci	}
41838c2ecf20Sopenharmony_ci
41848c2ecf20Sopenharmony_ci	return retval;
41858c2ecf20Sopenharmony_ci}
41868c2ecf20Sopenharmony_ci
41878c2ecf20Sopenharmony_cistatic int sctp_setsockopt_scheduler_value(struct sock *sk,
41888c2ecf20Sopenharmony_ci					   struct sctp_stream_value *params,
41898c2ecf20Sopenharmony_ci					   unsigned int optlen)
41908c2ecf20Sopenharmony_ci{
41918c2ecf20Sopenharmony_ci	struct sctp_association *asoc;
41928c2ecf20Sopenharmony_ci	int retval = -EINVAL;
41938c2ecf20Sopenharmony_ci
41948c2ecf20Sopenharmony_ci	if (optlen < sizeof(*params))
41958c2ecf20Sopenharmony_ci		goto out;
41968c2ecf20Sopenharmony_ci
41978c2ecf20Sopenharmony_ci	asoc = sctp_id2assoc(sk, params->assoc_id);
41988c2ecf20Sopenharmony_ci	if (!asoc && params->assoc_id != SCTP_CURRENT_ASSOC &&
41998c2ecf20Sopenharmony_ci	    sctp_style(sk, UDP))
42008c2ecf20Sopenharmony_ci		goto out;
42018c2ecf20Sopenharmony_ci
42028c2ecf20Sopenharmony_ci	if (asoc) {
42038c2ecf20Sopenharmony_ci		retval = sctp_sched_set_value(asoc, params->stream_id,
42048c2ecf20Sopenharmony_ci					      params->stream_value, GFP_KERNEL);
42058c2ecf20Sopenharmony_ci		goto out;
42068c2ecf20Sopenharmony_ci	}
42078c2ecf20Sopenharmony_ci
42088c2ecf20Sopenharmony_ci	retval = 0;
42098c2ecf20Sopenharmony_ci
42108c2ecf20Sopenharmony_ci	list_for_each_entry(asoc, &sctp_sk(sk)->ep->asocs, asocs) {
42118c2ecf20Sopenharmony_ci		int ret = sctp_sched_set_value(asoc, params->stream_id,
42128c2ecf20Sopenharmony_ci					       params->stream_value,
42138c2ecf20Sopenharmony_ci					       GFP_KERNEL);
42148c2ecf20Sopenharmony_ci		if (ret && !retval) /* try to return the 1st error. */
42158c2ecf20Sopenharmony_ci			retval = ret;
42168c2ecf20Sopenharmony_ci	}
42178c2ecf20Sopenharmony_ci
42188c2ecf20Sopenharmony_ciout:
42198c2ecf20Sopenharmony_ci	return retval;
42208c2ecf20Sopenharmony_ci}
42218c2ecf20Sopenharmony_ci
42228c2ecf20Sopenharmony_cistatic int sctp_setsockopt_interleaving_supported(struct sock *sk,
42238c2ecf20Sopenharmony_ci						  struct sctp_assoc_value *p,
42248c2ecf20Sopenharmony_ci						  unsigned int optlen)
42258c2ecf20Sopenharmony_ci{
42268c2ecf20Sopenharmony_ci	struct sctp_sock *sp = sctp_sk(sk);
42278c2ecf20Sopenharmony_ci	struct sctp_association *asoc;
42288c2ecf20Sopenharmony_ci
42298c2ecf20Sopenharmony_ci	if (optlen < sizeof(*p))
42308c2ecf20Sopenharmony_ci		return -EINVAL;
42318c2ecf20Sopenharmony_ci
42328c2ecf20Sopenharmony_ci	asoc = sctp_id2assoc(sk, p->assoc_id);
42338c2ecf20Sopenharmony_ci	if (!asoc && p->assoc_id != SCTP_FUTURE_ASSOC && sctp_style(sk, UDP))
42348c2ecf20Sopenharmony_ci		return -EINVAL;
42358c2ecf20Sopenharmony_ci
42368c2ecf20Sopenharmony_ci	if (!sock_net(sk)->sctp.intl_enable || !sp->frag_interleave) {
42378c2ecf20Sopenharmony_ci		return -EPERM;
42388c2ecf20Sopenharmony_ci	}
42398c2ecf20Sopenharmony_ci
42408c2ecf20Sopenharmony_ci	sp->ep->intl_enable = !!p->assoc_value;
42418c2ecf20Sopenharmony_ci	return 0;
42428c2ecf20Sopenharmony_ci}
42438c2ecf20Sopenharmony_ci
42448c2ecf20Sopenharmony_cistatic int sctp_setsockopt_reuse_port(struct sock *sk, int *val,
42458c2ecf20Sopenharmony_ci				      unsigned int optlen)
42468c2ecf20Sopenharmony_ci{
42478c2ecf20Sopenharmony_ci	if (!sctp_style(sk, TCP))
42488c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
42498c2ecf20Sopenharmony_ci
42508c2ecf20Sopenharmony_ci	if (sctp_sk(sk)->ep->base.bind_addr.port)
42518c2ecf20Sopenharmony_ci		return -EFAULT;
42528c2ecf20Sopenharmony_ci
42538c2ecf20Sopenharmony_ci	if (optlen < sizeof(int))
42548c2ecf20Sopenharmony_ci		return -EINVAL;
42558c2ecf20Sopenharmony_ci
42568c2ecf20Sopenharmony_ci	sctp_sk(sk)->reuse = !!*val;
42578c2ecf20Sopenharmony_ci
42588c2ecf20Sopenharmony_ci	return 0;
42598c2ecf20Sopenharmony_ci}
42608c2ecf20Sopenharmony_ci
42618c2ecf20Sopenharmony_cistatic int sctp_assoc_ulpevent_type_set(struct sctp_event *param,
42628c2ecf20Sopenharmony_ci					struct sctp_association *asoc)
42638c2ecf20Sopenharmony_ci{
42648c2ecf20Sopenharmony_ci	struct sctp_ulpevent *event;
42658c2ecf20Sopenharmony_ci
42668c2ecf20Sopenharmony_ci	sctp_ulpevent_type_set(&asoc->subscribe, param->se_type, param->se_on);
42678c2ecf20Sopenharmony_ci
42688c2ecf20Sopenharmony_ci	if (param->se_type == SCTP_SENDER_DRY_EVENT && param->se_on) {
42698c2ecf20Sopenharmony_ci		if (sctp_outq_is_empty(&asoc->outqueue)) {
42708c2ecf20Sopenharmony_ci			event = sctp_ulpevent_make_sender_dry_event(asoc,
42718c2ecf20Sopenharmony_ci					GFP_USER | __GFP_NOWARN);
42728c2ecf20Sopenharmony_ci			if (!event)
42738c2ecf20Sopenharmony_ci				return -ENOMEM;
42748c2ecf20Sopenharmony_ci
42758c2ecf20Sopenharmony_ci			asoc->stream.si->enqueue_event(&asoc->ulpq, event);
42768c2ecf20Sopenharmony_ci		}
42778c2ecf20Sopenharmony_ci	}
42788c2ecf20Sopenharmony_ci
42798c2ecf20Sopenharmony_ci	return 0;
42808c2ecf20Sopenharmony_ci}
42818c2ecf20Sopenharmony_ci
42828c2ecf20Sopenharmony_cistatic int sctp_setsockopt_event(struct sock *sk, struct sctp_event *param,
42838c2ecf20Sopenharmony_ci				 unsigned int optlen)
42848c2ecf20Sopenharmony_ci{
42858c2ecf20Sopenharmony_ci	struct sctp_sock *sp = sctp_sk(sk);
42868c2ecf20Sopenharmony_ci	struct sctp_association *asoc;
42878c2ecf20Sopenharmony_ci	int retval = 0;
42888c2ecf20Sopenharmony_ci
42898c2ecf20Sopenharmony_ci	if (optlen < sizeof(*param))
42908c2ecf20Sopenharmony_ci		return -EINVAL;
42918c2ecf20Sopenharmony_ci
42928c2ecf20Sopenharmony_ci	if (param->se_type < SCTP_SN_TYPE_BASE ||
42938c2ecf20Sopenharmony_ci	    param->se_type > SCTP_SN_TYPE_MAX)
42948c2ecf20Sopenharmony_ci		return -EINVAL;
42958c2ecf20Sopenharmony_ci
42968c2ecf20Sopenharmony_ci	asoc = sctp_id2assoc(sk, param->se_assoc_id);
42978c2ecf20Sopenharmony_ci	if (!asoc && param->se_assoc_id > SCTP_ALL_ASSOC &&
42988c2ecf20Sopenharmony_ci	    sctp_style(sk, UDP))
42998c2ecf20Sopenharmony_ci		return -EINVAL;
43008c2ecf20Sopenharmony_ci
43018c2ecf20Sopenharmony_ci	if (asoc)
43028c2ecf20Sopenharmony_ci		return sctp_assoc_ulpevent_type_set(param, asoc);
43038c2ecf20Sopenharmony_ci
43048c2ecf20Sopenharmony_ci	if (sctp_style(sk, TCP))
43058c2ecf20Sopenharmony_ci		param->se_assoc_id = SCTP_FUTURE_ASSOC;
43068c2ecf20Sopenharmony_ci
43078c2ecf20Sopenharmony_ci	if (param->se_assoc_id == SCTP_FUTURE_ASSOC ||
43088c2ecf20Sopenharmony_ci	    param->se_assoc_id == SCTP_ALL_ASSOC)
43098c2ecf20Sopenharmony_ci		sctp_ulpevent_type_set(&sp->subscribe,
43108c2ecf20Sopenharmony_ci				       param->se_type, param->se_on);
43118c2ecf20Sopenharmony_ci
43128c2ecf20Sopenharmony_ci	if (param->se_assoc_id == SCTP_CURRENT_ASSOC ||
43138c2ecf20Sopenharmony_ci	    param->se_assoc_id == SCTP_ALL_ASSOC) {
43148c2ecf20Sopenharmony_ci		list_for_each_entry(asoc, &sp->ep->asocs, asocs) {
43158c2ecf20Sopenharmony_ci			int ret = sctp_assoc_ulpevent_type_set(param, asoc);
43168c2ecf20Sopenharmony_ci
43178c2ecf20Sopenharmony_ci			if (ret && !retval)
43188c2ecf20Sopenharmony_ci				retval = ret;
43198c2ecf20Sopenharmony_ci		}
43208c2ecf20Sopenharmony_ci	}
43218c2ecf20Sopenharmony_ci
43228c2ecf20Sopenharmony_ci	return retval;
43238c2ecf20Sopenharmony_ci}
43248c2ecf20Sopenharmony_ci
43258c2ecf20Sopenharmony_cistatic int sctp_setsockopt_asconf_supported(struct sock *sk,
43268c2ecf20Sopenharmony_ci					    struct sctp_assoc_value *params,
43278c2ecf20Sopenharmony_ci					    unsigned int optlen)
43288c2ecf20Sopenharmony_ci{
43298c2ecf20Sopenharmony_ci	struct sctp_association *asoc;
43308c2ecf20Sopenharmony_ci	struct sctp_endpoint *ep;
43318c2ecf20Sopenharmony_ci	int retval = -EINVAL;
43328c2ecf20Sopenharmony_ci
43338c2ecf20Sopenharmony_ci	if (optlen != sizeof(*params))
43348c2ecf20Sopenharmony_ci		goto out;
43358c2ecf20Sopenharmony_ci
43368c2ecf20Sopenharmony_ci	asoc = sctp_id2assoc(sk, params->assoc_id);
43378c2ecf20Sopenharmony_ci	if (!asoc && params->assoc_id != SCTP_FUTURE_ASSOC &&
43388c2ecf20Sopenharmony_ci	    sctp_style(sk, UDP))
43398c2ecf20Sopenharmony_ci		goto out;
43408c2ecf20Sopenharmony_ci
43418c2ecf20Sopenharmony_ci	ep = sctp_sk(sk)->ep;
43428c2ecf20Sopenharmony_ci	ep->asconf_enable = !!params->assoc_value;
43438c2ecf20Sopenharmony_ci
43448c2ecf20Sopenharmony_ci	if (ep->asconf_enable && ep->auth_enable) {
43458c2ecf20Sopenharmony_ci		sctp_auth_ep_add_chunkid(ep, SCTP_CID_ASCONF);
43468c2ecf20Sopenharmony_ci		sctp_auth_ep_add_chunkid(ep, SCTP_CID_ASCONF_ACK);
43478c2ecf20Sopenharmony_ci	}
43488c2ecf20Sopenharmony_ci
43498c2ecf20Sopenharmony_ci	retval = 0;
43508c2ecf20Sopenharmony_ci
43518c2ecf20Sopenharmony_ciout:
43528c2ecf20Sopenharmony_ci	return retval;
43538c2ecf20Sopenharmony_ci}
43548c2ecf20Sopenharmony_ci
43558c2ecf20Sopenharmony_cistatic int sctp_setsockopt_auth_supported(struct sock *sk,
43568c2ecf20Sopenharmony_ci					  struct sctp_assoc_value *params,
43578c2ecf20Sopenharmony_ci					  unsigned int optlen)
43588c2ecf20Sopenharmony_ci{
43598c2ecf20Sopenharmony_ci	struct sctp_association *asoc;
43608c2ecf20Sopenharmony_ci	struct sctp_endpoint *ep;
43618c2ecf20Sopenharmony_ci	int retval = -EINVAL;
43628c2ecf20Sopenharmony_ci
43638c2ecf20Sopenharmony_ci	if (optlen != sizeof(*params))
43648c2ecf20Sopenharmony_ci		goto out;
43658c2ecf20Sopenharmony_ci
43668c2ecf20Sopenharmony_ci	asoc = sctp_id2assoc(sk, params->assoc_id);
43678c2ecf20Sopenharmony_ci	if (!asoc && params->assoc_id != SCTP_FUTURE_ASSOC &&
43688c2ecf20Sopenharmony_ci	    sctp_style(sk, UDP))
43698c2ecf20Sopenharmony_ci		goto out;
43708c2ecf20Sopenharmony_ci
43718c2ecf20Sopenharmony_ci	ep = sctp_sk(sk)->ep;
43728c2ecf20Sopenharmony_ci	if (params->assoc_value) {
43738c2ecf20Sopenharmony_ci		retval = sctp_auth_init(ep, GFP_KERNEL);
43748c2ecf20Sopenharmony_ci		if (retval)
43758c2ecf20Sopenharmony_ci			goto out;
43768c2ecf20Sopenharmony_ci		if (ep->asconf_enable) {
43778c2ecf20Sopenharmony_ci			sctp_auth_ep_add_chunkid(ep, SCTP_CID_ASCONF);
43788c2ecf20Sopenharmony_ci			sctp_auth_ep_add_chunkid(ep, SCTP_CID_ASCONF_ACK);
43798c2ecf20Sopenharmony_ci		}
43808c2ecf20Sopenharmony_ci	}
43818c2ecf20Sopenharmony_ci
43828c2ecf20Sopenharmony_ci	ep->auth_enable = !!params->assoc_value;
43838c2ecf20Sopenharmony_ci	retval = 0;
43848c2ecf20Sopenharmony_ci
43858c2ecf20Sopenharmony_ciout:
43868c2ecf20Sopenharmony_ci	return retval;
43878c2ecf20Sopenharmony_ci}
43888c2ecf20Sopenharmony_ci
43898c2ecf20Sopenharmony_cistatic int sctp_setsockopt_ecn_supported(struct sock *sk,
43908c2ecf20Sopenharmony_ci					 struct sctp_assoc_value *params,
43918c2ecf20Sopenharmony_ci					 unsigned int optlen)
43928c2ecf20Sopenharmony_ci{
43938c2ecf20Sopenharmony_ci	struct sctp_association *asoc;
43948c2ecf20Sopenharmony_ci	int retval = -EINVAL;
43958c2ecf20Sopenharmony_ci
43968c2ecf20Sopenharmony_ci	if (optlen != sizeof(*params))
43978c2ecf20Sopenharmony_ci		goto out;
43988c2ecf20Sopenharmony_ci
43998c2ecf20Sopenharmony_ci	asoc = sctp_id2assoc(sk, params->assoc_id);
44008c2ecf20Sopenharmony_ci	if (!asoc && params->assoc_id != SCTP_FUTURE_ASSOC &&
44018c2ecf20Sopenharmony_ci	    sctp_style(sk, UDP))
44028c2ecf20Sopenharmony_ci		goto out;
44038c2ecf20Sopenharmony_ci
44048c2ecf20Sopenharmony_ci	sctp_sk(sk)->ep->ecn_enable = !!params->assoc_value;
44058c2ecf20Sopenharmony_ci	retval = 0;
44068c2ecf20Sopenharmony_ci
44078c2ecf20Sopenharmony_ciout:
44088c2ecf20Sopenharmony_ci	return retval;
44098c2ecf20Sopenharmony_ci}
44108c2ecf20Sopenharmony_ci
44118c2ecf20Sopenharmony_cistatic int sctp_setsockopt_pf_expose(struct sock *sk,
44128c2ecf20Sopenharmony_ci				     struct sctp_assoc_value *params,
44138c2ecf20Sopenharmony_ci				     unsigned int optlen)
44148c2ecf20Sopenharmony_ci{
44158c2ecf20Sopenharmony_ci	struct sctp_association *asoc;
44168c2ecf20Sopenharmony_ci	int retval = -EINVAL;
44178c2ecf20Sopenharmony_ci
44188c2ecf20Sopenharmony_ci	if (optlen != sizeof(*params))
44198c2ecf20Sopenharmony_ci		goto out;
44208c2ecf20Sopenharmony_ci
44218c2ecf20Sopenharmony_ci	if (params->assoc_value > SCTP_PF_EXPOSE_MAX)
44228c2ecf20Sopenharmony_ci		goto out;
44238c2ecf20Sopenharmony_ci
44248c2ecf20Sopenharmony_ci	asoc = sctp_id2assoc(sk, params->assoc_id);
44258c2ecf20Sopenharmony_ci	if (!asoc && params->assoc_id != SCTP_FUTURE_ASSOC &&
44268c2ecf20Sopenharmony_ci	    sctp_style(sk, UDP))
44278c2ecf20Sopenharmony_ci		goto out;
44288c2ecf20Sopenharmony_ci
44298c2ecf20Sopenharmony_ci	if (asoc)
44308c2ecf20Sopenharmony_ci		asoc->pf_expose = params->assoc_value;
44318c2ecf20Sopenharmony_ci	else
44328c2ecf20Sopenharmony_ci		sctp_sk(sk)->pf_expose = params->assoc_value;
44338c2ecf20Sopenharmony_ci	retval = 0;
44348c2ecf20Sopenharmony_ci
44358c2ecf20Sopenharmony_ciout:
44368c2ecf20Sopenharmony_ci	return retval;
44378c2ecf20Sopenharmony_ci}
44388c2ecf20Sopenharmony_ci
44398c2ecf20Sopenharmony_ci/* API 6.2 setsockopt(), getsockopt()
44408c2ecf20Sopenharmony_ci *
44418c2ecf20Sopenharmony_ci * Applications use setsockopt() and getsockopt() to set or retrieve
44428c2ecf20Sopenharmony_ci * socket options.  Socket options are used to change the default
44438c2ecf20Sopenharmony_ci * behavior of sockets calls.  They are described in Section 7.
44448c2ecf20Sopenharmony_ci *
44458c2ecf20Sopenharmony_ci * The syntax is:
44468c2ecf20Sopenharmony_ci *
44478c2ecf20Sopenharmony_ci *   ret = getsockopt(int sd, int level, int optname, void __user *optval,
44488c2ecf20Sopenharmony_ci *                    int __user *optlen);
44498c2ecf20Sopenharmony_ci *   ret = setsockopt(int sd, int level, int optname, const void __user *optval,
44508c2ecf20Sopenharmony_ci *                    int optlen);
44518c2ecf20Sopenharmony_ci *
44528c2ecf20Sopenharmony_ci *   sd      - the socket descript.
44538c2ecf20Sopenharmony_ci *   level   - set to IPPROTO_SCTP for all SCTP options.
44548c2ecf20Sopenharmony_ci *   optname - the option name.
44558c2ecf20Sopenharmony_ci *   optval  - the buffer to store the value of the option.
44568c2ecf20Sopenharmony_ci *   optlen  - the size of the buffer.
44578c2ecf20Sopenharmony_ci */
44588c2ecf20Sopenharmony_cistatic int sctp_setsockopt(struct sock *sk, int level, int optname,
44598c2ecf20Sopenharmony_ci			   sockptr_t optval, unsigned int optlen)
44608c2ecf20Sopenharmony_ci{
44618c2ecf20Sopenharmony_ci	void *kopt = NULL;
44628c2ecf20Sopenharmony_ci	int retval = 0;
44638c2ecf20Sopenharmony_ci
44648c2ecf20Sopenharmony_ci	pr_debug("%s: sk:%p, optname:%d\n", __func__, sk, optname);
44658c2ecf20Sopenharmony_ci
44668c2ecf20Sopenharmony_ci	/* I can hardly begin to describe how wrong this is.  This is
44678c2ecf20Sopenharmony_ci	 * so broken as to be worse than useless.  The API draft
44688c2ecf20Sopenharmony_ci	 * REALLY is NOT helpful here...  I am not convinced that the
44698c2ecf20Sopenharmony_ci	 * semantics of setsockopt() with a level OTHER THAN SOL_SCTP
44708c2ecf20Sopenharmony_ci	 * are at all well-founded.
44718c2ecf20Sopenharmony_ci	 */
44728c2ecf20Sopenharmony_ci	if (level != SOL_SCTP) {
44738c2ecf20Sopenharmony_ci		struct sctp_af *af = sctp_sk(sk)->pf->af;
44748c2ecf20Sopenharmony_ci
44758c2ecf20Sopenharmony_ci		return af->setsockopt(sk, level, optname, optval, optlen);
44768c2ecf20Sopenharmony_ci	}
44778c2ecf20Sopenharmony_ci
44788c2ecf20Sopenharmony_ci	if (optlen > 0) {
44798c2ecf20Sopenharmony_ci		/* Trim it to the biggest size sctp sockopt may need if necessary */
44808c2ecf20Sopenharmony_ci		optlen = min_t(unsigned int, optlen,
44818c2ecf20Sopenharmony_ci			       PAGE_ALIGN(USHRT_MAX +
44828c2ecf20Sopenharmony_ci					  sizeof(__u16) * sizeof(struct sctp_reset_streams)));
44838c2ecf20Sopenharmony_ci		kopt = memdup_sockptr(optval, optlen);
44848c2ecf20Sopenharmony_ci		if (IS_ERR(kopt))
44858c2ecf20Sopenharmony_ci			return PTR_ERR(kopt);
44868c2ecf20Sopenharmony_ci	}
44878c2ecf20Sopenharmony_ci
44888c2ecf20Sopenharmony_ci	lock_sock(sk);
44898c2ecf20Sopenharmony_ci
44908c2ecf20Sopenharmony_ci	switch (optname) {
44918c2ecf20Sopenharmony_ci	case SCTP_SOCKOPT_BINDX_ADD:
44928c2ecf20Sopenharmony_ci		/* 'optlen' is the size of the addresses buffer. */
44938c2ecf20Sopenharmony_ci		retval = sctp_setsockopt_bindx(sk, kopt, optlen,
44948c2ecf20Sopenharmony_ci					       SCTP_BINDX_ADD_ADDR);
44958c2ecf20Sopenharmony_ci		break;
44968c2ecf20Sopenharmony_ci
44978c2ecf20Sopenharmony_ci	case SCTP_SOCKOPT_BINDX_REM:
44988c2ecf20Sopenharmony_ci		/* 'optlen' is the size of the addresses buffer. */
44998c2ecf20Sopenharmony_ci		retval = sctp_setsockopt_bindx(sk, kopt, optlen,
45008c2ecf20Sopenharmony_ci					       SCTP_BINDX_REM_ADDR);
45018c2ecf20Sopenharmony_ci		break;
45028c2ecf20Sopenharmony_ci
45038c2ecf20Sopenharmony_ci	case SCTP_SOCKOPT_CONNECTX_OLD:
45048c2ecf20Sopenharmony_ci		/* 'optlen' is the size of the addresses buffer. */
45058c2ecf20Sopenharmony_ci		retval = sctp_setsockopt_connectx_old(sk, kopt, optlen);
45068c2ecf20Sopenharmony_ci		break;
45078c2ecf20Sopenharmony_ci
45088c2ecf20Sopenharmony_ci	case SCTP_SOCKOPT_CONNECTX:
45098c2ecf20Sopenharmony_ci		/* 'optlen' is the size of the addresses buffer. */
45108c2ecf20Sopenharmony_ci		retval = sctp_setsockopt_connectx(sk, kopt, optlen);
45118c2ecf20Sopenharmony_ci		break;
45128c2ecf20Sopenharmony_ci
45138c2ecf20Sopenharmony_ci	case SCTP_DISABLE_FRAGMENTS:
45148c2ecf20Sopenharmony_ci		retval = sctp_setsockopt_disable_fragments(sk, kopt, optlen);
45158c2ecf20Sopenharmony_ci		break;
45168c2ecf20Sopenharmony_ci
45178c2ecf20Sopenharmony_ci	case SCTP_EVENTS:
45188c2ecf20Sopenharmony_ci		retval = sctp_setsockopt_events(sk, kopt, optlen);
45198c2ecf20Sopenharmony_ci		break;
45208c2ecf20Sopenharmony_ci
45218c2ecf20Sopenharmony_ci	case SCTP_AUTOCLOSE:
45228c2ecf20Sopenharmony_ci		retval = sctp_setsockopt_autoclose(sk, kopt, optlen);
45238c2ecf20Sopenharmony_ci		break;
45248c2ecf20Sopenharmony_ci
45258c2ecf20Sopenharmony_ci	case SCTP_PEER_ADDR_PARAMS:
45268c2ecf20Sopenharmony_ci		retval = sctp_setsockopt_peer_addr_params(sk, kopt, optlen);
45278c2ecf20Sopenharmony_ci		break;
45288c2ecf20Sopenharmony_ci
45298c2ecf20Sopenharmony_ci	case SCTP_DELAYED_SACK:
45308c2ecf20Sopenharmony_ci		retval = sctp_setsockopt_delayed_ack(sk, kopt, optlen);
45318c2ecf20Sopenharmony_ci		break;
45328c2ecf20Sopenharmony_ci	case SCTP_PARTIAL_DELIVERY_POINT:
45338c2ecf20Sopenharmony_ci		retval = sctp_setsockopt_partial_delivery_point(sk, kopt, optlen);
45348c2ecf20Sopenharmony_ci		break;
45358c2ecf20Sopenharmony_ci
45368c2ecf20Sopenharmony_ci	case SCTP_INITMSG:
45378c2ecf20Sopenharmony_ci		retval = sctp_setsockopt_initmsg(sk, kopt, optlen);
45388c2ecf20Sopenharmony_ci		break;
45398c2ecf20Sopenharmony_ci	case SCTP_DEFAULT_SEND_PARAM:
45408c2ecf20Sopenharmony_ci		retval = sctp_setsockopt_default_send_param(sk, kopt, optlen);
45418c2ecf20Sopenharmony_ci		break;
45428c2ecf20Sopenharmony_ci	case SCTP_DEFAULT_SNDINFO:
45438c2ecf20Sopenharmony_ci		retval = sctp_setsockopt_default_sndinfo(sk, kopt, optlen);
45448c2ecf20Sopenharmony_ci		break;
45458c2ecf20Sopenharmony_ci	case SCTP_PRIMARY_ADDR:
45468c2ecf20Sopenharmony_ci		retval = sctp_setsockopt_primary_addr(sk, kopt, optlen);
45478c2ecf20Sopenharmony_ci		break;
45488c2ecf20Sopenharmony_ci	case SCTP_SET_PEER_PRIMARY_ADDR:
45498c2ecf20Sopenharmony_ci		retval = sctp_setsockopt_peer_primary_addr(sk, kopt, optlen);
45508c2ecf20Sopenharmony_ci		break;
45518c2ecf20Sopenharmony_ci	case SCTP_NODELAY:
45528c2ecf20Sopenharmony_ci		retval = sctp_setsockopt_nodelay(sk, kopt, optlen);
45538c2ecf20Sopenharmony_ci		break;
45548c2ecf20Sopenharmony_ci	case SCTP_RTOINFO:
45558c2ecf20Sopenharmony_ci		retval = sctp_setsockopt_rtoinfo(sk, kopt, optlen);
45568c2ecf20Sopenharmony_ci		break;
45578c2ecf20Sopenharmony_ci	case SCTP_ASSOCINFO:
45588c2ecf20Sopenharmony_ci		retval = sctp_setsockopt_associnfo(sk, kopt, optlen);
45598c2ecf20Sopenharmony_ci		break;
45608c2ecf20Sopenharmony_ci	case SCTP_I_WANT_MAPPED_V4_ADDR:
45618c2ecf20Sopenharmony_ci		retval = sctp_setsockopt_mappedv4(sk, kopt, optlen);
45628c2ecf20Sopenharmony_ci		break;
45638c2ecf20Sopenharmony_ci	case SCTP_MAXSEG:
45648c2ecf20Sopenharmony_ci		retval = sctp_setsockopt_maxseg(sk, kopt, optlen);
45658c2ecf20Sopenharmony_ci		break;
45668c2ecf20Sopenharmony_ci	case SCTP_ADAPTATION_LAYER:
45678c2ecf20Sopenharmony_ci		retval = sctp_setsockopt_adaptation_layer(sk, kopt, optlen);
45688c2ecf20Sopenharmony_ci		break;
45698c2ecf20Sopenharmony_ci	case SCTP_CONTEXT:
45708c2ecf20Sopenharmony_ci		retval = sctp_setsockopt_context(sk, kopt, optlen);
45718c2ecf20Sopenharmony_ci		break;
45728c2ecf20Sopenharmony_ci	case SCTP_FRAGMENT_INTERLEAVE:
45738c2ecf20Sopenharmony_ci		retval = sctp_setsockopt_fragment_interleave(sk, kopt, optlen);
45748c2ecf20Sopenharmony_ci		break;
45758c2ecf20Sopenharmony_ci	case SCTP_MAX_BURST:
45768c2ecf20Sopenharmony_ci		retval = sctp_setsockopt_maxburst(sk, kopt, optlen);
45778c2ecf20Sopenharmony_ci		break;
45788c2ecf20Sopenharmony_ci	case SCTP_AUTH_CHUNK:
45798c2ecf20Sopenharmony_ci		retval = sctp_setsockopt_auth_chunk(sk, kopt, optlen);
45808c2ecf20Sopenharmony_ci		break;
45818c2ecf20Sopenharmony_ci	case SCTP_HMAC_IDENT:
45828c2ecf20Sopenharmony_ci		retval = sctp_setsockopt_hmac_ident(sk, kopt, optlen);
45838c2ecf20Sopenharmony_ci		break;
45848c2ecf20Sopenharmony_ci	case SCTP_AUTH_KEY:
45858c2ecf20Sopenharmony_ci		retval = sctp_setsockopt_auth_key(sk, kopt, optlen);
45868c2ecf20Sopenharmony_ci		break;
45878c2ecf20Sopenharmony_ci	case SCTP_AUTH_ACTIVE_KEY:
45888c2ecf20Sopenharmony_ci		retval = sctp_setsockopt_active_key(sk, kopt, optlen);
45898c2ecf20Sopenharmony_ci		break;
45908c2ecf20Sopenharmony_ci	case SCTP_AUTH_DELETE_KEY:
45918c2ecf20Sopenharmony_ci		retval = sctp_setsockopt_del_key(sk, kopt, optlen);
45928c2ecf20Sopenharmony_ci		break;
45938c2ecf20Sopenharmony_ci	case SCTP_AUTH_DEACTIVATE_KEY:
45948c2ecf20Sopenharmony_ci		retval = sctp_setsockopt_deactivate_key(sk, kopt, optlen);
45958c2ecf20Sopenharmony_ci		break;
45968c2ecf20Sopenharmony_ci	case SCTP_AUTO_ASCONF:
45978c2ecf20Sopenharmony_ci		retval = sctp_setsockopt_auto_asconf(sk, kopt, optlen);
45988c2ecf20Sopenharmony_ci		break;
45998c2ecf20Sopenharmony_ci	case SCTP_PEER_ADDR_THLDS:
46008c2ecf20Sopenharmony_ci		retval = sctp_setsockopt_paddr_thresholds(sk, kopt, optlen,
46018c2ecf20Sopenharmony_ci							  false);
46028c2ecf20Sopenharmony_ci		break;
46038c2ecf20Sopenharmony_ci	case SCTP_PEER_ADDR_THLDS_V2:
46048c2ecf20Sopenharmony_ci		retval = sctp_setsockopt_paddr_thresholds(sk, kopt, optlen,
46058c2ecf20Sopenharmony_ci							  true);
46068c2ecf20Sopenharmony_ci		break;
46078c2ecf20Sopenharmony_ci	case SCTP_RECVRCVINFO:
46088c2ecf20Sopenharmony_ci		retval = sctp_setsockopt_recvrcvinfo(sk, kopt, optlen);
46098c2ecf20Sopenharmony_ci		break;
46108c2ecf20Sopenharmony_ci	case SCTP_RECVNXTINFO:
46118c2ecf20Sopenharmony_ci		retval = sctp_setsockopt_recvnxtinfo(sk, kopt, optlen);
46128c2ecf20Sopenharmony_ci		break;
46138c2ecf20Sopenharmony_ci	case SCTP_PR_SUPPORTED:
46148c2ecf20Sopenharmony_ci		retval = sctp_setsockopt_pr_supported(sk, kopt, optlen);
46158c2ecf20Sopenharmony_ci		break;
46168c2ecf20Sopenharmony_ci	case SCTP_DEFAULT_PRINFO:
46178c2ecf20Sopenharmony_ci		retval = sctp_setsockopt_default_prinfo(sk, kopt, optlen);
46188c2ecf20Sopenharmony_ci		break;
46198c2ecf20Sopenharmony_ci	case SCTP_RECONFIG_SUPPORTED:
46208c2ecf20Sopenharmony_ci		retval = sctp_setsockopt_reconfig_supported(sk, kopt, optlen);
46218c2ecf20Sopenharmony_ci		break;
46228c2ecf20Sopenharmony_ci	case SCTP_ENABLE_STREAM_RESET:
46238c2ecf20Sopenharmony_ci		retval = sctp_setsockopt_enable_strreset(sk, kopt, optlen);
46248c2ecf20Sopenharmony_ci		break;
46258c2ecf20Sopenharmony_ci	case SCTP_RESET_STREAMS:
46268c2ecf20Sopenharmony_ci		retval = sctp_setsockopt_reset_streams(sk, kopt, optlen);
46278c2ecf20Sopenharmony_ci		break;
46288c2ecf20Sopenharmony_ci	case SCTP_RESET_ASSOC:
46298c2ecf20Sopenharmony_ci		retval = sctp_setsockopt_reset_assoc(sk, kopt, optlen);
46308c2ecf20Sopenharmony_ci		break;
46318c2ecf20Sopenharmony_ci	case SCTP_ADD_STREAMS:
46328c2ecf20Sopenharmony_ci		retval = sctp_setsockopt_add_streams(sk, kopt, optlen);
46338c2ecf20Sopenharmony_ci		break;
46348c2ecf20Sopenharmony_ci	case SCTP_STREAM_SCHEDULER:
46358c2ecf20Sopenharmony_ci		retval = sctp_setsockopt_scheduler(sk, kopt, optlen);
46368c2ecf20Sopenharmony_ci		break;
46378c2ecf20Sopenharmony_ci	case SCTP_STREAM_SCHEDULER_VALUE:
46388c2ecf20Sopenharmony_ci		retval = sctp_setsockopt_scheduler_value(sk, kopt, optlen);
46398c2ecf20Sopenharmony_ci		break;
46408c2ecf20Sopenharmony_ci	case SCTP_INTERLEAVING_SUPPORTED:
46418c2ecf20Sopenharmony_ci		retval = sctp_setsockopt_interleaving_supported(sk, kopt,
46428c2ecf20Sopenharmony_ci								optlen);
46438c2ecf20Sopenharmony_ci		break;
46448c2ecf20Sopenharmony_ci	case SCTP_REUSE_PORT:
46458c2ecf20Sopenharmony_ci		retval = sctp_setsockopt_reuse_port(sk, kopt, optlen);
46468c2ecf20Sopenharmony_ci		break;
46478c2ecf20Sopenharmony_ci	case SCTP_EVENT:
46488c2ecf20Sopenharmony_ci		retval = sctp_setsockopt_event(sk, kopt, optlen);
46498c2ecf20Sopenharmony_ci		break;
46508c2ecf20Sopenharmony_ci	case SCTP_ASCONF_SUPPORTED:
46518c2ecf20Sopenharmony_ci		retval = sctp_setsockopt_asconf_supported(sk, kopt, optlen);
46528c2ecf20Sopenharmony_ci		break;
46538c2ecf20Sopenharmony_ci	case SCTP_AUTH_SUPPORTED:
46548c2ecf20Sopenharmony_ci		retval = sctp_setsockopt_auth_supported(sk, kopt, optlen);
46558c2ecf20Sopenharmony_ci		break;
46568c2ecf20Sopenharmony_ci	case SCTP_ECN_SUPPORTED:
46578c2ecf20Sopenharmony_ci		retval = sctp_setsockopt_ecn_supported(sk, kopt, optlen);
46588c2ecf20Sopenharmony_ci		break;
46598c2ecf20Sopenharmony_ci	case SCTP_EXPOSE_POTENTIALLY_FAILED_STATE:
46608c2ecf20Sopenharmony_ci		retval = sctp_setsockopt_pf_expose(sk, kopt, optlen);
46618c2ecf20Sopenharmony_ci		break;
46628c2ecf20Sopenharmony_ci	default:
46638c2ecf20Sopenharmony_ci		retval = -ENOPROTOOPT;
46648c2ecf20Sopenharmony_ci		break;
46658c2ecf20Sopenharmony_ci	}
46668c2ecf20Sopenharmony_ci
46678c2ecf20Sopenharmony_ci	release_sock(sk);
46688c2ecf20Sopenharmony_ci	kfree(kopt);
46698c2ecf20Sopenharmony_ci	return retval;
46708c2ecf20Sopenharmony_ci}
46718c2ecf20Sopenharmony_ci
46728c2ecf20Sopenharmony_ci/* API 3.1.6 connect() - UDP Style Syntax
46738c2ecf20Sopenharmony_ci *
46748c2ecf20Sopenharmony_ci * An application may use the connect() call in the UDP model to initiate an
46758c2ecf20Sopenharmony_ci * association without sending data.
46768c2ecf20Sopenharmony_ci *
46778c2ecf20Sopenharmony_ci * The syntax is:
46788c2ecf20Sopenharmony_ci *
46798c2ecf20Sopenharmony_ci * ret = connect(int sd, const struct sockaddr *nam, socklen_t len);
46808c2ecf20Sopenharmony_ci *
46818c2ecf20Sopenharmony_ci * sd: the socket descriptor to have a new association added to.
46828c2ecf20Sopenharmony_ci *
46838c2ecf20Sopenharmony_ci * nam: the address structure (either struct sockaddr_in or struct
46848c2ecf20Sopenharmony_ci *    sockaddr_in6 defined in RFC2553 [7]).
46858c2ecf20Sopenharmony_ci *
46868c2ecf20Sopenharmony_ci * len: the size of the address.
46878c2ecf20Sopenharmony_ci */
46888c2ecf20Sopenharmony_cistatic int sctp_connect(struct sock *sk, struct sockaddr *addr,
46898c2ecf20Sopenharmony_ci			int addr_len, int flags)
46908c2ecf20Sopenharmony_ci{
46918c2ecf20Sopenharmony_ci	struct sctp_af *af;
46928c2ecf20Sopenharmony_ci	int err = -EINVAL;
46938c2ecf20Sopenharmony_ci
46948c2ecf20Sopenharmony_ci	lock_sock(sk);
46958c2ecf20Sopenharmony_ci	pr_debug("%s: sk:%p, sockaddr:%p, addr_len:%d\n", __func__, sk,
46968c2ecf20Sopenharmony_ci		 addr, addr_len);
46978c2ecf20Sopenharmony_ci
46988c2ecf20Sopenharmony_ci	/* Validate addr_len before calling common connect/connectx routine. */
46998c2ecf20Sopenharmony_ci	af = sctp_get_af_specific(addr->sa_family);
47008c2ecf20Sopenharmony_ci	if (af && addr_len >= af->sockaddr_len)
47018c2ecf20Sopenharmony_ci		err = __sctp_connect(sk, addr, af->sockaddr_len, flags, NULL);
47028c2ecf20Sopenharmony_ci
47038c2ecf20Sopenharmony_ci	release_sock(sk);
47048c2ecf20Sopenharmony_ci	return err;
47058c2ecf20Sopenharmony_ci}
47068c2ecf20Sopenharmony_ci
47078c2ecf20Sopenharmony_ciint sctp_inet_connect(struct socket *sock, struct sockaddr *uaddr,
47088c2ecf20Sopenharmony_ci		      int addr_len, int flags)
47098c2ecf20Sopenharmony_ci{
47108c2ecf20Sopenharmony_ci	if (addr_len < sizeof(uaddr->sa_family))
47118c2ecf20Sopenharmony_ci		return -EINVAL;
47128c2ecf20Sopenharmony_ci
47138c2ecf20Sopenharmony_ci	if (uaddr->sa_family == AF_UNSPEC)
47148c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
47158c2ecf20Sopenharmony_ci
47168c2ecf20Sopenharmony_ci	return sctp_connect(sock->sk, uaddr, addr_len, flags);
47178c2ecf20Sopenharmony_ci}
47188c2ecf20Sopenharmony_ci
47198c2ecf20Sopenharmony_ci/* FIXME: Write comments. */
47208c2ecf20Sopenharmony_cistatic int sctp_disconnect(struct sock *sk, int flags)
47218c2ecf20Sopenharmony_ci{
47228c2ecf20Sopenharmony_ci	return -EOPNOTSUPP; /* STUB */
47238c2ecf20Sopenharmony_ci}
47248c2ecf20Sopenharmony_ci
47258c2ecf20Sopenharmony_ci/* 4.1.4 accept() - TCP Style Syntax
47268c2ecf20Sopenharmony_ci *
47278c2ecf20Sopenharmony_ci * Applications use accept() call to remove an established SCTP
47288c2ecf20Sopenharmony_ci * association from the accept queue of the endpoint.  A new socket
47298c2ecf20Sopenharmony_ci * descriptor will be returned from accept() to represent the newly
47308c2ecf20Sopenharmony_ci * formed association.
47318c2ecf20Sopenharmony_ci */
47328c2ecf20Sopenharmony_cistatic struct sock *sctp_accept(struct sock *sk, int flags, int *err, bool kern)
47338c2ecf20Sopenharmony_ci{
47348c2ecf20Sopenharmony_ci	struct sctp_sock *sp;
47358c2ecf20Sopenharmony_ci	struct sctp_endpoint *ep;
47368c2ecf20Sopenharmony_ci	struct sock *newsk = NULL;
47378c2ecf20Sopenharmony_ci	struct sctp_association *asoc;
47388c2ecf20Sopenharmony_ci	long timeo;
47398c2ecf20Sopenharmony_ci	int error = 0;
47408c2ecf20Sopenharmony_ci
47418c2ecf20Sopenharmony_ci	lock_sock(sk);
47428c2ecf20Sopenharmony_ci
47438c2ecf20Sopenharmony_ci	sp = sctp_sk(sk);
47448c2ecf20Sopenharmony_ci	ep = sp->ep;
47458c2ecf20Sopenharmony_ci
47468c2ecf20Sopenharmony_ci	if (!sctp_style(sk, TCP)) {
47478c2ecf20Sopenharmony_ci		error = -EOPNOTSUPP;
47488c2ecf20Sopenharmony_ci		goto out;
47498c2ecf20Sopenharmony_ci	}
47508c2ecf20Sopenharmony_ci
47518c2ecf20Sopenharmony_ci	if (!sctp_sstate(sk, LISTENING)) {
47528c2ecf20Sopenharmony_ci		error = -EINVAL;
47538c2ecf20Sopenharmony_ci		goto out;
47548c2ecf20Sopenharmony_ci	}
47558c2ecf20Sopenharmony_ci
47568c2ecf20Sopenharmony_ci	timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK);
47578c2ecf20Sopenharmony_ci
47588c2ecf20Sopenharmony_ci	error = sctp_wait_for_accept(sk, timeo);
47598c2ecf20Sopenharmony_ci	if (error)
47608c2ecf20Sopenharmony_ci		goto out;
47618c2ecf20Sopenharmony_ci
47628c2ecf20Sopenharmony_ci	/* We treat the list of associations on the endpoint as the accept
47638c2ecf20Sopenharmony_ci	 * queue and pick the first association on the list.
47648c2ecf20Sopenharmony_ci	 */
47658c2ecf20Sopenharmony_ci	asoc = list_entry(ep->asocs.next, struct sctp_association, asocs);
47668c2ecf20Sopenharmony_ci
47678c2ecf20Sopenharmony_ci	newsk = sp->pf->create_accept_sk(sk, asoc, kern);
47688c2ecf20Sopenharmony_ci	if (!newsk) {
47698c2ecf20Sopenharmony_ci		error = -ENOMEM;
47708c2ecf20Sopenharmony_ci		goto out;
47718c2ecf20Sopenharmony_ci	}
47728c2ecf20Sopenharmony_ci
47738c2ecf20Sopenharmony_ci	/* Populate the fields of the newsk from the oldsk and migrate the
47748c2ecf20Sopenharmony_ci	 * asoc to the newsk.
47758c2ecf20Sopenharmony_ci	 */
47768c2ecf20Sopenharmony_ci	error = sctp_sock_migrate(sk, newsk, asoc, SCTP_SOCKET_TCP);
47778c2ecf20Sopenharmony_ci	if (error) {
47788c2ecf20Sopenharmony_ci		sk_common_release(newsk);
47798c2ecf20Sopenharmony_ci		newsk = NULL;
47808c2ecf20Sopenharmony_ci	}
47818c2ecf20Sopenharmony_ci
47828c2ecf20Sopenharmony_ciout:
47838c2ecf20Sopenharmony_ci	release_sock(sk);
47848c2ecf20Sopenharmony_ci	*err = error;
47858c2ecf20Sopenharmony_ci	return newsk;
47868c2ecf20Sopenharmony_ci}
47878c2ecf20Sopenharmony_ci
47888c2ecf20Sopenharmony_ci/* The SCTP ioctl handler. */
47898c2ecf20Sopenharmony_cistatic int sctp_ioctl(struct sock *sk, int cmd, unsigned long arg)
47908c2ecf20Sopenharmony_ci{
47918c2ecf20Sopenharmony_ci	int rc = -ENOTCONN;
47928c2ecf20Sopenharmony_ci
47938c2ecf20Sopenharmony_ci	lock_sock(sk);
47948c2ecf20Sopenharmony_ci
47958c2ecf20Sopenharmony_ci	/*
47968c2ecf20Sopenharmony_ci	 * SEQPACKET-style sockets in LISTENING state are valid, for
47978c2ecf20Sopenharmony_ci	 * SCTP, so only discard TCP-style sockets in LISTENING state.
47988c2ecf20Sopenharmony_ci	 */
47998c2ecf20Sopenharmony_ci	if (sctp_style(sk, TCP) && sctp_sstate(sk, LISTENING))
48008c2ecf20Sopenharmony_ci		goto out;
48018c2ecf20Sopenharmony_ci
48028c2ecf20Sopenharmony_ci	switch (cmd) {
48038c2ecf20Sopenharmony_ci	case SIOCINQ: {
48048c2ecf20Sopenharmony_ci		struct sk_buff *skb;
48058c2ecf20Sopenharmony_ci		unsigned int amount = 0;
48068c2ecf20Sopenharmony_ci
48078c2ecf20Sopenharmony_ci		skb = skb_peek(&sk->sk_receive_queue);
48088c2ecf20Sopenharmony_ci		if (skb != NULL) {
48098c2ecf20Sopenharmony_ci			/*
48108c2ecf20Sopenharmony_ci			 * We will only return the amount of this packet since
48118c2ecf20Sopenharmony_ci			 * that is all that will be read.
48128c2ecf20Sopenharmony_ci			 */
48138c2ecf20Sopenharmony_ci			amount = skb->len;
48148c2ecf20Sopenharmony_ci		}
48158c2ecf20Sopenharmony_ci		rc = put_user(amount, (int __user *)arg);
48168c2ecf20Sopenharmony_ci		break;
48178c2ecf20Sopenharmony_ci	}
48188c2ecf20Sopenharmony_ci	default:
48198c2ecf20Sopenharmony_ci		rc = -ENOIOCTLCMD;
48208c2ecf20Sopenharmony_ci		break;
48218c2ecf20Sopenharmony_ci	}
48228c2ecf20Sopenharmony_ciout:
48238c2ecf20Sopenharmony_ci	release_sock(sk);
48248c2ecf20Sopenharmony_ci	return rc;
48258c2ecf20Sopenharmony_ci}
48268c2ecf20Sopenharmony_ci
48278c2ecf20Sopenharmony_ci/* This is the function which gets called during socket creation to
48288c2ecf20Sopenharmony_ci * initialized the SCTP-specific portion of the sock.
48298c2ecf20Sopenharmony_ci * The sock structure should already be zero-filled memory.
48308c2ecf20Sopenharmony_ci */
48318c2ecf20Sopenharmony_cistatic int sctp_init_sock(struct sock *sk)
48328c2ecf20Sopenharmony_ci{
48338c2ecf20Sopenharmony_ci	struct net *net = sock_net(sk);
48348c2ecf20Sopenharmony_ci	struct sctp_sock *sp;
48358c2ecf20Sopenharmony_ci
48368c2ecf20Sopenharmony_ci	pr_debug("%s: sk:%p\n", __func__, sk);
48378c2ecf20Sopenharmony_ci
48388c2ecf20Sopenharmony_ci	sp = sctp_sk(sk);
48398c2ecf20Sopenharmony_ci
48408c2ecf20Sopenharmony_ci	/* Initialize the SCTP per socket area.  */
48418c2ecf20Sopenharmony_ci	switch (sk->sk_type) {
48428c2ecf20Sopenharmony_ci	case SOCK_SEQPACKET:
48438c2ecf20Sopenharmony_ci		sp->type = SCTP_SOCKET_UDP;
48448c2ecf20Sopenharmony_ci		break;
48458c2ecf20Sopenharmony_ci	case SOCK_STREAM:
48468c2ecf20Sopenharmony_ci		sp->type = SCTP_SOCKET_TCP;
48478c2ecf20Sopenharmony_ci		break;
48488c2ecf20Sopenharmony_ci	default:
48498c2ecf20Sopenharmony_ci		return -ESOCKTNOSUPPORT;
48508c2ecf20Sopenharmony_ci	}
48518c2ecf20Sopenharmony_ci
48528c2ecf20Sopenharmony_ci	sk->sk_gso_type = SKB_GSO_SCTP;
48538c2ecf20Sopenharmony_ci
48548c2ecf20Sopenharmony_ci	/* Initialize default send parameters. These parameters can be
48558c2ecf20Sopenharmony_ci	 * modified with the SCTP_DEFAULT_SEND_PARAM socket option.
48568c2ecf20Sopenharmony_ci	 */
48578c2ecf20Sopenharmony_ci	sp->default_stream = 0;
48588c2ecf20Sopenharmony_ci	sp->default_ppid = 0;
48598c2ecf20Sopenharmony_ci	sp->default_flags = 0;
48608c2ecf20Sopenharmony_ci	sp->default_context = 0;
48618c2ecf20Sopenharmony_ci	sp->default_timetolive = 0;
48628c2ecf20Sopenharmony_ci
48638c2ecf20Sopenharmony_ci	sp->default_rcv_context = 0;
48648c2ecf20Sopenharmony_ci	sp->max_burst = net->sctp.max_burst;
48658c2ecf20Sopenharmony_ci
48668c2ecf20Sopenharmony_ci	sp->sctp_hmac_alg = net->sctp.sctp_hmac_alg;
48678c2ecf20Sopenharmony_ci
48688c2ecf20Sopenharmony_ci	/* Initialize default setup parameters. These parameters
48698c2ecf20Sopenharmony_ci	 * can be modified with the SCTP_INITMSG socket option or
48708c2ecf20Sopenharmony_ci	 * overridden by the SCTP_INIT CMSG.
48718c2ecf20Sopenharmony_ci	 */
48728c2ecf20Sopenharmony_ci	sp->initmsg.sinit_num_ostreams   = sctp_max_outstreams;
48738c2ecf20Sopenharmony_ci	sp->initmsg.sinit_max_instreams  = sctp_max_instreams;
48748c2ecf20Sopenharmony_ci	sp->initmsg.sinit_max_attempts   = net->sctp.max_retrans_init;
48758c2ecf20Sopenharmony_ci	sp->initmsg.sinit_max_init_timeo = net->sctp.rto_max;
48768c2ecf20Sopenharmony_ci
48778c2ecf20Sopenharmony_ci	/* Initialize default RTO related parameters.  These parameters can
48788c2ecf20Sopenharmony_ci	 * be modified for with the SCTP_RTOINFO socket option.
48798c2ecf20Sopenharmony_ci	 */
48808c2ecf20Sopenharmony_ci	sp->rtoinfo.srto_initial = net->sctp.rto_initial;
48818c2ecf20Sopenharmony_ci	sp->rtoinfo.srto_max     = net->sctp.rto_max;
48828c2ecf20Sopenharmony_ci	sp->rtoinfo.srto_min     = net->sctp.rto_min;
48838c2ecf20Sopenharmony_ci
48848c2ecf20Sopenharmony_ci	/* Initialize default association related parameters. These parameters
48858c2ecf20Sopenharmony_ci	 * can be modified with the SCTP_ASSOCINFO socket option.
48868c2ecf20Sopenharmony_ci	 */
48878c2ecf20Sopenharmony_ci	sp->assocparams.sasoc_asocmaxrxt = net->sctp.max_retrans_association;
48888c2ecf20Sopenharmony_ci	sp->assocparams.sasoc_number_peer_destinations = 0;
48898c2ecf20Sopenharmony_ci	sp->assocparams.sasoc_peer_rwnd = 0;
48908c2ecf20Sopenharmony_ci	sp->assocparams.sasoc_local_rwnd = 0;
48918c2ecf20Sopenharmony_ci	sp->assocparams.sasoc_cookie_life = net->sctp.valid_cookie_life;
48928c2ecf20Sopenharmony_ci
48938c2ecf20Sopenharmony_ci	/* Initialize default event subscriptions. By default, all the
48948c2ecf20Sopenharmony_ci	 * options are off.
48958c2ecf20Sopenharmony_ci	 */
48968c2ecf20Sopenharmony_ci	sp->subscribe = 0;
48978c2ecf20Sopenharmony_ci
48988c2ecf20Sopenharmony_ci	/* Default Peer Address Parameters.  These defaults can
48998c2ecf20Sopenharmony_ci	 * be modified via SCTP_PEER_ADDR_PARAMS
49008c2ecf20Sopenharmony_ci	 */
49018c2ecf20Sopenharmony_ci	sp->hbinterval  = net->sctp.hb_interval;
49028c2ecf20Sopenharmony_ci	sp->pathmaxrxt  = net->sctp.max_retrans_path;
49038c2ecf20Sopenharmony_ci	sp->pf_retrans  = net->sctp.pf_retrans;
49048c2ecf20Sopenharmony_ci	sp->ps_retrans  = net->sctp.ps_retrans;
49058c2ecf20Sopenharmony_ci	sp->pf_expose   = net->sctp.pf_expose;
49068c2ecf20Sopenharmony_ci	sp->pathmtu     = 0; /* allow default discovery */
49078c2ecf20Sopenharmony_ci	sp->sackdelay   = net->sctp.sack_timeout;
49088c2ecf20Sopenharmony_ci	sp->sackfreq	= 2;
49098c2ecf20Sopenharmony_ci	sp->param_flags = SPP_HB_ENABLE |
49108c2ecf20Sopenharmony_ci			  SPP_PMTUD_ENABLE |
49118c2ecf20Sopenharmony_ci			  SPP_SACKDELAY_ENABLE;
49128c2ecf20Sopenharmony_ci	sp->default_ss = SCTP_SS_DEFAULT;
49138c2ecf20Sopenharmony_ci
49148c2ecf20Sopenharmony_ci	/* If enabled no SCTP message fragmentation will be performed.
49158c2ecf20Sopenharmony_ci	 * Configure through SCTP_DISABLE_FRAGMENTS socket option.
49168c2ecf20Sopenharmony_ci	 */
49178c2ecf20Sopenharmony_ci	sp->disable_fragments = 0;
49188c2ecf20Sopenharmony_ci
49198c2ecf20Sopenharmony_ci	/* Enable Nagle algorithm by default.  */
49208c2ecf20Sopenharmony_ci	sp->nodelay           = 0;
49218c2ecf20Sopenharmony_ci
49228c2ecf20Sopenharmony_ci	sp->recvrcvinfo = 0;
49238c2ecf20Sopenharmony_ci	sp->recvnxtinfo = 0;
49248c2ecf20Sopenharmony_ci
49258c2ecf20Sopenharmony_ci	/* Enable by default. */
49268c2ecf20Sopenharmony_ci	sp->v4mapped          = 1;
49278c2ecf20Sopenharmony_ci
49288c2ecf20Sopenharmony_ci	/* Auto-close idle associations after the configured
49298c2ecf20Sopenharmony_ci	 * number of seconds.  A value of 0 disables this
49308c2ecf20Sopenharmony_ci	 * feature.  Configure through the SCTP_AUTOCLOSE socket option,
49318c2ecf20Sopenharmony_ci	 * for UDP-style sockets only.
49328c2ecf20Sopenharmony_ci	 */
49338c2ecf20Sopenharmony_ci	sp->autoclose         = 0;
49348c2ecf20Sopenharmony_ci
49358c2ecf20Sopenharmony_ci	/* User specified fragmentation limit. */
49368c2ecf20Sopenharmony_ci	sp->user_frag         = 0;
49378c2ecf20Sopenharmony_ci
49388c2ecf20Sopenharmony_ci	sp->adaptation_ind = 0;
49398c2ecf20Sopenharmony_ci
49408c2ecf20Sopenharmony_ci	sp->pf = sctp_get_pf_specific(sk->sk_family);
49418c2ecf20Sopenharmony_ci
49428c2ecf20Sopenharmony_ci	/* Control variables for partial data delivery. */
49438c2ecf20Sopenharmony_ci	atomic_set(&sp->pd_mode, 0);
49448c2ecf20Sopenharmony_ci	skb_queue_head_init(&sp->pd_lobby);
49458c2ecf20Sopenharmony_ci	sp->frag_interleave = 0;
49468c2ecf20Sopenharmony_ci
49478c2ecf20Sopenharmony_ci	/* Create a per socket endpoint structure.  Even if we
49488c2ecf20Sopenharmony_ci	 * change the data structure relationships, this may still
49498c2ecf20Sopenharmony_ci	 * be useful for storing pre-connect address information.
49508c2ecf20Sopenharmony_ci	 */
49518c2ecf20Sopenharmony_ci	sp->ep = sctp_endpoint_new(sk, GFP_KERNEL);
49528c2ecf20Sopenharmony_ci	if (!sp->ep)
49538c2ecf20Sopenharmony_ci		return -ENOMEM;
49548c2ecf20Sopenharmony_ci
49558c2ecf20Sopenharmony_ci	sp->hmac = NULL;
49568c2ecf20Sopenharmony_ci
49578c2ecf20Sopenharmony_ci	sk->sk_destruct = sctp_destruct_sock;
49588c2ecf20Sopenharmony_ci
49598c2ecf20Sopenharmony_ci	SCTP_DBG_OBJCNT_INC(sock);
49608c2ecf20Sopenharmony_ci
49618c2ecf20Sopenharmony_ci	local_bh_disable();
49628c2ecf20Sopenharmony_ci	sk_sockets_allocated_inc(sk);
49638c2ecf20Sopenharmony_ci	sock_prot_inuse_add(net, sk->sk_prot, 1);
49648c2ecf20Sopenharmony_ci
49658c2ecf20Sopenharmony_ci	local_bh_enable();
49668c2ecf20Sopenharmony_ci
49678c2ecf20Sopenharmony_ci	return 0;
49688c2ecf20Sopenharmony_ci}
49698c2ecf20Sopenharmony_ci
49708c2ecf20Sopenharmony_ci/* Cleanup any SCTP per socket resources. Must be called with
49718c2ecf20Sopenharmony_ci * sock_net(sk)->sctp.addr_wq_lock held if sp->do_auto_asconf is true
49728c2ecf20Sopenharmony_ci */
49738c2ecf20Sopenharmony_cistatic void sctp_destroy_sock(struct sock *sk)
49748c2ecf20Sopenharmony_ci{
49758c2ecf20Sopenharmony_ci	struct sctp_sock *sp;
49768c2ecf20Sopenharmony_ci
49778c2ecf20Sopenharmony_ci	pr_debug("%s: sk:%p\n", __func__, sk);
49788c2ecf20Sopenharmony_ci
49798c2ecf20Sopenharmony_ci	/* Release our hold on the endpoint. */
49808c2ecf20Sopenharmony_ci	sp = sctp_sk(sk);
49818c2ecf20Sopenharmony_ci	/* This could happen during socket init, thus we bail out
49828c2ecf20Sopenharmony_ci	 * early, since the rest of the below is not setup either.
49838c2ecf20Sopenharmony_ci	 */
49848c2ecf20Sopenharmony_ci	if (sp->ep == NULL)
49858c2ecf20Sopenharmony_ci		return;
49868c2ecf20Sopenharmony_ci
49878c2ecf20Sopenharmony_ci	if (sp->do_auto_asconf) {
49888c2ecf20Sopenharmony_ci		sp->do_auto_asconf = 0;
49898c2ecf20Sopenharmony_ci		list_del(&sp->auto_asconf_list);
49908c2ecf20Sopenharmony_ci	}
49918c2ecf20Sopenharmony_ci	sctp_endpoint_free(sp->ep);
49928c2ecf20Sopenharmony_ci	local_bh_disable();
49938c2ecf20Sopenharmony_ci	sk_sockets_allocated_dec(sk);
49948c2ecf20Sopenharmony_ci	sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1);
49958c2ecf20Sopenharmony_ci	local_bh_enable();
49968c2ecf20Sopenharmony_ci}
49978c2ecf20Sopenharmony_ci
49988c2ecf20Sopenharmony_ci/* Triggered when there are no references on the socket anymore */
49998c2ecf20Sopenharmony_cistatic void sctp_destruct_common(struct sock *sk)
50008c2ecf20Sopenharmony_ci{
50018c2ecf20Sopenharmony_ci	struct sctp_sock *sp = sctp_sk(sk);
50028c2ecf20Sopenharmony_ci
50038c2ecf20Sopenharmony_ci	/* Free up the HMAC transform. */
50048c2ecf20Sopenharmony_ci	crypto_free_shash(sp->hmac);
50058c2ecf20Sopenharmony_ci}
50068c2ecf20Sopenharmony_ci
50078c2ecf20Sopenharmony_cistatic void sctp_destruct_sock(struct sock *sk)
50088c2ecf20Sopenharmony_ci{
50098c2ecf20Sopenharmony_ci	sctp_destruct_common(sk);
50108c2ecf20Sopenharmony_ci	inet_sock_destruct(sk);
50118c2ecf20Sopenharmony_ci}
50128c2ecf20Sopenharmony_ci
50138c2ecf20Sopenharmony_ci/* API 4.1.7 shutdown() - TCP Style Syntax
50148c2ecf20Sopenharmony_ci *     int shutdown(int socket, int how);
50158c2ecf20Sopenharmony_ci *
50168c2ecf20Sopenharmony_ci *     sd      - the socket descriptor of the association to be closed.
50178c2ecf20Sopenharmony_ci *     how     - Specifies the type of shutdown.  The  values  are
50188c2ecf20Sopenharmony_ci *               as follows:
50198c2ecf20Sopenharmony_ci *               SHUT_RD
50208c2ecf20Sopenharmony_ci *                     Disables further receive operations. No SCTP
50218c2ecf20Sopenharmony_ci *                     protocol action is taken.
50228c2ecf20Sopenharmony_ci *               SHUT_WR
50238c2ecf20Sopenharmony_ci *                     Disables further send operations, and initiates
50248c2ecf20Sopenharmony_ci *                     the SCTP shutdown sequence.
50258c2ecf20Sopenharmony_ci *               SHUT_RDWR
50268c2ecf20Sopenharmony_ci *                     Disables further send  and  receive  operations
50278c2ecf20Sopenharmony_ci *                     and initiates the SCTP shutdown sequence.
50288c2ecf20Sopenharmony_ci */
50298c2ecf20Sopenharmony_cistatic void sctp_shutdown(struct sock *sk, int how)
50308c2ecf20Sopenharmony_ci{
50318c2ecf20Sopenharmony_ci	struct net *net = sock_net(sk);
50328c2ecf20Sopenharmony_ci	struct sctp_endpoint *ep;
50338c2ecf20Sopenharmony_ci
50348c2ecf20Sopenharmony_ci	if (!sctp_style(sk, TCP))
50358c2ecf20Sopenharmony_ci		return;
50368c2ecf20Sopenharmony_ci
50378c2ecf20Sopenharmony_ci	ep = sctp_sk(sk)->ep;
50388c2ecf20Sopenharmony_ci	if (how & SEND_SHUTDOWN && !list_empty(&ep->asocs)) {
50398c2ecf20Sopenharmony_ci		struct sctp_association *asoc;
50408c2ecf20Sopenharmony_ci
50418c2ecf20Sopenharmony_ci		inet_sk_set_state(sk, SCTP_SS_CLOSING);
50428c2ecf20Sopenharmony_ci		asoc = list_entry(ep->asocs.next,
50438c2ecf20Sopenharmony_ci				  struct sctp_association, asocs);
50448c2ecf20Sopenharmony_ci		sctp_primitive_SHUTDOWN(net, asoc, NULL);
50458c2ecf20Sopenharmony_ci	}
50468c2ecf20Sopenharmony_ci}
50478c2ecf20Sopenharmony_ci
50488c2ecf20Sopenharmony_ciint sctp_get_sctp_info(struct sock *sk, struct sctp_association *asoc,
50498c2ecf20Sopenharmony_ci		       struct sctp_info *info)
50508c2ecf20Sopenharmony_ci{
50518c2ecf20Sopenharmony_ci	struct sctp_transport *prim;
50528c2ecf20Sopenharmony_ci	struct list_head *pos;
50538c2ecf20Sopenharmony_ci	int mask;
50548c2ecf20Sopenharmony_ci
50558c2ecf20Sopenharmony_ci	memset(info, 0, sizeof(*info));
50568c2ecf20Sopenharmony_ci	if (!asoc) {
50578c2ecf20Sopenharmony_ci		struct sctp_sock *sp = sctp_sk(sk);
50588c2ecf20Sopenharmony_ci
50598c2ecf20Sopenharmony_ci		info->sctpi_s_autoclose = sp->autoclose;
50608c2ecf20Sopenharmony_ci		info->sctpi_s_adaptation_ind = sp->adaptation_ind;
50618c2ecf20Sopenharmony_ci		info->sctpi_s_pd_point = sp->pd_point;
50628c2ecf20Sopenharmony_ci		info->sctpi_s_nodelay = sp->nodelay;
50638c2ecf20Sopenharmony_ci		info->sctpi_s_disable_fragments = sp->disable_fragments;
50648c2ecf20Sopenharmony_ci		info->sctpi_s_v4mapped = sp->v4mapped;
50658c2ecf20Sopenharmony_ci		info->sctpi_s_frag_interleave = sp->frag_interleave;
50668c2ecf20Sopenharmony_ci		info->sctpi_s_type = sp->type;
50678c2ecf20Sopenharmony_ci
50688c2ecf20Sopenharmony_ci		return 0;
50698c2ecf20Sopenharmony_ci	}
50708c2ecf20Sopenharmony_ci
50718c2ecf20Sopenharmony_ci	info->sctpi_tag = asoc->c.my_vtag;
50728c2ecf20Sopenharmony_ci	info->sctpi_state = asoc->state;
50738c2ecf20Sopenharmony_ci	info->sctpi_rwnd = asoc->a_rwnd;
50748c2ecf20Sopenharmony_ci	info->sctpi_unackdata = asoc->unack_data;
50758c2ecf20Sopenharmony_ci	info->sctpi_penddata = sctp_tsnmap_pending(&asoc->peer.tsn_map);
50768c2ecf20Sopenharmony_ci	info->sctpi_instrms = asoc->stream.incnt;
50778c2ecf20Sopenharmony_ci	info->sctpi_outstrms = asoc->stream.outcnt;
50788c2ecf20Sopenharmony_ci	list_for_each(pos, &asoc->base.inqueue.in_chunk_list)
50798c2ecf20Sopenharmony_ci		info->sctpi_inqueue++;
50808c2ecf20Sopenharmony_ci	list_for_each(pos, &asoc->outqueue.out_chunk_list)
50818c2ecf20Sopenharmony_ci		info->sctpi_outqueue++;
50828c2ecf20Sopenharmony_ci	info->sctpi_overall_error = asoc->overall_error_count;
50838c2ecf20Sopenharmony_ci	info->sctpi_max_burst = asoc->max_burst;
50848c2ecf20Sopenharmony_ci	info->sctpi_maxseg = asoc->frag_point;
50858c2ecf20Sopenharmony_ci	info->sctpi_peer_rwnd = asoc->peer.rwnd;
50868c2ecf20Sopenharmony_ci	info->sctpi_peer_tag = asoc->c.peer_vtag;
50878c2ecf20Sopenharmony_ci
50888c2ecf20Sopenharmony_ci	mask = asoc->peer.ecn_capable << 1;
50898c2ecf20Sopenharmony_ci	mask = (mask | asoc->peer.ipv4_address) << 1;
50908c2ecf20Sopenharmony_ci	mask = (mask | asoc->peer.ipv6_address) << 1;
50918c2ecf20Sopenharmony_ci	mask = (mask | asoc->peer.hostname_address) << 1;
50928c2ecf20Sopenharmony_ci	mask = (mask | asoc->peer.asconf_capable) << 1;
50938c2ecf20Sopenharmony_ci	mask = (mask | asoc->peer.prsctp_capable) << 1;
50948c2ecf20Sopenharmony_ci	mask = (mask | asoc->peer.auth_capable);
50958c2ecf20Sopenharmony_ci	info->sctpi_peer_capable = mask;
50968c2ecf20Sopenharmony_ci	mask = asoc->peer.sack_needed << 1;
50978c2ecf20Sopenharmony_ci	mask = (mask | asoc->peer.sack_generation) << 1;
50988c2ecf20Sopenharmony_ci	mask = (mask | asoc->peer.zero_window_announced);
50998c2ecf20Sopenharmony_ci	info->sctpi_peer_sack = mask;
51008c2ecf20Sopenharmony_ci
51018c2ecf20Sopenharmony_ci	info->sctpi_isacks = asoc->stats.isacks;
51028c2ecf20Sopenharmony_ci	info->sctpi_osacks = asoc->stats.osacks;
51038c2ecf20Sopenharmony_ci	info->sctpi_opackets = asoc->stats.opackets;
51048c2ecf20Sopenharmony_ci	info->sctpi_ipackets = asoc->stats.ipackets;
51058c2ecf20Sopenharmony_ci	info->sctpi_rtxchunks = asoc->stats.rtxchunks;
51068c2ecf20Sopenharmony_ci	info->sctpi_outofseqtsns = asoc->stats.outofseqtsns;
51078c2ecf20Sopenharmony_ci	info->sctpi_idupchunks = asoc->stats.idupchunks;
51088c2ecf20Sopenharmony_ci	info->sctpi_gapcnt = asoc->stats.gapcnt;
51098c2ecf20Sopenharmony_ci	info->sctpi_ouodchunks = asoc->stats.ouodchunks;
51108c2ecf20Sopenharmony_ci	info->sctpi_iuodchunks = asoc->stats.iuodchunks;
51118c2ecf20Sopenharmony_ci	info->sctpi_oodchunks = asoc->stats.oodchunks;
51128c2ecf20Sopenharmony_ci	info->sctpi_iodchunks = asoc->stats.iodchunks;
51138c2ecf20Sopenharmony_ci	info->sctpi_octrlchunks = asoc->stats.octrlchunks;
51148c2ecf20Sopenharmony_ci	info->sctpi_ictrlchunks = asoc->stats.ictrlchunks;
51158c2ecf20Sopenharmony_ci
51168c2ecf20Sopenharmony_ci	prim = asoc->peer.primary_path;
51178c2ecf20Sopenharmony_ci	memcpy(&info->sctpi_p_address, &prim->ipaddr, sizeof(prim->ipaddr));
51188c2ecf20Sopenharmony_ci	info->sctpi_p_state = prim->state;
51198c2ecf20Sopenharmony_ci	info->sctpi_p_cwnd = prim->cwnd;
51208c2ecf20Sopenharmony_ci	info->sctpi_p_srtt = prim->srtt;
51218c2ecf20Sopenharmony_ci	info->sctpi_p_rto = jiffies_to_msecs(prim->rto);
51228c2ecf20Sopenharmony_ci	info->sctpi_p_hbinterval = prim->hbinterval;
51238c2ecf20Sopenharmony_ci	info->sctpi_p_pathmaxrxt = prim->pathmaxrxt;
51248c2ecf20Sopenharmony_ci	info->sctpi_p_sackdelay = jiffies_to_msecs(prim->sackdelay);
51258c2ecf20Sopenharmony_ci	info->sctpi_p_ssthresh = prim->ssthresh;
51268c2ecf20Sopenharmony_ci	info->sctpi_p_partial_bytes_acked = prim->partial_bytes_acked;
51278c2ecf20Sopenharmony_ci	info->sctpi_p_flight_size = prim->flight_size;
51288c2ecf20Sopenharmony_ci	info->sctpi_p_error = prim->error_count;
51298c2ecf20Sopenharmony_ci
51308c2ecf20Sopenharmony_ci	return 0;
51318c2ecf20Sopenharmony_ci}
51328c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sctp_get_sctp_info);
51338c2ecf20Sopenharmony_ci
51348c2ecf20Sopenharmony_ci/* use callback to avoid exporting the core structure */
51358c2ecf20Sopenharmony_civoid sctp_transport_walk_start(struct rhashtable_iter *iter) __acquires(RCU)
51368c2ecf20Sopenharmony_ci{
51378c2ecf20Sopenharmony_ci	rhltable_walk_enter(&sctp_transport_hashtable, iter);
51388c2ecf20Sopenharmony_ci
51398c2ecf20Sopenharmony_ci	rhashtable_walk_start(iter);
51408c2ecf20Sopenharmony_ci}
51418c2ecf20Sopenharmony_ci
51428c2ecf20Sopenharmony_civoid sctp_transport_walk_stop(struct rhashtable_iter *iter) __releases(RCU)
51438c2ecf20Sopenharmony_ci{
51448c2ecf20Sopenharmony_ci	rhashtable_walk_stop(iter);
51458c2ecf20Sopenharmony_ci	rhashtable_walk_exit(iter);
51468c2ecf20Sopenharmony_ci}
51478c2ecf20Sopenharmony_ci
51488c2ecf20Sopenharmony_cistruct sctp_transport *sctp_transport_get_next(struct net *net,
51498c2ecf20Sopenharmony_ci					       struct rhashtable_iter *iter)
51508c2ecf20Sopenharmony_ci{
51518c2ecf20Sopenharmony_ci	struct sctp_transport *t;
51528c2ecf20Sopenharmony_ci
51538c2ecf20Sopenharmony_ci	t = rhashtable_walk_next(iter);
51548c2ecf20Sopenharmony_ci	for (; t; t = rhashtable_walk_next(iter)) {
51558c2ecf20Sopenharmony_ci		if (IS_ERR(t)) {
51568c2ecf20Sopenharmony_ci			if (PTR_ERR(t) == -EAGAIN)
51578c2ecf20Sopenharmony_ci				continue;
51588c2ecf20Sopenharmony_ci			break;
51598c2ecf20Sopenharmony_ci		}
51608c2ecf20Sopenharmony_ci
51618c2ecf20Sopenharmony_ci		if (!sctp_transport_hold(t))
51628c2ecf20Sopenharmony_ci			continue;
51638c2ecf20Sopenharmony_ci
51648c2ecf20Sopenharmony_ci		if (net_eq(t->asoc->base.net, net) &&
51658c2ecf20Sopenharmony_ci		    t->asoc->peer.primary_path == t)
51668c2ecf20Sopenharmony_ci			break;
51678c2ecf20Sopenharmony_ci
51688c2ecf20Sopenharmony_ci		sctp_transport_put(t);
51698c2ecf20Sopenharmony_ci	}
51708c2ecf20Sopenharmony_ci
51718c2ecf20Sopenharmony_ci	return t;
51728c2ecf20Sopenharmony_ci}
51738c2ecf20Sopenharmony_ci
51748c2ecf20Sopenharmony_cistruct sctp_transport *sctp_transport_get_idx(struct net *net,
51758c2ecf20Sopenharmony_ci					      struct rhashtable_iter *iter,
51768c2ecf20Sopenharmony_ci					      int pos)
51778c2ecf20Sopenharmony_ci{
51788c2ecf20Sopenharmony_ci	struct sctp_transport *t;
51798c2ecf20Sopenharmony_ci
51808c2ecf20Sopenharmony_ci	if (!pos)
51818c2ecf20Sopenharmony_ci		return SEQ_START_TOKEN;
51828c2ecf20Sopenharmony_ci
51838c2ecf20Sopenharmony_ci	while ((t = sctp_transport_get_next(net, iter)) && !IS_ERR(t)) {
51848c2ecf20Sopenharmony_ci		if (!--pos)
51858c2ecf20Sopenharmony_ci			break;
51868c2ecf20Sopenharmony_ci		sctp_transport_put(t);
51878c2ecf20Sopenharmony_ci	}
51888c2ecf20Sopenharmony_ci
51898c2ecf20Sopenharmony_ci	return t;
51908c2ecf20Sopenharmony_ci}
51918c2ecf20Sopenharmony_ci
51928c2ecf20Sopenharmony_ciint sctp_for_each_endpoint(int (*cb)(struct sctp_endpoint *, void *),
51938c2ecf20Sopenharmony_ci			   void *p) {
51948c2ecf20Sopenharmony_ci	int err = 0;
51958c2ecf20Sopenharmony_ci	int hash = 0;
51968c2ecf20Sopenharmony_ci	struct sctp_ep_common *epb;
51978c2ecf20Sopenharmony_ci	struct sctp_hashbucket *head;
51988c2ecf20Sopenharmony_ci
51998c2ecf20Sopenharmony_ci	for (head = sctp_ep_hashtable; hash < sctp_ep_hashsize;
52008c2ecf20Sopenharmony_ci	     hash++, head++) {
52018c2ecf20Sopenharmony_ci		read_lock_bh(&head->lock);
52028c2ecf20Sopenharmony_ci		sctp_for_each_hentry(epb, &head->chain) {
52038c2ecf20Sopenharmony_ci			err = cb(sctp_ep(epb), p);
52048c2ecf20Sopenharmony_ci			if (err)
52058c2ecf20Sopenharmony_ci				break;
52068c2ecf20Sopenharmony_ci		}
52078c2ecf20Sopenharmony_ci		read_unlock_bh(&head->lock);
52088c2ecf20Sopenharmony_ci	}
52098c2ecf20Sopenharmony_ci
52108c2ecf20Sopenharmony_ci	return err;
52118c2ecf20Sopenharmony_ci}
52128c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sctp_for_each_endpoint);
52138c2ecf20Sopenharmony_ci
52148c2ecf20Sopenharmony_ciint sctp_transport_lookup_process(int (*cb)(struct sctp_transport *, void *),
52158c2ecf20Sopenharmony_ci				  struct net *net,
52168c2ecf20Sopenharmony_ci				  const union sctp_addr *laddr,
52178c2ecf20Sopenharmony_ci				  const union sctp_addr *paddr, void *p)
52188c2ecf20Sopenharmony_ci{
52198c2ecf20Sopenharmony_ci	struct sctp_transport *transport;
52208c2ecf20Sopenharmony_ci	int err;
52218c2ecf20Sopenharmony_ci
52228c2ecf20Sopenharmony_ci	rcu_read_lock();
52238c2ecf20Sopenharmony_ci	transport = sctp_addrs_lookup_transport(net, laddr, paddr);
52248c2ecf20Sopenharmony_ci	rcu_read_unlock();
52258c2ecf20Sopenharmony_ci	if (!transport)
52268c2ecf20Sopenharmony_ci		return -ENOENT;
52278c2ecf20Sopenharmony_ci
52288c2ecf20Sopenharmony_ci	err = cb(transport, p);
52298c2ecf20Sopenharmony_ci	sctp_transport_put(transport);
52308c2ecf20Sopenharmony_ci
52318c2ecf20Sopenharmony_ci	return err;
52328c2ecf20Sopenharmony_ci}
52338c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sctp_transport_lookup_process);
52348c2ecf20Sopenharmony_ci
52358c2ecf20Sopenharmony_ciint sctp_transport_traverse_process(sctp_callback_t cb, sctp_callback_t cb_done,
52368c2ecf20Sopenharmony_ci				    struct net *net, int *pos, void *p)
52378c2ecf20Sopenharmony_ci{
52388c2ecf20Sopenharmony_ci	struct rhashtable_iter hti;
52398c2ecf20Sopenharmony_ci	struct sctp_transport *tsp;
52408c2ecf20Sopenharmony_ci	struct sctp_endpoint *ep;
52418c2ecf20Sopenharmony_ci	int ret;
52428c2ecf20Sopenharmony_ci
52438c2ecf20Sopenharmony_ciagain:
52448c2ecf20Sopenharmony_ci	ret = 0;
52458c2ecf20Sopenharmony_ci	sctp_transport_walk_start(&hti);
52468c2ecf20Sopenharmony_ci
52478c2ecf20Sopenharmony_ci	tsp = sctp_transport_get_idx(net, &hti, *pos + 1);
52488c2ecf20Sopenharmony_ci	for (; !IS_ERR_OR_NULL(tsp); tsp = sctp_transport_get_next(net, &hti)) {
52498c2ecf20Sopenharmony_ci		ep = tsp->asoc->ep;
52508c2ecf20Sopenharmony_ci		if (sctp_endpoint_hold(ep)) { /* asoc can be peeled off */
52518c2ecf20Sopenharmony_ci			ret = cb(ep, tsp, p);
52528c2ecf20Sopenharmony_ci			if (ret)
52538c2ecf20Sopenharmony_ci				break;
52548c2ecf20Sopenharmony_ci			sctp_endpoint_put(ep);
52558c2ecf20Sopenharmony_ci		}
52568c2ecf20Sopenharmony_ci		(*pos)++;
52578c2ecf20Sopenharmony_ci		sctp_transport_put(tsp);
52588c2ecf20Sopenharmony_ci	}
52598c2ecf20Sopenharmony_ci	sctp_transport_walk_stop(&hti);
52608c2ecf20Sopenharmony_ci
52618c2ecf20Sopenharmony_ci	if (ret) {
52628c2ecf20Sopenharmony_ci		if (cb_done && !cb_done(ep, tsp, p)) {
52638c2ecf20Sopenharmony_ci			(*pos)++;
52648c2ecf20Sopenharmony_ci			sctp_endpoint_put(ep);
52658c2ecf20Sopenharmony_ci			sctp_transport_put(tsp);
52668c2ecf20Sopenharmony_ci			goto again;
52678c2ecf20Sopenharmony_ci		}
52688c2ecf20Sopenharmony_ci		sctp_endpoint_put(ep);
52698c2ecf20Sopenharmony_ci		sctp_transport_put(tsp);
52708c2ecf20Sopenharmony_ci	}
52718c2ecf20Sopenharmony_ci
52728c2ecf20Sopenharmony_ci	return ret;
52738c2ecf20Sopenharmony_ci}
52748c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sctp_transport_traverse_process);
52758c2ecf20Sopenharmony_ci
52768c2ecf20Sopenharmony_ci/* 7.2.1 Association Status (SCTP_STATUS)
52778c2ecf20Sopenharmony_ci
52788c2ecf20Sopenharmony_ci * Applications can retrieve current status information about an
52798c2ecf20Sopenharmony_ci * association, including association state, peer receiver window size,
52808c2ecf20Sopenharmony_ci * number of unacked data chunks, and number of data chunks pending
52818c2ecf20Sopenharmony_ci * receipt.  This information is read-only.
52828c2ecf20Sopenharmony_ci */
52838c2ecf20Sopenharmony_cistatic int sctp_getsockopt_sctp_status(struct sock *sk, int len,
52848c2ecf20Sopenharmony_ci				       char __user *optval,
52858c2ecf20Sopenharmony_ci				       int __user *optlen)
52868c2ecf20Sopenharmony_ci{
52878c2ecf20Sopenharmony_ci	struct sctp_status status;
52888c2ecf20Sopenharmony_ci	struct sctp_association *asoc = NULL;
52898c2ecf20Sopenharmony_ci	struct sctp_transport *transport;
52908c2ecf20Sopenharmony_ci	sctp_assoc_t associd;
52918c2ecf20Sopenharmony_ci	int retval = 0;
52928c2ecf20Sopenharmony_ci
52938c2ecf20Sopenharmony_ci	if (len < sizeof(status)) {
52948c2ecf20Sopenharmony_ci		retval = -EINVAL;
52958c2ecf20Sopenharmony_ci		goto out;
52968c2ecf20Sopenharmony_ci	}
52978c2ecf20Sopenharmony_ci
52988c2ecf20Sopenharmony_ci	len = sizeof(status);
52998c2ecf20Sopenharmony_ci	if (copy_from_user(&status, optval, len)) {
53008c2ecf20Sopenharmony_ci		retval = -EFAULT;
53018c2ecf20Sopenharmony_ci		goto out;
53028c2ecf20Sopenharmony_ci	}
53038c2ecf20Sopenharmony_ci
53048c2ecf20Sopenharmony_ci	associd = status.sstat_assoc_id;
53058c2ecf20Sopenharmony_ci	asoc = sctp_id2assoc(sk, associd);
53068c2ecf20Sopenharmony_ci	if (!asoc) {
53078c2ecf20Sopenharmony_ci		retval = -EINVAL;
53088c2ecf20Sopenharmony_ci		goto out;
53098c2ecf20Sopenharmony_ci	}
53108c2ecf20Sopenharmony_ci
53118c2ecf20Sopenharmony_ci	transport = asoc->peer.primary_path;
53128c2ecf20Sopenharmony_ci
53138c2ecf20Sopenharmony_ci	status.sstat_assoc_id = sctp_assoc2id(asoc);
53148c2ecf20Sopenharmony_ci	status.sstat_state = sctp_assoc_to_state(asoc);
53158c2ecf20Sopenharmony_ci	status.sstat_rwnd =  asoc->peer.rwnd;
53168c2ecf20Sopenharmony_ci	status.sstat_unackdata = asoc->unack_data;
53178c2ecf20Sopenharmony_ci
53188c2ecf20Sopenharmony_ci	status.sstat_penddata = sctp_tsnmap_pending(&asoc->peer.tsn_map);
53198c2ecf20Sopenharmony_ci	status.sstat_instrms = asoc->stream.incnt;
53208c2ecf20Sopenharmony_ci	status.sstat_outstrms = asoc->stream.outcnt;
53218c2ecf20Sopenharmony_ci	status.sstat_fragmentation_point = asoc->frag_point;
53228c2ecf20Sopenharmony_ci	status.sstat_primary.spinfo_assoc_id = sctp_assoc2id(transport->asoc);
53238c2ecf20Sopenharmony_ci	memcpy(&status.sstat_primary.spinfo_address, &transport->ipaddr,
53248c2ecf20Sopenharmony_ci			transport->af_specific->sockaddr_len);
53258c2ecf20Sopenharmony_ci	/* Map ipv4 address into v4-mapped-on-v6 address.  */
53268c2ecf20Sopenharmony_ci	sctp_get_pf_specific(sk->sk_family)->addr_to_user(sctp_sk(sk),
53278c2ecf20Sopenharmony_ci		(union sctp_addr *)&status.sstat_primary.spinfo_address);
53288c2ecf20Sopenharmony_ci	status.sstat_primary.spinfo_state = transport->state;
53298c2ecf20Sopenharmony_ci	status.sstat_primary.spinfo_cwnd = transport->cwnd;
53308c2ecf20Sopenharmony_ci	status.sstat_primary.spinfo_srtt = transport->srtt;
53318c2ecf20Sopenharmony_ci	status.sstat_primary.spinfo_rto = jiffies_to_msecs(transport->rto);
53328c2ecf20Sopenharmony_ci	status.sstat_primary.spinfo_mtu = transport->pathmtu;
53338c2ecf20Sopenharmony_ci
53348c2ecf20Sopenharmony_ci	if (status.sstat_primary.spinfo_state == SCTP_UNKNOWN)
53358c2ecf20Sopenharmony_ci		status.sstat_primary.spinfo_state = SCTP_ACTIVE;
53368c2ecf20Sopenharmony_ci
53378c2ecf20Sopenharmony_ci	if (put_user(len, optlen)) {
53388c2ecf20Sopenharmony_ci		retval = -EFAULT;
53398c2ecf20Sopenharmony_ci		goto out;
53408c2ecf20Sopenharmony_ci	}
53418c2ecf20Sopenharmony_ci
53428c2ecf20Sopenharmony_ci	pr_debug("%s: len:%d, state:%d, rwnd:%d, assoc_id:%d\n",
53438c2ecf20Sopenharmony_ci		 __func__, len, status.sstat_state, status.sstat_rwnd,
53448c2ecf20Sopenharmony_ci		 status.sstat_assoc_id);
53458c2ecf20Sopenharmony_ci
53468c2ecf20Sopenharmony_ci	if (copy_to_user(optval, &status, len)) {
53478c2ecf20Sopenharmony_ci		retval = -EFAULT;
53488c2ecf20Sopenharmony_ci		goto out;
53498c2ecf20Sopenharmony_ci	}
53508c2ecf20Sopenharmony_ci
53518c2ecf20Sopenharmony_ciout:
53528c2ecf20Sopenharmony_ci	return retval;
53538c2ecf20Sopenharmony_ci}
53548c2ecf20Sopenharmony_ci
53558c2ecf20Sopenharmony_ci
53568c2ecf20Sopenharmony_ci/* 7.2.2 Peer Address Information (SCTP_GET_PEER_ADDR_INFO)
53578c2ecf20Sopenharmony_ci *
53588c2ecf20Sopenharmony_ci * Applications can retrieve information about a specific peer address
53598c2ecf20Sopenharmony_ci * of an association, including its reachability state, congestion
53608c2ecf20Sopenharmony_ci * window, and retransmission timer values.  This information is
53618c2ecf20Sopenharmony_ci * read-only.
53628c2ecf20Sopenharmony_ci */
53638c2ecf20Sopenharmony_cistatic int sctp_getsockopt_peer_addr_info(struct sock *sk, int len,
53648c2ecf20Sopenharmony_ci					  char __user *optval,
53658c2ecf20Sopenharmony_ci					  int __user *optlen)
53668c2ecf20Sopenharmony_ci{
53678c2ecf20Sopenharmony_ci	struct sctp_paddrinfo pinfo;
53688c2ecf20Sopenharmony_ci	struct sctp_transport *transport;
53698c2ecf20Sopenharmony_ci	int retval = 0;
53708c2ecf20Sopenharmony_ci
53718c2ecf20Sopenharmony_ci	if (len < sizeof(pinfo)) {
53728c2ecf20Sopenharmony_ci		retval = -EINVAL;
53738c2ecf20Sopenharmony_ci		goto out;
53748c2ecf20Sopenharmony_ci	}
53758c2ecf20Sopenharmony_ci
53768c2ecf20Sopenharmony_ci	len = sizeof(pinfo);
53778c2ecf20Sopenharmony_ci	if (copy_from_user(&pinfo, optval, len)) {
53788c2ecf20Sopenharmony_ci		retval = -EFAULT;
53798c2ecf20Sopenharmony_ci		goto out;
53808c2ecf20Sopenharmony_ci	}
53818c2ecf20Sopenharmony_ci
53828c2ecf20Sopenharmony_ci	transport = sctp_addr_id2transport(sk, &pinfo.spinfo_address,
53838c2ecf20Sopenharmony_ci					   pinfo.spinfo_assoc_id);
53848c2ecf20Sopenharmony_ci	if (!transport) {
53858c2ecf20Sopenharmony_ci		retval = -EINVAL;
53868c2ecf20Sopenharmony_ci		goto out;
53878c2ecf20Sopenharmony_ci	}
53888c2ecf20Sopenharmony_ci
53898c2ecf20Sopenharmony_ci	if (transport->state == SCTP_PF &&
53908c2ecf20Sopenharmony_ci	    transport->asoc->pf_expose == SCTP_PF_EXPOSE_DISABLE) {
53918c2ecf20Sopenharmony_ci		retval = -EACCES;
53928c2ecf20Sopenharmony_ci		goto out;
53938c2ecf20Sopenharmony_ci	}
53948c2ecf20Sopenharmony_ci
53958c2ecf20Sopenharmony_ci	pinfo.spinfo_assoc_id = sctp_assoc2id(transport->asoc);
53968c2ecf20Sopenharmony_ci	pinfo.spinfo_state = transport->state;
53978c2ecf20Sopenharmony_ci	pinfo.spinfo_cwnd = transport->cwnd;
53988c2ecf20Sopenharmony_ci	pinfo.spinfo_srtt = transport->srtt;
53998c2ecf20Sopenharmony_ci	pinfo.spinfo_rto = jiffies_to_msecs(transport->rto);
54008c2ecf20Sopenharmony_ci	pinfo.spinfo_mtu = transport->pathmtu;
54018c2ecf20Sopenharmony_ci
54028c2ecf20Sopenharmony_ci	if (pinfo.spinfo_state == SCTP_UNKNOWN)
54038c2ecf20Sopenharmony_ci		pinfo.spinfo_state = SCTP_ACTIVE;
54048c2ecf20Sopenharmony_ci
54058c2ecf20Sopenharmony_ci	if (put_user(len, optlen)) {
54068c2ecf20Sopenharmony_ci		retval = -EFAULT;
54078c2ecf20Sopenharmony_ci		goto out;
54088c2ecf20Sopenharmony_ci	}
54098c2ecf20Sopenharmony_ci
54108c2ecf20Sopenharmony_ci	if (copy_to_user(optval, &pinfo, len)) {
54118c2ecf20Sopenharmony_ci		retval = -EFAULT;
54128c2ecf20Sopenharmony_ci		goto out;
54138c2ecf20Sopenharmony_ci	}
54148c2ecf20Sopenharmony_ci
54158c2ecf20Sopenharmony_ciout:
54168c2ecf20Sopenharmony_ci	return retval;
54178c2ecf20Sopenharmony_ci}
54188c2ecf20Sopenharmony_ci
54198c2ecf20Sopenharmony_ci/* 7.1.12 Enable/Disable message fragmentation (SCTP_DISABLE_FRAGMENTS)
54208c2ecf20Sopenharmony_ci *
54218c2ecf20Sopenharmony_ci * This option is a on/off flag.  If enabled no SCTP message
54228c2ecf20Sopenharmony_ci * fragmentation will be performed.  Instead if a message being sent
54238c2ecf20Sopenharmony_ci * exceeds the current PMTU size, the message will NOT be sent and
54248c2ecf20Sopenharmony_ci * instead a error will be indicated to the user.
54258c2ecf20Sopenharmony_ci */
54268c2ecf20Sopenharmony_cistatic int sctp_getsockopt_disable_fragments(struct sock *sk, int len,
54278c2ecf20Sopenharmony_ci					char __user *optval, int __user *optlen)
54288c2ecf20Sopenharmony_ci{
54298c2ecf20Sopenharmony_ci	int val;
54308c2ecf20Sopenharmony_ci
54318c2ecf20Sopenharmony_ci	if (len < sizeof(int))
54328c2ecf20Sopenharmony_ci		return -EINVAL;
54338c2ecf20Sopenharmony_ci
54348c2ecf20Sopenharmony_ci	len = sizeof(int);
54358c2ecf20Sopenharmony_ci	val = (sctp_sk(sk)->disable_fragments == 1);
54368c2ecf20Sopenharmony_ci	if (put_user(len, optlen))
54378c2ecf20Sopenharmony_ci		return -EFAULT;
54388c2ecf20Sopenharmony_ci	if (copy_to_user(optval, &val, len))
54398c2ecf20Sopenharmony_ci		return -EFAULT;
54408c2ecf20Sopenharmony_ci	return 0;
54418c2ecf20Sopenharmony_ci}
54428c2ecf20Sopenharmony_ci
54438c2ecf20Sopenharmony_ci/* 7.1.15 Set notification and ancillary events (SCTP_EVENTS)
54448c2ecf20Sopenharmony_ci *
54458c2ecf20Sopenharmony_ci * This socket option is used to specify various notifications and
54468c2ecf20Sopenharmony_ci * ancillary data the user wishes to receive.
54478c2ecf20Sopenharmony_ci */
54488c2ecf20Sopenharmony_cistatic int sctp_getsockopt_events(struct sock *sk, int len, char __user *optval,
54498c2ecf20Sopenharmony_ci				  int __user *optlen)
54508c2ecf20Sopenharmony_ci{
54518c2ecf20Sopenharmony_ci	struct sctp_event_subscribe subscribe;
54528c2ecf20Sopenharmony_ci	__u8 *sn_type = (__u8 *)&subscribe;
54538c2ecf20Sopenharmony_ci	int i;
54548c2ecf20Sopenharmony_ci
54558c2ecf20Sopenharmony_ci	if (len == 0)
54568c2ecf20Sopenharmony_ci		return -EINVAL;
54578c2ecf20Sopenharmony_ci	if (len > sizeof(struct sctp_event_subscribe))
54588c2ecf20Sopenharmony_ci		len = sizeof(struct sctp_event_subscribe);
54598c2ecf20Sopenharmony_ci	if (put_user(len, optlen))
54608c2ecf20Sopenharmony_ci		return -EFAULT;
54618c2ecf20Sopenharmony_ci
54628c2ecf20Sopenharmony_ci	for (i = 0; i < len; i++)
54638c2ecf20Sopenharmony_ci		sn_type[i] = sctp_ulpevent_type_enabled(sctp_sk(sk)->subscribe,
54648c2ecf20Sopenharmony_ci							SCTP_SN_TYPE_BASE + i);
54658c2ecf20Sopenharmony_ci
54668c2ecf20Sopenharmony_ci	if (copy_to_user(optval, &subscribe, len))
54678c2ecf20Sopenharmony_ci		return -EFAULT;
54688c2ecf20Sopenharmony_ci
54698c2ecf20Sopenharmony_ci	return 0;
54708c2ecf20Sopenharmony_ci}
54718c2ecf20Sopenharmony_ci
54728c2ecf20Sopenharmony_ci/* 7.1.8 Automatic Close of associations (SCTP_AUTOCLOSE)
54738c2ecf20Sopenharmony_ci *
54748c2ecf20Sopenharmony_ci * This socket option is applicable to the UDP-style socket only.  When
54758c2ecf20Sopenharmony_ci * set it will cause associations that are idle for more than the
54768c2ecf20Sopenharmony_ci * specified number of seconds to automatically close.  An association
54778c2ecf20Sopenharmony_ci * being idle is defined an association that has NOT sent or received
54788c2ecf20Sopenharmony_ci * user data.  The special value of '0' indicates that no automatic
54798c2ecf20Sopenharmony_ci * close of any associations should be performed.  The option expects an
54808c2ecf20Sopenharmony_ci * integer defining the number of seconds of idle time before an
54818c2ecf20Sopenharmony_ci * association is closed.
54828c2ecf20Sopenharmony_ci */
54838c2ecf20Sopenharmony_cistatic int sctp_getsockopt_autoclose(struct sock *sk, int len, char __user *optval, int __user *optlen)
54848c2ecf20Sopenharmony_ci{
54858c2ecf20Sopenharmony_ci	/* Applicable to UDP-style socket only */
54868c2ecf20Sopenharmony_ci	if (sctp_style(sk, TCP))
54878c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
54888c2ecf20Sopenharmony_ci	if (len < sizeof(int))
54898c2ecf20Sopenharmony_ci		return -EINVAL;
54908c2ecf20Sopenharmony_ci	len = sizeof(int);
54918c2ecf20Sopenharmony_ci	if (put_user(len, optlen))
54928c2ecf20Sopenharmony_ci		return -EFAULT;
54938c2ecf20Sopenharmony_ci	if (put_user(sctp_sk(sk)->autoclose, (int __user *)optval))
54948c2ecf20Sopenharmony_ci		return -EFAULT;
54958c2ecf20Sopenharmony_ci	return 0;
54968c2ecf20Sopenharmony_ci}
54978c2ecf20Sopenharmony_ci
54988c2ecf20Sopenharmony_ci/* Helper routine to branch off an association to a new socket.  */
54998c2ecf20Sopenharmony_ciint sctp_do_peeloff(struct sock *sk, sctp_assoc_t id, struct socket **sockp)
55008c2ecf20Sopenharmony_ci{
55018c2ecf20Sopenharmony_ci	struct sctp_association *asoc = sctp_id2assoc(sk, id);
55028c2ecf20Sopenharmony_ci	struct sctp_sock *sp = sctp_sk(sk);
55038c2ecf20Sopenharmony_ci	struct socket *sock;
55048c2ecf20Sopenharmony_ci	int err = 0;
55058c2ecf20Sopenharmony_ci
55068c2ecf20Sopenharmony_ci	/* Do not peel off from one netns to another one. */
55078c2ecf20Sopenharmony_ci	if (!net_eq(current->nsproxy->net_ns, sock_net(sk)))
55088c2ecf20Sopenharmony_ci		return -EINVAL;
55098c2ecf20Sopenharmony_ci
55108c2ecf20Sopenharmony_ci	if (!asoc)
55118c2ecf20Sopenharmony_ci		return -EINVAL;
55128c2ecf20Sopenharmony_ci
55138c2ecf20Sopenharmony_ci	/* An association cannot be branched off from an already peeled-off
55148c2ecf20Sopenharmony_ci	 * socket, nor is this supported for tcp style sockets.
55158c2ecf20Sopenharmony_ci	 */
55168c2ecf20Sopenharmony_ci	if (!sctp_style(sk, UDP))
55178c2ecf20Sopenharmony_ci		return -EINVAL;
55188c2ecf20Sopenharmony_ci
55198c2ecf20Sopenharmony_ci	/* Create a new socket.  */
55208c2ecf20Sopenharmony_ci	err = sock_create(sk->sk_family, SOCK_SEQPACKET, IPPROTO_SCTP, &sock);
55218c2ecf20Sopenharmony_ci	if (err < 0)
55228c2ecf20Sopenharmony_ci		return err;
55238c2ecf20Sopenharmony_ci
55248c2ecf20Sopenharmony_ci	sctp_copy_sock(sock->sk, sk, asoc);
55258c2ecf20Sopenharmony_ci
55268c2ecf20Sopenharmony_ci	/* Make peeled-off sockets more like 1-1 accepted sockets.
55278c2ecf20Sopenharmony_ci	 * Set the daddr and initialize id to something more random and also
55288c2ecf20Sopenharmony_ci	 * copy over any ip options.
55298c2ecf20Sopenharmony_ci	 */
55308c2ecf20Sopenharmony_ci	sp->pf->to_sk_daddr(&asoc->peer.primary_addr, sock->sk);
55318c2ecf20Sopenharmony_ci	sp->pf->copy_ip_options(sk, sock->sk);
55328c2ecf20Sopenharmony_ci
55338c2ecf20Sopenharmony_ci	/* Populate the fields of the newsk from the oldsk and migrate the
55348c2ecf20Sopenharmony_ci	 * asoc to the newsk.
55358c2ecf20Sopenharmony_ci	 */
55368c2ecf20Sopenharmony_ci	err = sctp_sock_migrate(sk, sock->sk, asoc,
55378c2ecf20Sopenharmony_ci				SCTP_SOCKET_UDP_HIGH_BANDWIDTH);
55388c2ecf20Sopenharmony_ci	if (err) {
55398c2ecf20Sopenharmony_ci		sock_release(sock);
55408c2ecf20Sopenharmony_ci		sock = NULL;
55418c2ecf20Sopenharmony_ci	}
55428c2ecf20Sopenharmony_ci
55438c2ecf20Sopenharmony_ci	*sockp = sock;
55448c2ecf20Sopenharmony_ci
55458c2ecf20Sopenharmony_ci	return err;
55468c2ecf20Sopenharmony_ci}
55478c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sctp_do_peeloff);
55488c2ecf20Sopenharmony_ci
55498c2ecf20Sopenharmony_cistatic int sctp_getsockopt_peeloff_common(struct sock *sk, sctp_peeloff_arg_t *peeloff,
55508c2ecf20Sopenharmony_ci					  struct file **newfile, unsigned flags)
55518c2ecf20Sopenharmony_ci{
55528c2ecf20Sopenharmony_ci	struct socket *newsock;
55538c2ecf20Sopenharmony_ci	int retval;
55548c2ecf20Sopenharmony_ci
55558c2ecf20Sopenharmony_ci	retval = sctp_do_peeloff(sk, peeloff->associd, &newsock);
55568c2ecf20Sopenharmony_ci	if (retval < 0)
55578c2ecf20Sopenharmony_ci		goto out;
55588c2ecf20Sopenharmony_ci
55598c2ecf20Sopenharmony_ci	/* Map the socket to an unused fd that can be returned to the user.  */
55608c2ecf20Sopenharmony_ci	retval = get_unused_fd_flags(flags & SOCK_CLOEXEC);
55618c2ecf20Sopenharmony_ci	if (retval < 0) {
55628c2ecf20Sopenharmony_ci		sock_release(newsock);
55638c2ecf20Sopenharmony_ci		goto out;
55648c2ecf20Sopenharmony_ci	}
55658c2ecf20Sopenharmony_ci
55668c2ecf20Sopenharmony_ci	*newfile = sock_alloc_file(newsock, 0, NULL);
55678c2ecf20Sopenharmony_ci	if (IS_ERR(*newfile)) {
55688c2ecf20Sopenharmony_ci		put_unused_fd(retval);
55698c2ecf20Sopenharmony_ci		retval = PTR_ERR(*newfile);
55708c2ecf20Sopenharmony_ci		*newfile = NULL;
55718c2ecf20Sopenharmony_ci		return retval;
55728c2ecf20Sopenharmony_ci	}
55738c2ecf20Sopenharmony_ci
55748c2ecf20Sopenharmony_ci	pr_debug("%s: sk:%p, newsk:%p, sd:%d\n", __func__, sk, newsock->sk,
55758c2ecf20Sopenharmony_ci		 retval);
55768c2ecf20Sopenharmony_ci
55778c2ecf20Sopenharmony_ci	peeloff->sd = retval;
55788c2ecf20Sopenharmony_ci
55798c2ecf20Sopenharmony_ci	if (flags & SOCK_NONBLOCK)
55808c2ecf20Sopenharmony_ci		(*newfile)->f_flags |= O_NONBLOCK;
55818c2ecf20Sopenharmony_ciout:
55828c2ecf20Sopenharmony_ci	return retval;
55838c2ecf20Sopenharmony_ci}
55848c2ecf20Sopenharmony_ci
55858c2ecf20Sopenharmony_cistatic int sctp_getsockopt_peeloff(struct sock *sk, int len, char __user *optval, int __user *optlen)
55868c2ecf20Sopenharmony_ci{
55878c2ecf20Sopenharmony_ci	sctp_peeloff_arg_t peeloff;
55888c2ecf20Sopenharmony_ci	struct file *newfile = NULL;
55898c2ecf20Sopenharmony_ci	int retval = 0;
55908c2ecf20Sopenharmony_ci
55918c2ecf20Sopenharmony_ci	if (len < sizeof(sctp_peeloff_arg_t))
55928c2ecf20Sopenharmony_ci		return -EINVAL;
55938c2ecf20Sopenharmony_ci	len = sizeof(sctp_peeloff_arg_t);
55948c2ecf20Sopenharmony_ci	if (copy_from_user(&peeloff, optval, len))
55958c2ecf20Sopenharmony_ci		return -EFAULT;
55968c2ecf20Sopenharmony_ci
55978c2ecf20Sopenharmony_ci	retval = sctp_getsockopt_peeloff_common(sk, &peeloff, &newfile, 0);
55988c2ecf20Sopenharmony_ci	if (retval < 0)
55998c2ecf20Sopenharmony_ci		goto out;
56008c2ecf20Sopenharmony_ci
56018c2ecf20Sopenharmony_ci	/* Return the fd mapped to the new socket.  */
56028c2ecf20Sopenharmony_ci	if (put_user(len, optlen)) {
56038c2ecf20Sopenharmony_ci		fput(newfile);
56048c2ecf20Sopenharmony_ci		put_unused_fd(retval);
56058c2ecf20Sopenharmony_ci		return -EFAULT;
56068c2ecf20Sopenharmony_ci	}
56078c2ecf20Sopenharmony_ci
56088c2ecf20Sopenharmony_ci	if (copy_to_user(optval, &peeloff, len)) {
56098c2ecf20Sopenharmony_ci		fput(newfile);
56108c2ecf20Sopenharmony_ci		put_unused_fd(retval);
56118c2ecf20Sopenharmony_ci		return -EFAULT;
56128c2ecf20Sopenharmony_ci	}
56138c2ecf20Sopenharmony_ci	fd_install(retval, newfile);
56148c2ecf20Sopenharmony_ciout:
56158c2ecf20Sopenharmony_ci	return retval;
56168c2ecf20Sopenharmony_ci}
56178c2ecf20Sopenharmony_ci
56188c2ecf20Sopenharmony_cistatic int sctp_getsockopt_peeloff_flags(struct sock *sk, int len,
56198c2ecf20Sopenharmony_ci					 char __user *optval, int __user *optlen)
56208c2ecf20Sopenharmony_ci{
56218c2ecf20Sopenharmony_ci	sctp_peeloff_flags_arg_t peeloff;
56228c2ecf20Sopenharmony_ci	struct file *newfile = NULL;
56238c2ecf20Sopenharmony_ci	int retval = 0;
56248c2ecf20Sopenharmony_ci
56258c2ecf20Sopenharmony_ci	if (len < sizeof(sctp_peeloff_flags_arg_t))
56268c2ecf20Sopenharmony_ci		return -EINVAL;
56278c2ecf20Sopenharmony_ci	len = sizeof(sctp_peeloff_flags_arg_t);
56288c2ecf20Sopenharmony_ci	if (copy_from_user(&peeloff, optval, len))
56298c2ecf20Sopenharmony_ci		return -EFAULT;
56308c2ecf20Sopenharmony_ci
56318c2ecf20Sopenharmony_ci	retval = sctp_getsockopt_peeloff_common(sk, &peeloff.p_arg,
56328c2ecf20Sopenharmony_ci						&newfile, peeloff.flags);
56338c2ecf20Sopenharmony_ci	if (retval < 0)
56348c2ecf20Sopenharmony_ci		goto out;
56358c2ecf20Sopenharmony_ci
56368c2ecf20Sopenharmony_ci	/* Return the fd mapped to the new socket.  */
56378c2ecf20Sopenharmony_ci	if (put_user(len, optlen)) {
56388c2ecf20Sopenharmony_ci		fput(newfile);
56398c2ecf20Sopenharmony_ci		put_unused_fd(retval);
56408c2ecf20Sopenharmony_ci		return -EFAULT;
56418c2ecf20Sopenharmony_ci	}
56428c2ecf20Sopenharmony_ci
56438c2ecf20Sopenharmony_ci	if (copy_to_user(optval, &peeloff, len)) {
56448c2ecf20Sopenharmony_ci		fput(newfile);
56458c2ecf20Sopenharmony_ci		put_unused_fd(retval);
56468c2ecf20Sopenharmony_ci		return -EFAULT;
56478c2ecf20Sopenharmony_ci	}
56488c2ecf20Sopenharmony_ci	fd_install(retval, newfile);
56498c2ecf20Sopenharmony_ciout:
56508c2ecf20Sopenharmony_ci	return retval;
56518c2ecf20Sopenharmony_ci}
56528c2ecf20Sopenharmony_ci
56538c2ecf20Sopenharmony_ci/* 7.1.13 Peer Address Parameters (SCTP_PEER_ADDR_PARAMS)
56548c2ecf20Sopenharmony_ci *
56558c2ecf20Sopenharmony_ci * Applications can enable or disable heartbeats for any peer address of
56568c2ecf20Sopenharmony_ci * an association, modify an address's heartbeat interval, force a
56578c2ecf20Sopenharmony_ci * heartbeat to be sent immediately, and adjust the address's maximum
56588c2ecf20Sopenharmony_ci * number of retransmissions sent before an address is considered
56598c2ecf20Sopenharmony_ci * unreachable.  The following structure is used to access and modify an
56608c2ecf20Sopenharmony_ci * address's parameters:
56618c2ecf20Sopenharmony_ci *
56628c2ecf20Sopenharmony_ci *  struct sctp_paddrparams {
56638c2ecf20Sopenharmony_ci *     sctp_assoc_t            spp_assoc_id;
56648c2ecf20Sopenharmony_ci *     struct sockaddr_storage spp_address;
56658c2ecf20Sopenharmony_ci *     uint32_t                spp_hbinterval;
56668c2ecf20Sopenharmony_ci *     uint16_t                spp_pathmaxrxt;
56678c2ecf20Sopenharmony_ci *     uint32_t                spp_pathmtu;
56688c2ecf20Sopenharmony_ci *     uint32_t                spp_sackdelay;
56698c2ecf20Sopenharmony_ci *     uint32_t                spp_flags;
56708c2ecf20Sopenharmony_ci * };
56718c2ecf20Sopenharmony_ci *
56728c2ecf20Sopenharmony_ci *   spp_assoc_id    - (one-to-many style socket) This is filled in the
56738c2ecf20Sopenharmony_ci *                     application, and identifies the association for
56748c2ecf20Sopenharmony_ci *                     this query.
56758c2ecf20Sopenharmony_ci *   spp_address     - This specifies which address is of interest.
56768c2ecf20Sopenharmony_ci *   spp_hbinterval  - This contains the value of the heartbeat interval,
56778c2ecf20Sopenharmony_ci *                     in milliseconds.  If a  value of zero
56788c2ecf20Sopenharmony_ci *                     is present in this field then no changes are to
56798c2ecf20Sopenharmony_ci *                     be made to this parameter.
56808c2ecf20Sopenharmony_ci *   spp_pathmaxrxt  - This contains the maximum number of
56818c2ecf20Sopenharmony_ci *                     retransmissions before this address shall be
56828c2ecf20Sopenharmony_ci *                     considered unreachable. If a  value of zero
56838c2ecf20Sopenharmony_ci *                     is present in this field then no changes are to
56848c2ecf20Sopenharmony_ci *                     be made to this parameter.
56858c2ecf20Sopenharmony_ci *   spp_pathmtu     - When Path MTU discovery is disabled the value
56868c2ecf20Sopenharmony_ci *                     specified here will be the "fixed" path mtu.
56878c2ecf20Sopenharmony_ci *                     Note that if the spp_address field is empty
56888c2ecf20Sopenharmony_ci *                     then all associations on this address will
56898c2ecf20Sopenharmony_ci *                     have this fixed path mtu set upon them.
56908c2ecf20Sopenharmony_ci *
56918c2ecf20Sopenharmony_ci *   spp_sackdelay   - When delayed sack is enabled, this value specifies
56928c2ecf20Sopenharmony_ci *                     the number of milliseconds that sacks will be delayed
56938c2ecf20Sopenharmony_ci *                     for. This value will apply to all addresses of an
56948c2ecf20Sopenharmony_ci *                     association if the spp_address field is empty. Note
56958c2ecf20Sopenharmony_ci *                     also, that if delayed sack is enabled and this
56968c2ecf20Sopenharmony_ci *                     value is set to 0, no change is made to the last
56978c2ecf20Sopenharmony_ci *                     recorded delayed sack timer value.
56988c2ecf20Sopenharmony_ci *
56998c2ecf20Sopenharmony_ci *   spp_flags       - These flags are used to control various features
57008c2ecf20Sopenharmony_ci *                     on an association. The flag field may contain
57018c2ecf20Sopenharmony_ci *                     zero or more of the following options.
57028c2ecf20Sopenharmony_ci *
57038c2ecf20Sopenharmony_ci *                     SPP_HB_ENABLE  - Enable heartbeats on the
57048c2ecf20Sopenharmony_ci *                     specified address. Note that if the address
57058c2ecf20Sopenharmony_ci *                     field is empty all addresses for the association
57068c2ecf20Sopenharmony_ci *                     have heartbeats enabled upon them.
57078c2ecf20Sopenharmony_ci *
57088c2ecf20Sopenharmony_ci *                     SPP_HB_DISABLE - Disable heartbeats on the
57098c2ecf20Sopenharmony_ci *                     speicifed address. Note that if the address
57108c2ecf20Sopenharmony_ci *                     field is empty all addresses for the association
57118c2ecf20Sopenharmony_ci *                     will have their heartbeats disabled. Note also
57128c2ecf20Sopenharmony_ci *                     that SPP_HB_ENABLE and SPP_HB_DISABLE are
57138c2ecf20Sopenharmony_ci *                     mutually exclusive, only one of these two should
57148c2ecf20Sopenharmony_ci *                     be specified. Enabling both fields will have
57158c2ecf20Sopenharmony_ci *                     undetermined results.
57168c2ecf20Sopenharmony_ci *
57178c2ecf20Sopenharmony_ci *                     SPP_HB_DEMAND - Request a user initiated heartbeat
57188c2ecf20Sopenharmony_ci *                     to be made immediately.
57198c2ecf20Sopenharmony_ci *
57208c2ecf20Sopenharmony_ci *                     SPP_PMTUD_ENABLE - This field will enable PMTU
57218c2ecf20Sopenharmony_ci *                     discovery upon the specified address. Note that
57228c2ecf20Sopenharmony_ci *                     if the address feild is empty then all addresses
57238c2ecf20Sopenharmony_ci *                     on the association are effected.
57248c2ecf20Sopenharmony_ci *
57258c2ecf20Sopenharmony_ci *                     SPP_PMTUD_DISABLE - This field will disable PMTU
57268c2ecf20Sopenharmony_ci *                     discovery upon the specified address. Note that
57278c2ecf20Sopenharmony_ci *                     if the address feild is empty then all addresses
57288c2ecf20Sopenharmony_ci *                     on the association are effected. Not also that
57298c2ecf20Sopenharmony_ci *                     SPP_PMTUD_ENABLE and SPP_PMTUD_DISABLE are mutually
57308c2ecf20Sopenharmony_ci *                     exclusive. Enabling both will have undetermined
57318c2ecf20Sopenharmony_ci *                     results.
57328c2ecf20Sopenharmony_ci *
57338c2ecf20Sopenharmony_ci *                     SPP_SACKDELAY_ENABLE - Setting this flag turns
57348c2ecf20Sopenharmony_ci *                     on delayed sack. The time specified in spp_sackdelay
57358c2ecf20Sopenharmony_ci *                     is used to specify the sack delay for this address. Note
57368c2ecf20Sopenharmony_ci *                     that if spp_address is empty then all addresses will
57378c2ecf20Sopenharmony_ci *                     enable delayed sack and take on the sack delay
57388c2ecf20Sopenharmony_ci *                     value specified in spp_sackdelay.
57398c2ecf20Sopenharmony_ci *                     SPP_SACKDELAY_DISABLE - Setting this flag turns
57408c2ecf20Sopenharmony_ci *                     off delayed sack. If the spp_address field is blank then
57418c2ecf20Sopenharmony_ci *                     delayed sack is disabled for the entire association. Note
57428c2ecf20Sopenharmony_ci *                     also that this field is mutually exclusive to
57438c2ecf20Sopenharmony_ci *                     SPP_SACKDELAY_ENABLE, setting both will have undefined
57448c2ecf20Sopenharmony_ci *                     results.
57458c2ecf20Sopenharmony_ci *
57468c2ecf20Sopenharmony_ci *                     SPP_IPV6_FLOWLABEL:  Setting this flag enables the
57478c2ecf20Sopenharmony_ci *                     setting of the IPV6 flow label value.  The value is
57488c2ecf20Sopenharmony_ci *                     contained in the spp_ipv6_flowlabel field.
57498c2ecf20Sopenharmony_ci *                     Upon retrieval, this flag will be set to indicate that
57508c2ecf20Sopenharmony_ci *                     the spp_ipv6_flowlabel field has a valid value returned.
57518c2ecf20Sopenharmony_ci *                     If a specific destination address is set (in the
57528c2ecf20Sopenharmony_ci *                     spp_address field), then the value returned is that of
57538c2ecf20Sopenharmony_ci *                     the address.  If just an association is specified (and
57548c2ecf20Sopenharmony_ci *                     no address), then the association's default flow label
57558c2ecf20Sopenharmony_ci *                     is returned.  If neither an association nor a destination
57568c2ecf20Sopenharmony_ci *                     is specified, then the socket's default flow label is
57578c2ecf20Sopenharmony_ci *                     returned.  For non-IPv6 sockets, this flag will be left
57588c2ecf20Sopenharmony_ci *                     cleared.
57598c2ecf20Sopenharmony_ci *
57608c2ecf20Sopenharmony_ci *                     SPP_DSCP:  Setting this flag enables the setting of the
57618c2ecf20Sopenharmony_ci *                     Differentiated Services Code Point (DSCP) value
57628c2ecf20Sopenharmony_ci *                     associated with either the association or a specific
57638c2ecf20Sopenharmony_ci *                     address.  The value is obtained in the spp_dscp field.
57648c2ecf20Sopenharmony_ci *                     Upon retrieval, this flag will be set to indicate that
57658c2ecf20Sopenharmony_ci *                     the spp_dscp field has a valid value returned.  If a
57668c2ecf20Sopenharmony_ci *                     specific destination address is set when called (in the
57678c2ecf20Sopenharmony_ci *                     spp_address field), then that specific destination
57688c2ecf20Sopenharmony_ci *                     address's DSCP value is returned.  If just an association
57698c2ecf20Sopenharmony_ci *                     is specified, then the association's default DSCP is
57708c2ecf20Sopenharmony_ci *                     returned.  If neither an association nor a destination is
57718c2ecf20Sopenharmony_ci *                     specified, then the socket's default DSCP is returned.
57728c2ecf20Sopenharmony_ci *
57738c2ecf20Sopenharmony_ci *   spp_ipv6_flowlabel
57748c2ecf20Sopenharmony_ci *                   - This field is used in conjunction with the
57758c2ecf20Sopenharmony_ci *                     SPP_IPV6_FLOWLABEL flag and contains the IPv6 flow label.
57768c2ecf20Sopenharmony_ci *                     The 20 least significant bits are used for the flow
57778c2ecf20Sopenharmony_ci *                     label.  This setting has precedence over any IPv6-layer
57788c2ecf20Sopenharmony_ci *                     setting.
57798c2ecf20Sopenharmony_ci *
57808c2ecf20Sopenharmony_ci *   spp_dscp        - This field is used in conjunction with the SPP_DSCP flag
57818c2ecf20Sopenharmony_ci *                     and contains the DSCP.  The 6 most significant bits are
57828c2ecf20Sopenharmony_ci *                     used for the DSCP.  This setting has precedence over any
57838c2ecf20Sopenharmony_ci *                     IPv4- or IPv6- layer setting.
57848c2ecf20Sopenharmony_ci */
57858c2ecf20Sopenharmony_cistatic int sctp_getsockopt_peer_addr_params(struct sock *sk, int len,
57868c2ecf20Sopenharmony_ci					    char __user *optval, int __user *optlen)
57878c2ecf20Sopenharmony_ci{
57888c2ecf20Sopenharmony_ci	struct sctp_paddrparams  params;
57898c2ecf20Sopenharmony_ci	struct sctp_transport   *trans = NULL;
57908c2ecf20Sopenharmony_ci	struct sctp_association *asoc = NULL;
57918c2ecf20Sopenharmony_ci	struct sctp_sock        *sp = sctp_sk(sk);
57928c2ecf20Sopenharmony_ci
57938c2ecf20Sopenharmony_ci	if (len >= sizeof(params))
57948c2ecf20Sopenharmony_ci		len = sizeof(params);
57958c2ecf20Sopenharmony_ci	else if (len >= ALIGN(offsetof(struct sctp_paddrparams,
57968c2ecf20Sopenharmony_ci				       spp_ipv6_flowlabel), 4))
57978c2ecf20Sopenharmony_ci		len = ALIGN(offsetof(struct sctp_paddrparams,
57988c2ecf20Sopenharmony_ci				     spp_ipv6_flowlabel), 4);
57998c2ecf20Sopenharmony_ci	else
58008c2ecf20Sopenharmony_ci		return -EINVAL;
58018c2ecf20Sopenharmony_ci
58028c2ecf20Sopenharmony_ci	if (copy_from_user(&params, optval, len))
58038c2ecf20Sopenharmony_ci		return -EFAULT;
58048c2ecf20Sopenharmony_ci
58058c2ecf20Sopenharmony_ci	/* If an address other than INADDR_ANY is specified, and
58068c2ecf20Sopenharmony_ci	 * no transport is found, then the request is invalid.
58078c2ecf20Sopenharmony_ci	 */
58088c2ecf20Sopenharmony_ci	if (!sctp_is_any(sk, (union sctp_addr *)&params.spp_address)) {
58098c2ecf20Sopenharmony_ci		trans = sctp_addr_id2transport(sk, &params.spp_address,
58108c2ecf20Sopenharmony_ci					       params.spp_assoc_id);
58118c2ecf20Sopenharmony_ci		if (!trans) {
58128c2ecf20Sopenharmony_ci			pr_debug("%s: failed no transport\n", __func__);
58138c2ecf20Sopenharmony_ci			return -EINVAL;
58148c2ecf20Sopenharmony_ci		}
58158c2ecf20Sopenharmony_ci	}
58168c2ecf20Sopenharmony_ci
58178c2ecf20Sopenharmony_ci	/* Get association, if assoc_id != SCTP_FUTURE_ASSOC and the
58188c2ecf20Sopenharmony_ci	 * socket is a one to many style socket, and an association
58198c2ecf20Sopenharmony_ci	 * was not found, then the id was invalid.
58208c2ecf20Sopenharmony_ci	 */
58218c2ecf20Sopenharmony_ci	asoc = sctp_id2assoc(sk, params.spp_assoc_id);
58228c2ecf20Sopenharmony_ci	if (!asoc && params.spp_assoc_id != SCTP_FUTURE_ASSOC &&
58238c2ecf20Sopenharmony_ci	    sctp_style(sk, UDP)) {
58248c2ecf20Sopenharmony_ci		pr_debug("%s: failed no association\n", __func__);
58258c2ecf20Sopenharmony_ci		return -EINVAL;
58268c2ecf20Sopenharmony_ci	}
58278c2ecf20Sopenharmony_ci
58288c2ecf20Sopenharmony_ci	if (trans) {
58298c2ecf20Sopenharmony_ci		/* Fetch transport values. */
58308c2ecf20Sopenharmony_ci		params.spp_hbinterval = jiffies_to_msecs(trans->hbinterval);
58318c2ecf20Sopenharmony_ci		params.spp_pathmtu    = trans->pathmtu;
58328c2ecf20Sopenharmony_ci		params.spp_pathmaxrxt = trans->pathmaxrxt;
58338c2ecf20Sopenharmony_ci		params.spp_sackdelay  = jiffies_to_msecs(trans->sackdelay);
58348c2ecf20Sopenharmony_ci
58358c2ecf20Sopenharmony_ci		/*draft-11 doesn't say what to return in spp_flags*/
58368c2ecf20Sopenharmony_ci		params.spp_flags      = trans->param_flags;
58378c2ecf20Sopenharmony_ci		if (trans->flowlabel & SCTP_FLOWLABEL_SET_MASK) {
58388c2ecf20Sopenharmony_ci			params.spp_ipv6_flowlabel = trans->flowlabel &
58398c2ecf20Sopenharmony_ci						    SCTP_FLOWLABEL_VAL_MASK;
58408c2ecf20Sopenharmony_ci			params.spp_flags |= SPP_IPV6_FLOWLABEL;
58418c2ecf20Sopenharmony_ci		}
58428c2ecf20Sopenharmony_ci		if (trans->dscp & SCTP_DSCP_SET_MASK) {
58438c2ecf20Sopenharmony_ci			params.spp_dscp	= trans->dscp & SCTP_DSCP_VAL_MASK;
58448c2ecf20Sopenharmony_ci			params.spp_flags |= SPP_DSCP;
58458c2ecf20Sopenharmony_ci		}
58468c2ecf20Sopenharmony_ci	} else if (asoc) {
58478c2ecf20Sopenharmony_ci		/* Fetch association values. */
58488c2ecf20Sopenharmony_ci		params.spp_hbinterval = jiffies_to_msecs(asoc->hbinterval);
58498c2ecf20Sopenharmony_ci		params.spp_pathmtu    = asoc->pathmtu;
58508c2ecf20Sopenharmony_ci		params.spp_pathmaxrxt = asoc->pathmaxrxt;
58518c2ecf20Sopenharmony_ci		params.spp_sackdelay  = jiffies_to_msecs(asoc->sackdelay);
58528c2ecf20Sopenharmony_ci
58538c2ecf20Sopenharmony_ci		/*draft-11 doesn't say what to return in spp_flags*/
58548c2ecf20Sopenharmony_ci		params.spp_flags      = asoc->param_flags;
58558c2ecf20Sopenharmony_ci		if (asoc->flowlabel & SCTP_FLOWLABEL_SET_MASK) {
58568c2ecf20Sopenharmony_ci			params.spp_ipv6_flowlabel = asoc->flowlabel &
58578c2ecf20Sopenharmony_ci						    SCTP_FLOWLABEL_VAL_MASK;
58588c2ecf20Sopenharmony_ci			params.spp_flags |= SPP_IPV6_FLOWLABEL;
58598c2ecf20Sopenharmony_ci		}
58608c2ecf20Sopenharmony_ci		if (asoc->dscp & SCTP_DSCP_SET_MASK) {
58618c2ecf20Sopenharmony_ci			params.spp_dscp	= asoc->dscp & SCTP_DSCP_VAL_MASK;
58628c2ecf20Sopenharmony_ci			params.spp_flags |= SPP_DSCP;
58638c2ecf20Sopenharmony_ci		}
58648c2ecf20Sopenharmony_ci	} else {
58658c2ecf20Sopenharmony_ci		/* Fetch socket values. */
58668c2ecf20Sopenharmony_ci		params.spp_hbinterval = sp->hbinterval;
58678c2ecf20Sopenharmony_ci		params.spp_pathmtu    = sp->pathmtu;
58688c2ecf20Sopenharmony_ci		params.spp_sackdelay  = sp->sackdelay;
58698c2ecf20Sopenharmony_ci		params.spp_pathmaxrxt = sp->pathmaxrxt;
58708c2ecf20Sopenharmony_ci
58718c2ecf20Sopenharmony_ci		/*draft-11 doesn't say what to return in spp_flags*/
58728c2ecf20Sopenharmony_ci		params.spp_flags      = sp->param_flags;
58738c2ecf20Sopenharmony_ci		if (sp->flowlabel & SCTP_FLOWLABEL_SET_MASK) {
58748c2ecf20Sopenharmony_ci			params.spp_ipv6_flowlabel = sp->flowlabel &
58758c2ecf20Sopenharmony_ci						    SCTP_FLOWLABEL_VAL_MASK;
58768c2ecf20Sopenharmony_ci			params.spp_flags |= SPP_IPV6_FLOWLABEL;
58778c2ecf20Sopenharmony_ci		}
58788c2ecf20Sopenharmony_ci		if (sp->dscp & SCTP_DSCP_SET_MASK) {
58798c2ecf20Sopenharmony_ci			params.spp_dscp	= sp->dscp & SCTP_DSCP_VAL_MASK;
58808c2ecf20Sopenharmony_ci			params.spp_flags |= SPP_DSCP;
58818c2ecf20Sopenharmony_ci		}
58828c2ecf20Sopenharmony_ci	}
58838c2ecf20Sopenharmony_ci
58848c2ecf20Sopenharmony_ci	if (copy_to_user(optval, &params, len))
58858c2ecf20Sopenharmony_ci		return -EFAULT;
58868c2ecf20Sopenharmony_ci
58878c2ecf20Sopenharmony_ci	if (put_user(len, optlen))
58888c2ecf20Sopenharmony_ci		return -EFAULT;
58898c2ecf20Sopenharmony_ci
58908c2ecf20Sopenharmony_ci	return 0;
58918c2ecf20Sopenharmony_ci}
58928c2ecf20Sopenharmony_ci
58938c2ecf20Sopenharmony_ci/*
58948c2ecf20Sopenharmony_ci * 7.1.23.  Get or set delayed ack timer (SCTP_DELAYED_SACK)
58958c2ecf20Sopenharmony_ci *
58968c2ecf20Sopenharmony_ci * This option will effect the way delayed acks are performed.  This
58978c2ecf20Sopenharmony_ci * option allows you to get or set the delayed ack time, in
58988c2ecf20Sopenharmony_ci * milliseconds.  It also allows changing the delayed ack frequency.
58998c2ecf20Sopenharmony_ci * Changing the frequency to 1 disables the delayed sack algorithm.  If
59008c2ecf20Sopenharmony_ci * the assoc_id is 0, then this sets or gets the endpoints default
59018c2ecf20Sopenharmony_ci * values.  If the assoc_id field is non-zero, then the set or get
59028c2ecf20Sopenharmony_ci * effects the specified association for the one to many model (the
59038c2ecf20Sopenharmony_ci * assoc_id field is ignored by the one to one model).  Note that if
59048c2ecf20Sopenharmony_ci * sack_delay or sack_freq are 0 when setting this option, then the
59058c2ecf20Sopenharmony_ci * current values will remain unchanged.
59068c2ecf20Sopenharmony_ci *
59078c2ecf20Sopenharmony_ci * struct sctp_sack_info {
59088c2ecf20Sopenharmony_ci *     sctp_assoc_t            sack_assoc_id;
59098c2ecf20Sopenharmony_ci *     uint32_t                sack_delay;
59108c2ecf20Sopenharmony_ci *     uint32_t                sack_freq;
59118c2ecf20Sopenharmony_ci * };
59128c2ecf20Sopenharmony_ci *
59138c2ecf20Sopenharmony_ci * sack_assoc_id -  This parameter, indicates which association the user
59148c2ecf20Sopenharmony_ci *    is performing an action upon.  Note that if this field's value is
59158c2ecf20Sopenharmony_ci *    zero then the endpoints default value is changed (effecting future
59168c2ecf20Sopenharmony_ci *    associations only).
59178c2ecf20Sopenharmony_ci *
59188c2ecf20Sopenharmony_ci * sack_delay -  This parameter contains the number of milliseconds that
59198c2ecf20Sopenharmony_ci *    the user is requesting the delayed ACK timer be set to.  Note that
59208c2ecf20Sopenharmony_ci *    this value is defined in the standard to be between 200 and 500
59218c2ecf20Sopenharmony_ci *    milliseconds.
59228c2ecf20Sopenharmony_ci *
59238c2ecf20Sopenharmony_ci * sack_freq -  This parameter contains the number of packets that must
59248c2ecf20Sopenharmony_ci *    be received before a sack is sent without waiting for the delay
59258c2ecf20Sopenharmony_ci *    timer to expire.  The default value for this is 2, setting this
59268c2ecf20Sopenharmony_ci *    value to 1 will disable the delayed sack algorithm.
59278c2ecf20Sopenharmony_ci */
59288c2ecf20Sopenharmony_cistatic int sctp_getsockopt_delayed_ack(struct sock *sk, int len,
59298c2ecf20Sopenharmony_ci					    char __user *optval,
59308c2ecf20Sopenharmony_ci					    int __user *optlen)
59318c2ecf20Sopenharmony_ci{
59328c2ecf20Sopenharmony_ci	struct sctp_sack_info    params;
59338c2ecf20Sopenharmony_ci	struct sctp_association *asoc = NULL;
59348c2ecf20Sopenharmony_ci	struct sctp_sock        *sp = sctp_sk(sk);
59358c2ecf20Sopenharmony_ci
59368c2ecf20Sopenharmony_ci	if (len >= sizeof(struct sctp_sack_info)) {
59378c2ecf20Sopenharmony_ci		len = sizeof(struct sctp_sack_info);
59388c2ecf20Sopenharmony_ci
59398c2ecf20Sopenharmony_ci		if (copy_from_user(&params, optval, len))
59408c2ecf20Sopenharmony_ci			return -EFAULT;
59418c2ecf20Sopenharmony_ci	} else if (len == sizeof(struct sctp_assoc_value)) {
59428c2ecf20Sopenharmony_ci		pr_warn_ratelimited(DEPRECATED
59438c2ecf20Sopenharmony_ci				    "%s (pid %d) "
59448c2ecf20Sopenharmony_ci				    "Use of struct sctp_assoc_value in delayed_ack socket option.\n"
59458c2ecf20Sopenharmony_ci				    "Use struct sctp_sack_info instead\n",
59468c2ecf20Sopenharmony_ci				    current->comm, task_pid_nr(current));
59478c2ecf20Sopenharmony_ci		if (copy_from_user(&params, optval, len))
59488c2ecf20Sopenharmony_ci			return -EFAULT;
59498c2ecf20Sopenharmony_ci	} else
59508c2ecf20Sopenharmony_ci		return -EINVAL;
59518c2ecf20Sopenharmony_ci
59528c2ecf20Sopenharmony_ci	/* Get association, if sack_assoc_id != SCTP_FUTURE_ASSOC and the
59538c2ecf20Sopenharmony_ci	 * socket is a one to many style socket, and an association
59548c2ecf20Sopenharmony_ci	 * was not found, then the id was invalid.
59558c2ecf20Sopenharmony_ci	 */
59568c2ecf20Sopenharmony_ci	asoc = sctp_id2assoc(sk, params.sack_assoc_id);
59578c2ecf20Sopenharmony_ci	if (!asoc && params.sack_assoc_id != SCTP_FUTURE_ASSOC &&
59588c2ecf20Sopenharmony_ci	    sctp_style(sk, UDP))
59598c2ecf20Sopenharmony_ci		return -EINVAL;
59608c2ecf20Sopenharmony_ci
59618c2ecf20Sopenharmony_ci	if (asoc) {
59628c2ecf20Sopenharmony_ci		/* Fetch association values. */
59638c2ecf20Sopenharmony_ci		if (asoc->param_flags & SPP_SACKDELAY_ENABLE) {
59648c2ecf20Sopenharmony_ci			params.sack_delay = jiffies_to_msecs(asoc->sackdelay);
59658c2ecf20Sopenharmony_ci			params.sack_freq = asoc->sackfreq;
59668c2ecf20Sopenharmony_ci
59678c2ecf20Sopenharmony_ci		} else {
59688c2ecf20Sopenharmony_ci			params.sack_delay = 0;
59698c2ecf20Sopenharmony_ci			params.sack_freq = 1;
59708c2ecf20Sopenharmony_ci		}
59718c2ecf20Sopenharmony_ci	} else {
59728c2ecf20Sopenharmony_ci		/* Fetch socket values. */
59738c2ecf20Sopenharmony_ci		if (sp->param_flags & SPP_SACKDELAY_ENABLE) {
59748c2ecf20Sopenharmony_ci			params.sack_delay  = sp->sackdelay;
59758c2ecf20Sopenharmony_ci			params.sack_freq = sp->sackfreq;
59768c2ecf20Sopenharmony_ci		} else {
59778c2ecf20Sopenharmony_ci			params.sack_delay  = 0;
59788c2ecf20Sopenharmony_ci			params.sack_freq = 1;
59798c2ecf20Sopenharmony_ci		}
59808c2ecf20Sopenharmony_ci	}
59818c2ecf20Sopenharmony_ci
59828c2ecf20Sopenharmony_ci	if (copy_to_user(optval, &params, len))
59838c2ecf20Sopenharmony_ci		return -EFAULT;
59848c2ecf20Sopenharmony_ci
59858c2ecf20Sopenharmony_ci	if (put_user(len, optlen))
59868c2ecf20Sopenharmony_ci		return -EFAULT;
59878c2ecf20Sopenharmony_ci
59888c2ecf20Sopenharmony_ci	return 0;
59898c2ecf20Sopenharmony_ci}
59908c2ecf20Sopenharmony_ci
59918c2ecf20Sopenharmony_ci/* 7.1.3 Initialization Parameters (SCTP_INITMSG)
59928c2ecf20Sopenharmony_ci *
59938c2ecf20Sopenharmony_ci * Applications can specify protocol parameters for the default association
59948c2ecf20Sopenharmony_ci * initialization.  The option name argument to setsockopt() and getsockopt()
59958c2ecf20Sopenharmony_ci * is SCTP_INITMSG.
59968c2ecf20Sopenharmony_ci *
59978c2ecf20Sopenharmony_ci * Setting initialization parameters is effective only on an unconnected
59988c2ecf20Sopenharmony_ci * socket (for UDP-style sockets only future associations are effected
59998c2ecf20Sopenharmony_ci * by the change).  With TCP-style sockets, this option is inherited by
60008c2ecf20Sopenharmony_ci * sockets derived from a listener socket.
60018c2ecf20Sopenharmony_ci */
60028c2ecf20Sopenharmony_cistatic int sctp_getsockopt_initmsg(struct sock *sk, int len, char __user *optval, int __user *optlen)
60038c2ecf20Sopenharmony_ci{
60048c2ecf20Sopenharmony_ci	if (len < sizeof(struct sctp_initmsg))
60058c2ecf20Sopenharmony_ci		return -EINVAL;
60068c2ecf20Sopenharmony_ci	len = sizeof(struct sctp_initmsg);
60078c2ecf20Sopenharmony_ci	if (put_user(len, optlen))
60088c2ecf20Sopenharmony_ci		return -EFAULT;
60098c2ecf20Sopenharmony_ci	if (copy_to_user(optval, &sctp_sk(sk)->initmsg, len))
60108c2ecf20Sopenharmony_ci		return -EFAULT;
60118c2ecf20Sopenharmony_ci	return 0;
60128c2ecf20Sopenharmony_ci}
60138c2ecf20Sopenharmony_ci
60148c2ecf20Sopenharmony_ci
60158c2ecf20Sopenharmony_cistatic int sctp_getsockopt_peer_addrs(struct sock *sk, int len,
60168c2ecf20Sopenharmony_ci				      char __user *optval, int __user *optlen)
60178c2ecf20Sopenharmony_ci{
60188c2ecf20Sopenharmony_ci	struct sctp_association *asoc;
60198c2ecf20Sopenharmony_ci	int cnt = 0;
60208c2ecf20Sopenharmony_ci	struct sctp_getaddrs getaddrs;
60218c2ecf20Sopenharmony_ci	struct sctp_transport *from;
60228c2ecf20Sopenharmony_ci	void __user *to;
60238c2ecf20Sopenharmony_ci	union sctp_addr temp;
60248c2ecf20Sopenharmony_ci	struct sctp_sock *sp = sctp_sk(sk);
60258c2ecf20Sopenharmony_ci	int addrlen;
60268c2ecf20Sopenharmony_ci	size_t space_left;
60278c2ecf20Sopenharmony_ci	int bytes_copied;
60288c2ecf20Sopenharmony_ci
60298c2ecf20Sopenharmony_ci	if (len < sizeof(struct sctp_getaddrs))
60308c2ecf20Sopenharmony_ci		return -EINVAL;
60318c2ecf20Sopenharmony_ci
60328c2ecf20Sopenharmony_ci	if (copy_from_user(&getaddrs, optval, sizeof(struct sctp_getaddrs)))
60338c2ecf20Sopenharmony_ci		return -EFAULT;
60348c2ecf20Sopenharmony_ci
60358c2ecf20Sopenharmony_ci	/* For UDP-style sockets, id specifies the association to query.  */
60368c2ecf20Sopenharmony_ci	asoc = sctp_id2assoc(sk, getaddrs.assoc_id);
60378c2ecf20Sopenharmony_ci	if (!asoc)
60388c2ecf20Sopenharmony_ci		return -EINVAL;
60398c2ecf20Sopenharmony_ci
60408c2ecf20Sopenharmony_ci	to = optval + offsetof(struct sctp_getaddrs, addrs);
60418c2ecf20Sopenharmony_ci	space_left = len - offsetof(struct sctp_getaddrs, addrs);
60428c2ecf20Sopenharmony_ci
60438c2ecf20Sopenharmony_ci	list_for_each_entry(from, &asoc->peer.transport_addr_list,
60448c2ecf20Sopenharmony_ci				transports) {
60458c2ecf20Sopenharmony_ci		memcpy(&temp, &from->ipaddr, sizeof(temp));
60468c2ecf20Sopenharmony_ci		addrlen = sctp_get_pf_specific(sk->sk_family)
60478c2ecf20Sopenharmony_ci			      ->addr_to_user(sp, &temp);
60488c2ecf20Sopenharmony_ci		if (space_left < addrlen)
60498c2ecf20Sopenharmony_ci			return -ENOMEM;
60508c2ecf20Sopenharmony_ci		if (copy_to_user(to, &temp, addrlen))
60518c2ecf20Sopenharmony_ci			return -EFAULT;
60528c2ecf20Sopenharmony_ci		to += addrlen;
60538c2ecf20Sopenharmony_ci		cnt++;
60548c2ecf20Sopenharmony_ci		space_left -= addrlen;
60558c2ecf20Sopenharmony_ci	}
60568c2ecf20Sopenharmony_ci
60578c2ecf20Sopenharmony_ci	if (put_user(cnt, &((struct sctp_getaddrs __user *)optval)->addr_num))
60588c2ecf20Sopenharmony_ci		return -EFAULT;
60598c2ecf20Sopenharmony_ci	bytes_copied = ((char __user *)to) - optval;
60608c2ecf20Sopenharmony_ci	if (put_user(bytes_copied, optlen))
60618c2ecf20Sopenharmony_ci		return -EFAULT;
60628c2ecf20Sopenharmony_ci
60638c2ecf20Sopenharmony_ci	return 0;
60648c2ecf20Sopenharmony_ci}
60658c2ecf20Sopenharmony_ci
60668c2ecf20Sopenharmony_cistatic int sctp_copy_laddrs(struct sock *sk, __u16 port, void *to,
60678c2ecf20Sopenharmony_ci			    size_t space_left, int *bytes_copied)
60688c2ecf20Sopenharmony_ci{
60698c2ecf20Sopenharmony_ci	struct sctp_sockaddr_entry *addr;
60708c2ecf20Sopenharmony_ci	union sctp_addr temp;
60718c2ecf20Sopenharmony_ci	int cnt = 0;
60728c2ecf20Sopenharmony_ci	int addrlen;
60738c2ecf20Sopenharmony_ci	struct net *net = sock_net(sk);
60748c2ecf20Sopenharmony_ci
60758c2ecf20Sopenharmony_ci	rcu_read_lock();
60768c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(addr, &net->sctp.local_addr_list, list) {
60778c2ecf20Sopenharmony_ci		if (!addr->valid)
60788c2ecf20Sopenharmony_ci			continue;
60798c2ecf20Sopenharmony_ci
60808c2ecf20Sopenharmony_ci		if ((PF_INET == sk->sk_family) &&
60818c2ecf20Sopenharmony_ci		    (AF_INET6 == addr->a.sa.sa_family))
60828c2ecf20Sopenharmony_ci			continue;
60838c2ecf20Sopenharmony_ci		if ((PF_INET6 == sk->sk_family) &&
60848c2ecf20Sopenharmony_ci		    inet_v6_ipv6only(sk) &&
60858c2ecf20Sopenharmony_ci		    (AF_INET == addr->a.sa.sa_family))
60868c2ecf20Sopenharmony_ci			continue;
60878c2ecf20Sopenharmony_ci		memcpy(&temp, &addr->a, sizeof(temp));
60888c2ecf20Sopenharmony_ci		if (!temp.v4.sin_port)
60898c2ecf20Sopenharmony_ci			temp.v4.sin_port = htons(port);
60908c2ecf20Sopenharmony_ci
60918c2ecf20Sopenharmony_ci		addrlen = sctp_get_pf_specific(sk->sk_family)
60928c2ecf20Sopenharmony_ci			      ->addr_to_user(sctp_sk(sk), &temp);
60938c2ecf20Sopenharmony_ci
60948c2ecf20Sopenharmony_ci		if (space_left < addrlen) {
60958c2ecf20Sopenharmony_ci			cnt =  -ENOMEM;
60968c2ecf20Sopenharmony_ci			break;
60978c2ecf20Sopenharmony_ci		}
60988c2ecf20Sopenharmony_ci		memcpy(to, &temp, addrlen);
60998c2ecf20Sopenharmony_ci
61008c2ecf20Sopenharmony_ci		to += addrlen;
61018c2ecf20Sopenharmony_ci		cnt++;
61028c2ecf20Sopenharmony_ci		space_left -= addrlen;
61038c2ecf20Sopenharmony_ci		*bytes_copied += addrlen;
61048c2ecf20Sopenharmony_ci	}
61058c2ecf20Sopenharmony_ci	rcu_read_unlock();
61068c2ecf20Sopenharmony_ci
61078c2ecf20Sopenharmony_ci	return cnt;
61088c2ecf20Sopenharmony_ci}
61098c2ecf20Sopenharmony_ci
61108c2ecf20Sopenharmony_ci
61118c2ecf20Sopenharmony_cistatic int sctp_getsockopt_local_addrs(struct sock *sk, int len,
61128c2ecf20Sopenharmony_ci				       char __user *optval, int __user *optlen)
61138c2ecf20Sopenharmony_ci{
61148c2ecf20Sopenharmony_ci	struct sctp_bind_addr *bp;
61158c2ecf20Sopenharmony_ci	struct sctp_association *asoc;
61168c2ecf20Sopenharmony_ci	int cnt = 0;
61178c2ecf20Sopenharmony_ci	struct sctp_getaddrs getaddrs;
61188c2ecf20Sopenharmony_ci	struct sctp_sockaddr_entry *addr;
61198c2ecf20Sopenharmony_ci	void __user *to;
61208c2ecf20Sopenharmony_ci	union sctp_addr temp;
61218c2ecf20Sopenharmony_ci	struct sctp_sock *sp = sctp_sk(sk);
61228c2ecf20Sopenharmony_ci	int addrlen;
61238c2ecf20Sopenharmony_ci	int err = 0;
61248c2ecf20Sopenharmony_ci	size_t space_left;
61258c2ecf20Sopenharmony_ci	int bytes_copied = 0;
61268c2ecf20Sopenharmony_ci	void *addrs;
61278c2ecf20Sopenharmony_ci	void *buf;
61288c2ecf20Sopenharmony_ci
61298c2ecf20Sopenharmony_ci	if (len < sizeof(struct sctp_getaddrs))
61308c2ecf20Sopenharmony_ci		return -EINVAL;
61318c2ecf20Sopenharmony_ci
61328c2ecf20Sopenharmony_ci	if (copy_from_user(&getaddrs, optval, sizeof(struct sctp_getaddrs)))
61338c2ecf20Sopenharmony_ci		return -EFAULT;
61348c2ecf20Sopenharmony_ci
61358c2ecf20Sopenharmony_ci	/*
61368c2ecf20Sopenharmony_ci	 *  For UDP-style sockets, id specifies the association to query.
61378c2ecf20Sopenharmony_ci	 *  If the id field is set to the value '0' then the locally bound
61388c2ecf20Sopenharmony_ci	 *  addresses are returned without regard to any particular
61398c2ecf20Sopenharmony_ci	 *  association.
61408c2ecf20Sopenharmony_ci	 */
61418c2ecf20Sopenharmony_ci	if (0 == getaddrs.assoc_id) {
61428c2ecf20Sopenharmony_ci		bp = &sctp_sk(sk)->ep->base.bind_addr;
61438c2ecf20Sopenharmony_ci	} else {
61448c2ecf20Sopenharmony_ci		asoc = sctp_id2assoc(sk, getaddrs.assoc_id);
61458c2ecf20Sopenharmony_ci		if (!asoc)
61468c2ecf20Sopenharmony_ci			return -EINVAL;
61478c2ecf20Sopenharmony_ci		bp = &asoc->base.bind_addr;
61488c2ecf20Sopenharmony_ci	}
61498c2ecf20Sopenharmony_ci
61508c2ecf20Sopenharmony_ci	to = optval + offsetof(struct sctp_getaddrs, addrs);
61518c2ecf20Sopenharmony_ci	space_left = len - offsetof(struct sctp_getaddrs, addrs);
61528c2ecf20Sopenharmony_ci
61538c2ecf20Sopenharmony_ci	addrs = kmalloc(space_left, GFP_USER | __GFP_NOWARN);
61548c2ecf20Sopenharmony_ci	if (!addrs)
61558c2ecf20Sopenharmony_ci		return -ENOMEM;
61568c2ecf20Sopenharmony_ci
61578c2ecf20Sopenharmony_ci	/* If the endpoint is bound to 0.0.0.0 or ::0, get the valid
61588c2ecf20Sopenharmony_ci	 * addresses from the global local address list.
61598c2ecf20Sopenharmony_ci	 */
61608c2ecf20Sopenharmony_ci	if (sctp_list_single_entry(&bp->address_list)) {
61618c2ecf20Sopenharmony_ci		addr = list_entry(bp->address_list.next,
61628c2ecf20Sopenharmony_ci				  struct sctp_sockaddr_entry, list);
61638c2ecf20Sopenharmony_ci		if (sctp_is_any(sk, &addr->a)) {
61648c2ecf20Sopenharmony_ci			cnt = sctp_copy_laddrs(sk, bp->port, addrs,
61658c2ecf20Sopenharmony_ci						space_left, &bytes_copied);
61668c2ecf20Sopenharmony_ci			if (cnt < 0) {
61678c2ecf20Sopenharmony_ci				err = cnt;
61688c2ecf20Sopenharmony_ci				goto out;
61698c2ecf20Sopenharmony_ci			}
61708c2ecf20Sopenharmony_ci			goto copy_getaddrs;
61718c2ecf20Sopenharmony_ci		}
61728c2ecf20Sopenharmony_ci	}
61738c2ecf20Sopenharmony_ci
61748c2ecf20Sopenharmony_ci	buf = addrs;
61758c2ecf20Sopenharmony_ci	/* Protection on the bound address list is not needed since
61768c2ecf20Sopenharmony_ci	 * in the socket option context we hold a socket lock and
61778c2ecf20Sopenharmony_ci	 * thus the bound address list can't change.
61788c2ecf20Sopenharmony_ci	 */
61798c2ecf20Sopenharmony_ci	list_for_each_entry(addr, &bp->address_list, list) {
61808c2ecf20Sopenharmony_ci		memcpy(&temp, &addr->a, sizeof(temp));
61818c2ecf20Sopenharmony_ci		addrlen = sctp_get_pf_specific(sk->sk_family)
61828c2ecf20Sopenharmony_ci			      ->addr_to_user(sp, &temp);
61838c2ecf20Sopenharmony_ci		if (space_left < addrlen) {
61848c2ecf20Sopenharmony_ci			err =  -ENOMEM; /*fixme: right error?*/
61858c2ecf20Sopenharmony_ci			goto out;
61868c2ecf20Sopenharmony_ci		}
61878c2ecf20Sopenharmony_ci		memcpy(buf, &temp, addrlen);
61888c2ecf20Sopenharmony_ci		buf += addrlen;
61898c2ecf20Sopenharmony_ci		bytes_copied += addrlen;
61908c2ecf20Sopenharmony_ci		cnt++;
61918c2ecf20Sopenharmony_ci		space_left -= addrlen;
61928c2ecf20Sopenharmony_ci	}
61938c2ecf20Sopenharmony_ci
61948c2ecf20Sopenharmony_cicopy_getaddrs:
61958c2ecf20Sopenharmony_ci	if (copy_to_user(to, addrs, bytes_copied)) {
61968c2ecf20Sopenharmony_ci		err = -EFAULT;
61978c2ecf20Sopenharmony_ci		goto out;
61988c2ecf20Sopenharmony_ci	}
61998c2ecf20Sopenharmony_ci	if (put_user(cnt, &((struct sctp_getaddrs __user *)optval)->addr_num)) {
62008c2ecf20Sopenharmony_ci		err = -EFAULT;
62018c2ecf20Sopenharmony_ci		goto out;
62028c2ecf20Sopenharmony_ci	}
62038c2ecf20Sopenharmony_ci	/* XXX: We should have accounted for sizeof(struct sctp_getaddrs) too,
62048c2ecf20Sopenharmony_ci	 * but we can't change it anymore.
62058c2ecf20Sopenharmony_ci	 */
62068c2ecf20Sopenharmony_ci	if (put_user(bytes_copied, optlen))
62078c2ecf20Sopenharmony_ci		err = -EFAULT;
62088c2ecf20Sopenharmony_ciout:
62098c2ecf20Sopenharmony_ci	kfree(addrs);
62108c2ecf20Sopenharmony_ci	return err;
62118c2ecf20Sopenharmony_ci}
62128c2ecf20Sopenharmony_ci
62138c2ecf20Sopenharmony_ci/* 7.1.10 Set Primary Address (SCTP_PRIMARY_ADDR)
62148c2ecf20Sopenharmony_ci *
62158c2ecf20Sopenharmony_ci * Requests that the local SCTP stack use the enclosed peer address as
62168c2ecf20Sopenharmony_ci * the association primary.  The enclosed address must be one of the
62178c2ecf20Sopenharmony_ci * association peer's addresses.
62188c2ecf20Sopenharmony_ci */
62198c2ecf20Sopenharmony_cistatic int sctp_getsockopt_primary_addr(struct sock *sk, int len,
62208c2ecf20Sopenharmony_ci					char __user *optval, int __user *optlen)
62218c2ecf20Sopenharmony_ci{
62228c2ecf20Sopenharmony_ci	struct sctp_prim prim;
62238c2ecf20Sopenharmony_ci	struct sctp_association *asoc;
62248c2ecf20Sopenharmony_ci	struct sctp_sock *sp = sctp_sk(sk);
62258c2ecf20Sopenharmony_ci
62268c2ecf20Sopenharmony_ci	if (len < sizeof(struct sctp_prim))
62278c2ecf20Sopenharmony_ci		return -EINVAL;
62288c2ecf20Sopenharmony_ci
62298c2ecf20Sopenharmony_ci	len = sizeof(struct sctp_prim);
62308c2ecf20Sopenharmony_ci
62318c2ecf20Sopenharmony_ci	if (copy_from_user(&prim, optval, len))
62328c2ecf20Sopenharmony_ci		return -EFAULT;
62338c2ecf20Sopenharmony_ci
62348c2ecf20Sopenharmony_ci	asoc = sctp_id2assoc(sk, prim.ssp_assoc_id);
62358c2ecf20Sopenharmony_ci	if (!asoc)
62368c2ecf20Sopenharmony_ci		return -EINVAL;
62378c2ecf20Sopenharmony_ci
62388c2ecf20Sopenharmony_ci	if (!asoc->peer.primary_path)
62398c2ecf20Sopenharmony_ci		return -ENOTCONN;
62408c2ecf20Sopenharmony_ci
62418c2ecf20Sopenharmony_ci	memcpy(&prim.ssp_addr, &asoc->peer.primary_path->ipaddr,
62428c2ecf20Sopenharmony_ci		asoc->peer.primary_path->af_specific->sockaddr_len);
62438c2ecf20Sopenharmony_ci
62448c2ecf20Sopenharmony_ci	sctp_get_pf_specific(sk->sk_family)->addr_to_user(sp,
62458c2ecf20Sopenharmony_ci			(union sctp_addr *)&prim.ssp_addr);
62468c2ecf20Sopenharmony_ci
62478c2ecf20Sopenharmony_ci	if (put_user(len, optlen))
62488c2ecf20Sopenharmony_ci		return -EFAULT;
62498c2ecf20Sopenharmony_ci	if (copy_to_user(optval, &prim, len))
62508c2ecf20Sopenharmony_ci		return -EFAULT;
62518c2ecf20Sopenharmony_ci
62528c2ecf20Sopenharmony_ci	return 0;
62538c2ecf20Sopenharmony_ci}
62548c2ecf20Sopenharmony_ci
62558c2ecf20Sopenharmony_ci/*
62568c2ecf20Sopenharmony_ci * 7.1.11  Set Adaptation Layer Indicator (SCTP_ADAPTATION_LAYER)
62578c2ecf20Sopenharmony_ci *
62588c2ecf20Sopenharmony_ci * Requests that the local endpoint set the specified Adaptation Layer
62598c2ecf20Sopenharmony_ci * Indication parameter for all future INIT and INIT-ACK exchanges.
62608c2ecf20Sopenharmony_ci */
62618c2ecf20Sopenharmony_cistatic int sctp_getsockopt_adaptation_layer(struct sock *sk, int len,
62628c2ecf20Sopenharmony_ci				  char __user *optval, int __user *optlen)
62638c2ecf20Sopenharmony_ci{
62648c2ecf20Sopenharmony_ci	struct sctp_setadaptation adaptation;
62658c2ecf20Sopenharmony_ci
62668c2ecf20Sopenharmony_ci	if (len < sizeof(struct sctp_setadaptation))
62678c2ecf20Sopenharmony_ci		return -EINVAL;
62688c2ecf20Sopenharmony_ci
62698c2ecf20Sopenharmony_ci	len = sizeof(struct sctp_setadaptation);
62708c2ecf20Sopenharmony_ci
62718c2ecf20Sopenharmony_ci	adaptation.ssb_adaptation_ind = sctp_sk(sk)->adaptation_ind;
62728c2ecf20Sopenharmony_ci
62738c2ecf20Sopenharmony_ci	if (put_user(len, optlen))
62748c2ecf20Sopenharmony_ci		return -EFAULT;
62758c2ecf20Sopenharmony_ci	if (copy_to_user(optval, &adaptation, len))
62768c2ecf20Sopenharmony_ci		return -EFAULT;
62778c2ecf20Sopenharmony_ci
62788c2ecf20Sopenharmony_ci	return 0;
62798c2ecf20Sopenharmony_ci}
62808c2ecf20Sopenharmony_ci
62818c2ecf20Sopenharmony_ci/*
62828c2ecf20Sopenharmony_ci *
62838c2ecf20Sopenharmony_ci * 7.1.14 Set default send parameters (SCTP_DEFAULT_SEND_PARAM)
62848c2ecf20Sopenharmony_ci *
62858c2ecf20Sopenharmony_ci *   Applications that wish to use the sendto() system call may wish to
62868c2ecf20Sopenharmony_ci *   specify a default set of parameters that would normally be supplied
62878c2ecf20Sopenharmony_ci *   through the inclusion of ancillary data.  This socket option allows
62888c2ecf20Sopenharmony_ci *   such an application to set the default sctp_sndrcvinfo structure.
62898c2ecf20Sopenharmony_ci
62908c2ecf20Sopenharmony_ci
62918c2ecf20Sopenharmony_ci *   The application that wishes to use this socket option simply passes
62928c2ecf20Sopenharmony_ci *   in to this call the sctp_sndrcvinfo structure defined in Section
62938c2ecf20Sopenharmony_ci *   5.2.2) The input parameters accepted by this call include
62948c2ecf20Sopenharmony_ci *   sinfo_stream, sinfo_flags, sinfo_ppid, sinfo_context,
62958c2ecf20Sopenharmony_ci *   sinfo_timetolive.  The user must provide the sinfo_assoc_id field in
62968c2ecf20Sopenharmony_ci *   to this call if the caller is using the UDP model.
62978c2ecf20Sopenharmony_ci *
62988c2ecf20Sopenharmony_ci *   For getsockopt, it get the default sctp_sndrcvinfo structure.
62998c2ecf20Sopenharmony_ci */
63008c2ecf20Sopenharmony_cistatic int sctp_getsockopt_default_send_param(struct sock *sk,
63018c2ecf20Sopenharmony_ci					int len, char __user *optval,
63028c2ecf20Sopenharmony_ci					int __user *optlen)
63038c2ecf20Sopenharmony_ci{
63048c2ecf20Sopenharmony_ci	struct sctp_sock *sp = sctp_sk(sk);
63058c2ecf20Sopenharmony_ci	struct sctp_association *asoc;
63068c2ecf20Sopenharmony_ci	struct sctp_sndrcvinfo info;
63078c2ecf20Sopenharmony_ci
63088c2ecf20Sopenharmony_ci	if (len < sizeof(info))
63098c2ecf20Sopenharmony_ci		return -EINVAL;
63108c2ecf20Sopenharmony_ci
63118c2ecf20Sopenharmony_ci	len = sizeof(info);
63128c2ecf20Sopenharmony_ci
63138c2ecf20Sopenharmony_ci	if (copy_from_user(&info, optval, len))
63148c2ecf20Sopenharmony_ci		return -EFAULT;
63158c2ecf20Sopenharmony_ci
63168c2ecf20Sopenharmony_ci	asoc = sctp_id2assoc(sk, info.sinfo_assoc_id);
63178c2ecf20Sopenharmony_ci	if (!asoc && info.sinfo_assoc_id != SCTP_FUTURE_ASSOC &&
63188c2ecf20Sopenharmony_ci	    sctp_style(sk, UDP))
63198c2ecf20Sopenharmony_ci		return -EINVAL;
63208c2ecf20Sopenharmony_ci
63218c2ecf20Sopenharmony_ci	if (asoc) {
63228c2ecf20Sopenharmony_ci		info.sinfo_stream = asoc->default_stream;
63238c2ecf20Sopenharmony_ci		info.sinfo_flags = asoc->default_flags;
63248c2ecf20Sopenharmony_ci		info.sinfo_ppid = asoc->default_ppid;
63258c2ecf20Sopenharmony_ci		info.sinfo_context = asoc->default_context;
63268c2ecf20Sopenharmony_ci		info.sinfo_timetolive = asoc->default_timetolive;
63278c2ecf20Sopenharmony_ci	} else {
63288c2ecf20Sopenharmony_ci		info.sinfo_stream = sp->default_stream;
63298c2ecf20Sopenharmony_ci		info.sinfo_flags = sp->default_flags;
63308c2ecf20Sopenharmony_ci		info.sinfo_ppid = sp->default_ppid;
63318c2ecf20Sopenharmony_ci		info.sinfo_context = sp->default_context;
63328c2ecf20Sopenharmony_ci		info.sinfo_timetolive = sp->default_timetolive;
63338c2ecf20Sopenharmony_ci	}
63348c2ecf20Sopenharmony_ci
63358c2ecf20Sopenharmony_ci	if (put_user(len, optlen))
63368c2ecf20Sopenharmony_ci		return -EFAULT;
63378c2ecf20Sopenharmony_ci	if (copy_to_user(optval, &info, len))
63388c2ecf20Sopenharmony_ci		return -EFAULT;
63398c2ecf20Sopenharmony_ci
63408c2ecf20Sopenharmony_ci	return 0;
63418c2ecf20Sopenharmony_ci}
63428c2ecf20Sopenharmony_ci
63438c2ecf20Sopenharmony_ci/* RFC6458, Section 8.1.31. Set/get Default Send Parameters
63448c2ecf20Sopenharmony_ci * (SCTP_DEFAULT_SNDINFO)
63458c2ecf20Sopenharmony_ci */
63468c2ecf20Sopenharmony_cistatic int sctp_getsockopt_default_sndinfo(struct sock *sk, int len,
63478c2ecf20Sopenharmony_ci					   char __user *optval,
63488c2ecf20Sopenharmony_ci					   int __user *optlen)
63498c2ecf20Sopenharmony_ci{
63508c2ecf20Sopenharmony_ci	struct sctp_sock *sp = sctp_sk(sk);
63518c2ecf20Sopenharmony_ci	struct sctp_association *asoc;
63528c2ecf20Sopenharmony_ci	struct sctp_sndinfo info;
63538c2ecf20Sopenharmony_ci
63548c2ecf20Sopenharmony_ci	if (len < sizeof(info))
63558c2ecf20Sopenharmony_ci		return -EINVAL;
63568c2ecf20Sopenharmony_ci
63578c2ecf20Sopenharmony_ci	len = sizeof(info);
63588c2ecf20Sopenharmony_ci
63598c2ecf20Sopenharmony_ci	if (copy_from_user(&info, optval, len))
63608c2ecf20Sopenharmony_ci		return -EFAULT;
63618c2ecf20Sopenharmony_ci
63628c2ecf20Sopenharmony_ci	asoc = sctp_id2assoc(sk, info.snd_assoc_id);
63638c2ecf20Sopenharmony_ci	if (!asoc && info.snd_assoc_id != SCTP_FUTURE_ASSOC &&
63648c2ecf20Sopenharmony_ci	    sctp_style(sk, UDP))
63658c2ecf20Sopenharmony_ci		return -EINVAL;
63668c2ecf20Sopenharmony_ci
63678c2ecf20Sopenharmony_ci	if (asoc) {
63688c2ecf20Sopenharmony_ci		info.snd_sid = asoc->default_stream;
63698c2ecf20Sopenharmony_ci		info.snd_flags = asoc->default_flags;
63708c2ecf20Sopenharmony_ci		info.snd_ppid = asoc->default_ppid;
63718c2ecf20Sopenharmony_ci		info.snd_context = asoc->default_context;
63728c2ecf20Sopenharmony_ci	} else {
63738c2ecf20Sopenharmony_ci		info.snd_sid = sp->default_stream;
63748c2ecf20Sopenharmony_ci		info.snd_flags = sp->default_flags;
63758c2ecf20Sopenharmony_ci		info.snd_ppid = sp->default_ppid;
63768c2ecf20Sopenharmony_ci		info.snd_context = sp->default_context;
63778c2ecf20Sopenharmony_ci	}
63788c2ecf20Sopenharmony_ci
63798c2ecf20Sopenharmony_ci	if (put_user(len, optlen))
63808c2ecf20Sopenharmony_ci		return -EFAULT;
63818c2ecf20Sopenharmony_ci	if (copy_to_user(optval, &info, len))
63828c2ecf20Sopenharmony_ci		return -EFAULT;
63838c2ecf20Sopenharmony_ci
63848c2ecf20Sopenharmony_ci	return 0;
63858c2ecf20Sopenharmony_ci}
63868c2ecf20Sopenharmony_ci
63878c2ecf20Sopenharmony_ci/*
63888c2ecf20Sopenharmony_ci *
63898c2ecf20Sopenharmony_ci * 7.1.5 SCTP_NODELAY
63908c2ecf20Sopenharmony_ci *
63918c2ecf20Sopenharmony_ci * Turn on/off any Nagle-like algorithm.  This means that packets are
63928c2ecf20Sopenharmony_ci * generally sent as soon as possible and no unnecessary delays are
63938c2ecf20Sopenharmony_ci * introduced, at the cost of more packets in the network.  Expects an
63948c2ecf20Sopenharmony_ci * integer boolean flag.
63958c2ecf20Sopenharmony_ci */
63968c2ecf20Sopenharmony_ci
63978c2ecf20Sopenharmony_cistatic int sctp_getsockopt_nodelay(struct sock *sk, int len,
63988c2ecf20Sopenharmony_ci				   char __user *optval, int __user *optlen)
63998c2ecf20Sopenharmony_ci{
64008c2ecf20Sopenharmony_ci	int val;
64018c2ecf20Sopenharmony_ci
64028c2ecf20Sopenharmony_ci	if (len < sizeof(int))
64038c2ecf20Sopenharmony_ci		return -EINVAL;
64048c2ecf20Sopenharmony_ci
64058c2ecf20Sopenharmony_ci	len = sizeof(int);
64068c2ecf20Sopenharmony_ci	val = (sctp_sk(sk)->nodelay == 1);
64078c2ecf20Sopenharmony_ci	if (put_user(len, optlen))
64088c2ecf20Sopenharmony_ci		return -EFAULT;
64098c2ecf20Sopenharmony_ci	if (copy_to_user(optval, &val, len))
64108c2ecf20Sopenharmony_ci		return -EFAULT;
64118c2ecf20Sopenharmony_ci	return 0;
64128c2ecf20Sopenharmony_ci}
64138c2ecf20Sopenharmony_ci
64148c2ecf20Sopenharmony_ci/*
64158c2ecf20Sopenharmony_ci *
64168c2ecf20Sopenharmony_ci * 7.1.1 SCTP_RTOINFO
64178c2ecf20Sopenharmony_ci *
64188c2ecf20Sopenharmony_ci * The protocol parameters used to initialize and bound retransmission
64198c2ecf20Sopenharmony_ci * timeout (RTO) are tunable. sctp_rtoinfo structure is used to access
64208c2ecf20Sopenharmony_ci * and modify these parameters.
64218c2ecf20Sopenharmony_ci * All parameters are time values, in milliseconds.  A value of 0, when
64228c2ecf20Sopenharmony_ci * modifying the parameters, indicates that the current value should not
64238c2ecf20Sopenharmony_ci * be changed.
64248c2ecf20Sopenharmony_ci *
64258c2ecf20Sopenharmony_ci */
64268c2ecf20Sopenharmony_cistatic int sctp_getsockopt_rtoinfo(struct sock *sk, int len,
64278c2ecf20Sopenharmony_ci				char __user *optval,
64288c2ecf20Sopenharmony_ci				int __user *optlen) {
64298c2ecf20Sopenharmony_ci	struct sctp_rtoinfo rtoinfo;
64308c2ecf20Sopenharmony_ci	struct sctp_association *asoc;
64318c2ecf20Sopenharmony_ci
64328c2ecf20Sopenharmony_ci	if (len < sizeof (struct sctp_rtoinfo))
64338c2ecf20Sopenharmony_ci		return -EINVAL;
64348c2ecf20Sopenharmony_ci
64358c2ecf20Sopenharmony_ci	len = sizeof(struct sctp_rtoinfo);
64368c2ecf20Sopenharmony_ci
64378c2ecf20Sopenharmony_ci	if (copy_from_user(&rtoinfo, optval, len))
64388c2ecf20Sopenharmony_ci		return -EFAULT;
64398c2ecf20Sopenharmony_ci
64408c2ecf20Sopenharmony_ci	asoc = sctp_id2assoc(sk, rtoinfo.srto_assoc_id);
64418c2ecf20Sopenharmony_ci
64428c2ecf20Sopenharmony_ci	if (!asoc && rtoinfo.srto_assoc_id != SCTP_FUTURE_ASSOC &&
64438c2ecf20Sopenharmony_ci	    sctp_style(sk, UDP))
64448c2ecf20Sopenharmony_ci		return -EINVAL;
64458c2ecf20Sopenharmony_ci
64468c2ecf20Sopenharmony_ci	/* Values corresponding to the specific association. */
64478c2ecf20Sopenharmony_ci	if (asoc) {
64488c2ecf20Sopenharmony_ci		rtoinfo.srto_initial = jiffies_to_msecs(asoc->rto_initial);
64498c2ecf20Sopenharmony_ci		rtoinfo.srto_max = jiffies_to_msecs(asoc->rto_max);
64508c2ecf20Sopenharmony_ci		rtoinfo.srto_min = jiffies_to_msecs(asoc->rto_min);
64518c2ecf20Sopenharmony_ci	} else {
64528c2ecf20Sopenharmony_ci		/* Values corresponding to the endpoint. */
64538c2ecf20Sopenharmony_ci		struct sctp_sock *sp = sctp_sk(sk);
64548c2ecf20Sopenharmony_ci
64558c2ecf20Sopenharmony_ci		rtoinfo.srto_initial = sp->rtoinfo.srto_initial;
64568c2ecf20Sopenharmony_ci		rtoinfo.srto_max = sp->rtoinfo.srto_max;
64578c2ecf20Sopenharmony_ci		rtoinfo.srto_min = sp->rtoinfo.srto_min;
64588c2ecf20Sopenharmony_ci	}
64598c2ecf20Sopenharmony_ci
64608c2ecf20Sopenharmony_ci	if (put_user(len, optlen))
64618c2ecf20Sopenharmony_ci		return -EFAULT;
64628c2ecf20Sopenharmony_ci
64638c2ecf20Sopenharmony_ci	if (copy_to_user(optval, &rtoinfo, len))
64648c2ecf20Sopenharmony_ci		return -EFAULT;
64658c2ecf20Sopenharmony_ci
64668c2ecf20Sopenharmony_ci	return 0;
64678c2ecf20Sopenharmony_ci}
64688c2ecf20Sopenharmony_ci
64698c2ecf20Sopenharmony_ci/*
64708c2ecf20Sopenharmony_ci *
64718c2ecf20Sopenharmony_ci * 7.1.2 SCTP_ASSOCINFO
64728c2ecf20Sopenharmony_ci *
64738c2ecf20Sopenharmony_ci * This option is used to tune the maximum retransmission attempts
64748c2ecf20Sopenharmony_ci * of the association.
64758c2ecf20Sopenharmony_ci * Returns an error if the new association retransmission value is
64768c2ecf20Sopenharmony_ci * greater than the sum of the retransmission value  of the peer.
64778c2ecf20Sopenharmony_ci * See [SCTP] for more information.
64788c2ecf20Sopenharmony_ci *
64798c2ecf20Sopenharmony_ci */
64808c2ecf20Sopenharmony_cistatic int sctp_getsockopt_associnfo(struct sock *sk, int len,
64818c2ecf20Sopenharmony_ci				     char __user *optval,
64828c2ecf20Sopenharmony_ci				     int __user *optlen)
64838c2ecf20Sopenharmony_ci{
64848c2ecf20Sopenharmony_ci
64858c2ecf20Sopenharmony_ci	struct sctp_assocparams assocparams;
64868c2ecf20Sopenharmony_ci	struct sctp_association *asoc;
64878c2ecf20Sopenharmony_ci	struct list_head *pos;
64888c2ecf20Sopenharmony_ci	int cnt = 0;
64898c2ecf20Sopenharmony_ci
64908c2ecf20Sopenharmony_ci	if (len < sizeof (struct sctp_assocparams))
64918c2ecf20Sopenharmony_ci		return -EINVAL;
64928c2ecf20Sopenharmony_ci
64938c2ecf20Sopenharmony_ci	len = sizeof(struct sctp_assocparams);
64948c2ecf20Sopenharmony_ci
64958c2ecf20Sopenharmony_ci	if (copy_from_user(&assocparams, optval, len))
64968c2ecf20Sopenharmony_ci		return -EFAULT;
64978c2ecf20Sopenharmony_ci
64988c2ecf20Sopenharmony_ci	asoc = sctp_id2assoc(sk, assocparams.sasoc_assoc_id);
64998c2ecf20Sopenharmony_ci
65008c2ecf20Sopenharmony_ci	if (!asoc && assocparams.sasoc_assoc_id != SCTP_FUTURE_ASSOC &&
65018c2ecf20Sopenharmony_ci	    sctp_style(sk, UDP))
65028c2ecf20Sopenharmony_ci		return -EINVAL;
65038c2ecf20Sopenharmony_ci
65048c2ecf20Sopenharmony_ci	/* Values correspoinding to the specific association */
65058c2ecf20Sopenharmony_ci	if (asoc) {
65068c2ecf20Sopenharmony_ci		assocparams.sasoc_asocmaxrxt = asoc->max_retrans;
65078c2ecf20Sopenharmony_ci		assocparams.sasoc_peer_rwnd = asoc->peer.rwnd;
65088c2ecf20Sopenharmony_ci		assocparams.sasoc_local_rwnd = asoc->a_rwnd;
65098c2ecf20Sopenharmony_ci		assocparams.sasoc_cookie_life = ktime_to_ms(asoc->cookie_life);
65108c2ecf20Sopenharmony_ci
65118c2ecf20Sopenharmony_ci		list_for_each(pos, &asoc->peer.transport_addr_list) {
65128c2ecf20Sopenharmony_ci			cnt++;
65138c2ecf20Sopenharmony_ci		}
65148c2ecf20Sopenharmony_ci
65158c2ecf20Sopenharmony_ci		assocparams.sasoc_number_peer_destinations = cnt;
65168c2ecf20Sopenharmony_ci	} else {
65178c2ecf20Sopenharmony_ci		/* Values corresponding to the endpoint */
65188c2ecf20Sopenharmony_ci		struct sctp_sock *sp = sctp_sk(sk);
65198c2ecf20Sopenharmony_ci
65208c2ecf20Sopenharmony_ci		assocparams.sasoc_asocmaxrxt = sp->assocparams.sasoc_asocmaxrxt;
65218c2ecf20Sopenharmony_ci		assocparams.sasoc_peer_rwnd = sp->assocparams.sasoc_peer_rwnd;
65228c2ecf20Sopenharmony_ci		assocparams.sasoc_local_rwnd = sp->assocparams.sasoc_local_rwnd;
65238c2ecf20Sopenharmony_ci		assocparams.sasoc_cookie_life =
65248c2ecf20Sopenharmony_ci					sp->assocparams.sasoc_cookie_life;
65258c2ecf20Sopenharmony_ci		assocparams.sasoc_number_peer_destinations =
65268c2ecf20Sopenharmony_ci					sp->assocparams.
65278c2ecf20Sopenharmony_ci					sasoc_number_peer_destinations;
65288c2ecf20Sopenharmony_ci	}
65298c2ecf20Sopenharmony_ci
65308c2ecf20Sopenharmony_ci	if (put_user(len, optlen))
65318c2ecf20Sopenharmony_ci		return -EFAULT;
65328c2ecf20Sopenharmony_ci
65338c2ecf20Sopenharmony_ci	if (copy_to_user(optval, &assocparams, len))
65348c2ecf20Sopenharmony_ci		return -EFAULT;
65358c2ecf20Sopenharmony_ci
65368c2ecf20Sopenharmony_ci	return 0;
65378c2ecf20Sopenharmony_ci}
65388c2ecf20Sopenharmony_ci
65398c2ecf20Sopenharmony_ci/*
65408c2ecf20Sopenharmony_ci * 7.1.16 Set/clear IPv4 mapped addresses (SCTP_I_WANT_MAPPED_V4_ADDR)
65418c2ecf20Sopenharmony_ci *
65428c2ecf20Sopenharmony_ci * This socket option is a boolean flag which turns on or off mapped V4
65438c2ecf20Sopenharmony_ci * addresses.  If this option is turned on and the socket is type
65448c2ecf20Sopenharmony_ci * PF_INET6, then IPv4 addresses will be mapped to V6 representation.
65458c2ecf20Sopenharmony_ci * If this option is turned off, then no mapping will be done of V4
65468c2ecf20Sopenharmony_ci * addresses and a user will receive both PF_INET6 and PF_INET type
65478c2ecf20Sopenharmony_ci * addresses on the socket.
65488c2ecf20Sopenharmony_ci */
65498c2ecf20Sopenharmony_cistatic int sctp_getsockopt_mappedv4(struct sock *sk, int len,
65508c2ecf20Sopenharmony_ci				    char __user *optval, int __user *optlen)
65518c2ecf20Sopenharmony_ci{
65528c2ecf20Sopenharmony_ci	int val;
65538c2ecf20Sopenharmony_ci	struct sctp_sock *sp = sctp_sk(sk);
65548c2ecf20Sopenharmony_ci
65558c2ecf20Sopenharmony_ci	if (len < sizeof(int))
65568c2ecf20Sopenharmony_ci		return -EINVAL;
65578c2ecf20Sopenharmony_ci
65588c2ecf20Sopenharmony_ci	len = sizeof(int);
65598c2ecf20Sopenharmony_ci	val = sp->v4mapped;
65608c2ecf20Sopenharmony_ci	if (put_user(len, optlen))
65618c2ecf20Sopenharmony_ci		return -EFAULT;
65628c2ecf20Sopenharmony_ci	if (copy_to_user(optval, &val, len))
65638c2ecf20Sopenharmony_ci		return -EFAULT;
65648c2ecf20Sopenharmony_ci
65658c2ecf20Sopenharmony_ci	return 0;
65668c2ecf20Sopenharmony_ci}
65678c2ecf20Sopenharmony_ci
65688c2ecf20Sopenharmony_ci/*
65698c2ecf20Sopenharmony_ci * 7.1.29.  Set or Get the default context (SCTP_CONTEXT)
65708c2ecf20Sopenharmony_ci * (chapter and verse is quoted at sctp_setsockopt_context())
65718c2ecf20Sopenharmony_ci */
65728c2ecf20Sopenharmony_cistatic int sctp_getsockopt_context(struct sock *sk, int len,
65738c2ecf20Sopenharmony_ci				   char __user *optval, int __user *optlen)
65748c2ecf20Sopenharmony_ci{
65758c2ecf20Sopenharmony_ci	struct sctp_assoc_value params;
65768c2ecf20Sopenharmony_ci	struct sctp_association *asoc;
65778c2ecf20Sopenharmony_ci
65788c2ecf20Sopenharmony_ci	if (len < sizeof(struct sctp_assoc_value))
65798c2ecf20Sopenharmony_ci		return -EINVAL;
65808c2ecf20Sopenharmony_ci
65818c2ecf20Sopenharmony_ci	len = sizeof(struct sctp_assoc_value);
65828c2ecf20Sopenharmony_ci
65838c2ecf20Sopenharmony_ci	if (copy_from_user(&params, optval, len))
65848c2ecf20Sopenharmony_ci		return -EFAULT;
65858c2ecf20Sopenharmony_ci
65868c2ecf20Sopenharmony_ci	asoc = sctp_id2assoc(sk, params.assoc_id);
65878c2ecf20Sopenharmony_ci	if (!asoc && params.assoc_id != SCTP_FUTURE_ASSOC &&
65888c2ecf20Sopenharmony_ci	    sctp_style(sk, UDP))
65898c2ecf20Sopenharmony_ci		return -EINVAL;
65908c2ecf20Sopenharmony_ci
65918c2ecf20Sopenharmony_ci	params.assoc_value = asoc ? asoc->default_rcv_context
65928c2ecf20Sopenharmony_ci				  : sctp_sk(sk)->default_rcv_context;
65938c2ecf20Sopenharmony_ci
65948c2ecf20Sopenharmony_ci	if (put_user(len, optlen))
65958c2ecf20Sopenharmony_ci		return -EFAULT;
65968c2ecf20Sopenharmony_ci	if (copy_to_user(optval, &params, len))
65978c2ecf20Sopenharmony_ci		return -EFAULT;
65988c2ecf20Sopenharmony_ci
65998c2ecf20Sopenharmony_ci	return 0;
66008c2ecf20Sopenharmony_ci}
66018c2ecf20Sopenharmony_ci
66028c2ecf20Sopenharmony_ci/*
66038c2ecf20Sopenharmony_ci * 8.1.16.  Get or Set the Maximum Fragmentation Size (SCTP_MAXSEG)
66048c2ecf20Sopenharmony_ci * This option will get or set the maximum size to put in any outgoing
66058c2ecf20Sopenharmony_ci * SCTP DATA chunk.  If a message is larger than this size it will be
66068c2ecf20Sopenharmony_ci * fragmented by SCTP into the specified size.  Note that the underlying
66078c2ecf20Sopenharmony_ci * SCTP implementation may fragment into smaller sized chunks when the
66088c2ecf20Sopenharmony_ci * PMTU of the underlying association is smaller than the value set by
66098c2ecf20Sopenharmony_ci * the user.  The default value for this option is '0' which indicates
66108c2ecf20Sopenharmony_ci * the user is NOT limiting fragmentation and only the PMTU will effect
66118c2ecf20Sopenharmony_ci * SCTP's choice of DATA chunk size.  Note also that values set larger
66128c2ecf20Sopenharmony_ci * than the maximum size of an IP datagram will effectively let SCTP
66138c2ecf20Sopenharmony_ci * control fragmentation (i.e. the same as setting this option to 0).
66148c2ecf20Sopenharmony_ci *
66158c2ecf20Sopenharmony_ci * The following structure is used to access and modify this parameter:
66168c2ecf20Sopenharmony_ci *
66178c2ecf20Sopenharmony_ci * struct sctp_assoc_value {
66188c2ecf20Sopenharmony_ci *   sctp_assoc_t assoc_id;
66198c2ecf20Sopenharmony_ci *   uint32_t assoc_value;
66208c2ecf20Sopenharmony_ci * };
66218c2ecf20Sopenharmony_ci *
66228c2ecf20Sopenharmony_ci * assoc_id:  This parameter is ignored for one-to-one style sockets.
66238c2ecf20Sopenharmony_ci *    For one-to-many style sockets this parameter indicates which
66248c2ecf20Sopenharmony_ci *    association the user is performing an action upon.  Note that if
66258c2ecf20Sopenharmony_ci *    this field's value is zero then the endpoints default value is
66268c2ecf20Sopenharmony_ci *    changed (effecting future associations only).
66278c2ecf20Sopenharmony_ci * assoc_value:  This parameter specifies the maximum size in bytes.
66288c2ecf20Sopenharmony_ci */
66298c2ecf20Sopenharmony_cistatic int sctp_getsockopt_maxseg(struct sock *sk, int len,
66308c2ecf20Sopenharmony_ci				  char __user *optval, int __user *optlen)
66318c2ecf20Sopenharmony_ci{
66328c2ecf20Sopenharmony_ci	struct sctp_assoc_value params;
66338c2ecf20Sopenharmony_ci	struct sctp_association *asoc;
66348c2ecf20Sopenharmony_ci
66358c2ecf20Sopenharmony_ci	if (len == sizeof(int)) {
66368c2ecf20Sopenharmony_ci		pr_warn_ratelimited(DEPRECATED
66378c2ecf20Sopenharmony_ci				    "%s (pid %d) "
66388c2ecf20Sopenharmony_ci				    "Use of int in maxseg socket option.\n"
66398c2ecf20Sopenharmony_ci				    "Use struct sctp_assoc_value instead\n",
66408c2ecf20Sopenharmony_ci				    current->comm, task_pid_nr(current));
66418c2ecf20Sopenharmony_ci		params.assoc_id = SCTP_FUTURE_ASSOC;
66428c2ecf20Sopenharmony_ci	} else if (len >= sizeof(struct sctp_assoc_value)) {
66438c2ecf20Sopenharmony_ci		len = sizeof(struct sctp_assoc_value);
66448c2ecf20Sopenharmony_ci		if (copy_from_user(&params, optval, len))
66458c2ecf20Sopenharmony_ci			return -EFAULT;
66468c2ecf20Sopenharmony_ci	} else
66478c2ecf20Sopenharmony_ci		return -EINVAL;
66488c2ecf20Sopenharmony_ci
66498c2ecf20Sopenharmony_ci	asoc = sctp_id2assoc(sk, params.assoc_id);
66508c2ecf20Sopenharmony_ci	if (!asoc && params.assoc_id != SCTP_FUTURE_ASSOC &&
66518c2ecf20Sopenharmony_ci	    sctp_style(sk, UDP))
66528c2ecf20Sopenharmony_ci		return -EINVAL;
66538c2ecf20Sopenharmony_ci
66548c2ecf20Sopenharmony_ci	if (asoc)
66558c2ecf20Sopenharmony_ci		params.assoc_value = asoc->frag_point;
66568c2ecf20Sopenharmony_ci	else
66578c2ecf20Sopenharmony_ci		params.assoc_value = sctp_sk(sk)->user_frag;
66588c2ecf20Sopenharmony_ci
66598c2ecf20Sopenharmony_ci	if (put_user(len, optlen))
66608c2ecf20Sopenharmony_ci		return -EFAULT;
66618c2ecf20Sopenharmony_ci	if (len == sizeof(int)) {
66628c2ecf20Sopenharmony_ci		if (copy_to_user(optval, &params.assoc_value, len))
66638c2ecf20Sopenharmony_ci			return -EFAULT;
66648c2ecf20Sopenharmony_ci	} else {
66658c2ecf20Sopenharmony_ci		if (copy_to_user(optval, &params, len))
66668c2ecf20Sopenharmony_ci			return -EFAULT;
66678c2ecf20Sopenharmony_ci	}
66688c2ecf20Sopenharmony_ci
66698c2ecf20Sopenharmony_ci	return 0;
66708c2ecf20Sopenharmony_ci}
66718c2ecf20Sopenharmony_ci
66728c2ecf20Sopenharmony_ci/*
66738c2ecf20Sopenharmony_ci * 7.1.24.  Get or set fragmented interleave (SCTP_FRAGMENT_INTERLEAVE)
66748c2ecf20Sopenharmony_ci * (chapter and verse is quoted at sctp_setsockopt_fragment_interleave())
66758c2ecf20Sopenharmony_ci */
66768c2ecf20Sopenharmony_cistatic int sctp_getsockopt_fragment_interleave(struct sock *sk, int len,
66778c2ecf20Sopenharmony_ci					       char __user *optval, int __user *optlen)
66788c2ecf20Sopenharmony_ci{
66798c2ecf20Sopenharmony_ci	int val;
66808c2ecf20Sopenharmony_ci
66818c2ecf20Sopenharmony_ci	if (len < sizeof(int))
66828c2ecf20Sopenharmony_ci		return -EINVAL;
66838c2ecf20Sopenharmony_ci
66848c2ecf20Sopenharmony_ci	len = sizeof(int);
66858c2ecf20Sopenharmony_ci
66868c2ecf20Sopenharmony_ci	val = sctp_sk(sk)->frag_interleave;
66878c2ecf20Sopenharmony_ci	if (put_user(len, optlen))
66888c2ecf20Sopenharmony_ci		return -EFAULT;
66898c2ecf20Sopenharmony_ci	if (copy_to_user(optval, &val, len))
66908c2ecf20Sopenharmony_ci		return -EFAULT;
66918c2ecf20Sopenharmony_ci
66928c2ecf20Sopenharmony_ci	return 0;
66938c2ecf20Sopenharmony_ci}
66948c2ecf20Sopenharmony_ci
66958c2ecf20Sopenharmony_ci/*
66968c2ecf20Sopenharmony_ci * 7.1.25.  Set or Get the sctp partial delivery point
66978c2ecf20Sopenharmony_ci * (chapter and verse is quoted at sctp_setsockopt_partial_delivery_point())
66988c2ecf20Sopenharmony_ci */
66998c2ecf20Sopenharmony_cistatic int sctp_getsockopt_partial_delivery_point(struct sock *sk, int len,
67008c2ecf20Sopenharmony_ci						  char __user *optval,
67018c2ecf20Sopenharmony_ci						  int __user *optlen)
67028c2ecf20Sopenharmony_ci{
67038c2ecf20Sopenharmony_ci	u32 val;
67048c2ecf20Sopenharmony_ci
67058c2ecf20Sopenharmony_ci	if (len < sizeof(u32))
67068c2ecf20Sopenharmony_ci		return -EINVAL;
67078c2ecf20Sopenharmony_ci
67088c2ecf20Sopenharmony_ci	len = sizeof(u32);
67098c2ecf20Sopenharmony_ci
67108c2ecf20Sopenharmony_ci	val = sctp_sk(sk)->pd_point;
67118c2ecf20Sopenharmony_ci	if (put_user(len, optlen))
67128c2ecf20Sopenharmony_ci		return -EFAULT;
67138c2ecf20Sopenharmony_ci	if (copy_to_user(optval, &val, len))
67148c2ecf20Sopenharmony_ci		return -EFAULT;
67158c2ecf20Sopenharmony_ci
67168c2ecf20Sopenharmony_ci	return 0;
67178c2ecf20Sopenharmony_ci}
67188c2ecf20Sopenharmony_ci
67198c2ecf20Sopenharmony_ci/*
67208c2ecf20Sopenharmony_ci * 7.1.28.  Set or Get the maximum burst (SCTP_MAX_BURST)
67218c2ecf20Sopenharmony_ci * (chapter and verse is quoted at sctp_setsockopt_maxburst())
67228c2ecf20Sopenharmony_ci */
67238c2ecf20Sopenharmony_cistatic int sctp_getsockopt_maxburst(struct sock *sk, int len,
67248c2ecf20Sopenharmony_ci				    char __user *optval,
67258c2ecf20Sopenharmony_ci				    int __user *optlen)
67268c2ecf20Sopenharmony_ci{
67278c2ecf20Sopenharmony_ci	struct sctp_assoc_value params;
67288c2ecf20Sopenharmony_ci	struct sctp_association *asoc;
67298c2ecf20Sopenharmony_ci
67308c2ecf20Sopenharmony_ci	if (len == sizeof(int)) {
67318c2ecf20Sopenharmony_ci		pr_warn_ratelimited(DEPRECATED
67328c2ecf20Sopenharmony_ci				    "%s (pid %d) "
67338c2ecf20Sopenharmony_ci				    "Use of int in max_burst socket option.\n"
67348c2ecf20Sopenharmony_ci				    "Use struct sctp_assoc_value instead\n",
67358c2ecf20Sopenharmony_ci				    current->comm, task_pid_nr(current));
67368c2ecf20Sopenharmony_ci		params.assoc_id = SCTP_FUTURE_ASSOC;
67378c2ecf20Sopenharmony_ci	} else if (len >= sizeof(struct sctp_assoc_value)) {
67388c2ecf20Sopenharmony_ci		len = sizeof(struct sctp_assoc_value);
67398c2ecf20Sopenharmony_ci		if (copy_from_user(&params, optval, len))
67408c2ecf20Sopenharmony_ci			return -EFAULT;
67418c2ecf20Sopenharmony_ci	} else
67428c2ecf20Sopenharmony_ci		return -EINVAL;
67438c2ecf20Sopenharmony_ci
67448c2ecf20Sopenharmony_ci	asoc = sctp_id2assoc(sk, params.assoc_id);
67458c2ecf20Sopenharmony_ci	if (!asoc && params.assoc_id != SCTP_FUTURE_ASSOC &&
67468c2ecf20Sopenharmony_ci	    sctp_style(sk, UDP))
67478c2ecf20Sopenharmony_ci		return -EINVAL;
67488c2ecf20Sopenharmony_ci
67498c2ecf20Sopenharmony_ci	params.assoc_value = asoc ? asoc->max_burst : sctp_sk(sk)->max_burst;
67508c2ecf20Sopenharmony_ci
67518c2ecf20Sopenharmony_ci	if (len == sizeof(int)) {
67528c2ecf20Sopenharmony_ci		if (copy_to_user(optval, &params.assoc_value, len))
67538c2ecf20Sopenharmony_ci			return -EFAULT;
67548c2ecf20Sopenharmony_ci	} else {
67558c2ecf20Sopenharmony_ci		if (copy_to_user(optval, &params, len))
67568c2ecf20Sopenharmony_ci			return -EFAULT;
67578c2ecf20Sopenharmony_ci	}
67588c2ecf20Sopenharmony_ci
67598c2ecf20Sopenharmony_ci	return 0;
67608c2ecf20Sopenharmony_ci
67618c2ecf20Sopenharmony_ci}
67628c2ecf20Sopenharmony_ci
67638c2ecf20Sopenharmony_cistatic int sctp_getsockopt_hmac_ident(struct sock *sk, int len,
67648c2ecf20Sopenharmony_ci				    char __user *optval, int __user *optlen)
67658c2ecf20Sopenharmony_ci{
67668c2ecf20Sopenharmony_ci	struct sctp_endpoint *ep = sctp_sk(sk)->ep;
67678c2ecf20Sopenharmony_ci	struct sctp_hmacalgo  __user *p = (void __user *)optval;
67688c2ecf20Sopenharmony_ci	struct sctp_hmac_algo_param *hmacs;
67698c2ecf20Sopenharmony_ci	__u16 data_len = 0;
67708c2ecf20Sopenharmony_ci	u32 num_idents;
67718c2ecf20Sopenharmony_ci	int i;
67728c2ecf20Sopenharmony_ci
67738c2ecf20Sopenharmony_ci	if (!ep->auth_enable)
67748c2ecf20Sopenharmony_ci		return -EACCES;
67758c2ecf20Sopenharmony_ci
67768c2ecf20Sopenharmony_ci	hmacs = ep->auth_hmacs_list;
67778c2ecf20Sopenharmony_ci	data_len = ntohs(hmacs->param_hdr.length) -
67788c2ecf20Sopenharmony_ci		   sizeof(struct sctp_paramhdr);
67798c2ecf20Sopenharmony_ci
67808c2ecf20Sopenharmony_ci	if (len < sizeof(struct sctp_hmacalgo) + data_len)
67818c2ecf20Sopenharmony_ci		return -EINVAL;
67828c2ecf20Sopenharmony_ci
67838c2ecf20Sopenharmony_ci	len = sizeof(struct sctp_hmacalgo) + data_len;
67848c2ecf20Sopenharmony_ci	num_idents = data_len / sizeof(u16);
67858c2ecf20Sopenharmony_ci
67868c2ecf20Sopenharmony_ci	if (put_user(len, optlen))
67878c2ecf20Sopenharmony_ci		return -EFAULT;
67888c2ecf20Sopenharmony_ci	if (put_user(num_idents, &p->shmac_num_idents))
67898c2ecf20Sopenharmony_ci		return -EFAULT;
67908c2ecf20Sopenharmony_ci	for (i = 0; i < num_idents; i++) {
67918c2ecf20Sopenharmony_ci		__u16 hmacid = ntohs(hmacs->hmac_ids[i]);
67928c2ecf20Sopenharmony_ci
67938c2ecf20Sopenharmony_ci		if (copy_to_user(&p->shmac_idents[i], &hmacid, sizeof(__u16)))
67948c2ecf20Sopenharmony_ci			return -EFAULT;
67958c2ecf20Sopenharmony_ci	}
67968c2ecf20Sopenharmony_ci	return 0;
67978c2ecf20Sopenharmony_ci}
67988c2ecf20Sopenharmony_ci
67998c2ecf20Sopenharmony_cistatic int sctp_getsockopt_active_key(struct sock *sk, int len,
68008c2ecf20Sopenharmony_ci				    char __user *optval, int __user *optlen)
68018c2ecf20Sopenharmony_ci{
68028c2ecf20Sopenharmony_ci	struct sctp_endpoint *ep = sctp_sk(sk)->ep;
68038c2ecf20Sopenharmony_ci	struct sctp_authkeyid val;
68048c2ecf20Sopenharmony_ci	struct sctp_association *asoc;
68058c2ecf20Sopenharmony_ci
68068c2ecf20Sopenharmony_ci	if (len < sizeof(struct sctp_authkeyid))
68078c2ecf20Sopenharmony_ci		return -EINVAL;
68088c2ecf20Sopenharmony_ci
68098c2ecf20Sopenharmony_ci	len = sizeof(struct sctp_authkeyid);
68108c2ecf20Sopenharmony_ci	if (copy_from_user(&val, optval, len))
68118c2ecf20Sopenharmony_ci		return -EFAULT;
68128c2ecf20Sopenharmony_ci
68138c2ecf20Sopenharmony_ci	asoc = sctp_id2assoc(sk, val.scact_assoc_id);
68148c2ecf20Sopenharmony_ci	if (!asoc && val.scact_assoc_id && sctp_style(sk, UDP))
68158c2ecf20Sopenharmony_ci		return -EINVAL;
68168c2ecf20Sopenharmony_ci
68178c2ecf20Sopenharmony_ci	if (asoc) {
68188c2ecf20Sopenharmony_ci		if (!asoc->peer.auth_capable)
68198c2ecf20Sopenharmony_ci			return -EACCES;
68208c2ecf20Sopenharmony_ci		val.scact_keynumber = asoc->active_key_id;
68218c2ecf20Sopenharmony_ci	} else {
68228c2ecf20Sopenharmony_ci		if (!ep->auth_enable)
68238c2ecf20Sopenharmony_ci			return -EACCES;
68248c2ecf20Sopenharmony_ci		val.scact_keynumber = ep->active_key_id;
68258c2ecf20Sopenharmony_ci	}
68268c2ecf20Sopenharmony_ci
68278c2ecf20Sopenharmony_ci	if (put_user(len, optlen))
68288c2ecf20Sopenharmony_ci		return -EFAULT;
68298c2ecf20Sopenharmony_ci	if (copy_to_user(optval, &val, len))
68308c2ecf20Sopenharmony_ci		return -EFAULT;
68318c2ecf20Sopenharmony_ci
68328c2ecf20Sopenharmony_ci	return 0;
68338c2ecf20Sopenharmony_ci}
68348c2ecf20Sopenharmony_ci
68358c2ecf20Sopenharmony_cistatic int sctp_getsockopt_peer_auth_chunks(struct sock *sk, int len,
68368c2ecf20Sopenharmony_ci				    char __user *optval, int __user *optlen)
68378c2ecf20Sopenharmony_ci{
68388c2ecf20Sopenharmony_ci	struct sctp_authchunks __user *p = (void __user *)optval;
68398c2ecf20Sopenharmony_ci	struct sctp_authchunks val;
68408c2ecf20Sopenharmony_ci	struct sctp_association *asoc;
68418c2ecf20Sopenharmony_ci	struct sctp_chunks_param *ch;
68428c2ecf20Sopenharmony_ci	u32    num_chunks = 0;
68438c2ecf20Sopenharmony_ci	char __user *to;
68448c2ecf20Sopenharmony_ci
68458c2ecf20Sopenharmony_ci	if (len < sizeof(struct sctp_authchunks))
68468c2ecf20Sopenharmony_ci		return -EINVAL;
68478c2ecf20Sopenharmony_ci
68488c2ecf20Sopenharmony_ci	if (copy_from_user(&val, optval, sizeof(val)))
68498c2ecf20Sopenharmony_ci		return -EFAULT;
68508c2ecf20Sopenharmony_ci
68518c2ecf20Sopenharmony_ci	to = p->gauth_chunks;
68528c2ecf20Sopenharmony_ci	asoc = sctp_id2assoc(sk, val.gauth_assoc_id);
68538c2ecf20Sopenharmony_ci	if (!asoc)
68548c2ecf20Sopenharmony_ci		return -EINVAL;
68558c2ecf20Sopenharmony_ci
68568c2ecf20Sopenharmony_ci	if (!asoc->peer.auth_capable)
68578c2ecf20Sopenharmony_ci		return -EACCES;
68588c2ecf20Sopenharmony_ci
68598c2ecf20Sopenharmony_ci	ch = asoc->peer.peer_chunks;
68608c2ecf20Sopenharmony_ci	if (!ch)
68618c2ecf20Sopenharmony_ci		goto num;
68628c2ecf20Sopenharmony_ci
68638c2ecf20Sopenharmony_ci	/* See if the user provided enough room for all the data */
68648c2ecf20Sopenharmony_ci	num_chunks = ntohs(ch->param_hdr.length) - sizeof(struct sctp_paramhdr);
68658c2ecf20Sopenharmony_ci	if (len < num_chunks)
68668c2ecf20Sopenharmony_ci		return -EINVAL;
68678c2ecf20Sopenharmony_ci
68688c2ecf20Sopenharmony_ci	if (copy_to_user(to, ch->chunks, num_chunks))
68698c2ecf20Sopenharmony_ci		return -EFAULT;
68708c2ecf20Sopenharmony_cinum:
68718c2ecf20Sopenharmony_ci	len = sizeof(struct sctp_authchunks) + num_chunks;
68728c2ecf20Sopenharmony_ci	if (put_user(len, optlen))
68738c2ecf20Sopenharmony_ci		return -EFAULT;
68748c2ecf20Sopenharmony_ci	if (put_user(num_chunks, &p->gauth_number_of_chunks))
68758c2ecf20Sopenharmony_ci		return -EFAULT;
68768c2ecf20Sopenharmony_ci	return 0;
68778c2ecf20Sopenharmony_ci}
68788c2ecf20Sopenharmony_ci
68798c2ecf20Sopenharmony_cistatic int sctp_getsockopt_local_auth_chunks(struct sock *sk, int len,
68808c2ecf20Sopenharmony_ci				    char __user *optval, int __user *optlen)
68818c2ecf20Sopenharmony_ci{
68828c2ecf20Sopenharmony_ci	struct sctp_endpoint *ep = sctp_sk(sk)->ep;
68838c2ecf20Sopenharmony_ci	struct sctp_authchunks __user *p = (void __user *)optval;
68848c2ecf20Sopenharmony_ci	struct sctp_authchunks val;
68858c2ecf20Sopenharmony_ci	struct sctp_association *asoc;
68868c2ecf20Sopenharmony_ci	struct sctp_chunks_param *ch;
68878c2ecf20Sopenharmony_ci	u32    num_chunks = 0;
68888c2ecf20Sopenharmony_ci	char __user *to;
68898c2ecf20Sopenharmony_ci
68908c2ecf20Sopenharmony_ci	if (len < sizeof(struct sctp_authchunks))
68918c2ecf20Sopenharmony_ci		return -EINVAL;
68928c2ecf20Sopenharmony_ci
68938c2ecf20Sopenharmony_ci	if (copy_from_user(&val, optval, sizeof(val)))
68948c2ecf20Sopenharmony_ci		return -EFAULT;
68958c2ecf20Sopenharmony_ci
68968c2ecf20Sopenharmony_ci	to = p->gauth_chunks;
68978c2ecf20Sopenharmony_ci	asoc = sctp_id2assoc(sk, val.gauth_assoc_id);
68988c2ecf20Sopenharmony_ci	if (!asoc && val.gauth_assoc_id != SCTP_FUTURE_ASSOC &&
68998c2ecf20Sopenharmony_ci	    sctp_style(sk, UDP))
69008c2ecf20Sopenharmony_ci		return -EINVAL;
69018c2ecf20Sopenharmony_ci
69028c2ecf20Sopenharmony_ci	if (asoc) {
69038c2ecf20Sopenharmony_ci		if (!asoc->peer.auth_capable)
69048c2ecf20Sopenharmony_ci			return -EACCES;
69058c2ecf20Sopenharmony_ci		ch = (struct sctp_chunks_param *)asoc->c.auth_chunks;
69068c2ecf20Sopenharmony_ci	} else {
69078c2ecf20Sopenharmony_ci		if (!ep->auth_enable)
69088c2ecf20Sopenharmony_ci			return -EACCES;
69098c2ecf20Sopenharmony_ci		ch = ep->auth_chunk_list;
69108c2ecf20Sopenharmony_ci	}
69118c2ecf20Sopenharmony_ci	if (!ch)
69128c2ecf20Sopenharmony_ci		goto num;
69138c2ecf20Sopenharmony_ci
69148c2ecf20Sopenharmony_ci	num_chunks = ntohs(ch->param_hdr.length) - sizeof(struct sctp_paramhdr);
69158c2ecf20Sopenharmony_ci	if (len < sizeof(struct sctp_authchunks) + num_chunks)
69168c2ecf20Sopenharmony_ci		return -EINVAL;
69178c2ecf20Sopenharmony_ci
69188c2ecf20Sopenharmony_ci	if (copy_to_user(to, ch->chunks, num_chunks))
69198c2ecf20Sopenharmony_ci		return -EFAULT;
69208c2ecf20Sopenharmony_cinum:
69218c2ecf20Sopenharmony_ci	len = sizeof(struct sctp_authchunks) + num_chunks;
69228c2ecf20Sopenharmony_ci	if (put_user(len, optlen))
69238c2ecf20Sopenharmony_ci		return -EFAULT;
69248c2ecf20Sopenharmony_ci	if (put_user(num_chunks, &p->gauth_number_of_chunks))
69258c2ecf20Sopenharmony_ci		return -EFAULT;
69268c2ecf20Sopenharmony_ci
69278c2ecf20Sopenharmony_ci	return 0;
69288c2ecf20Sopenharmony_ci}
69298c2ecf20Sopenharmony_ci
69308c2ecf20Sopenharmony_ci/*
69318c2ecf20Sopenharmony_ci * 8.2.5.  Get the Current Number of Associations (SCTP_GET_ASSOC_NUMBER)
69328c2ecf20Sopenharmony_ci * This option gets the current number of associations that are attached
69338c2ecf20Sopenharmony_ci * to a one-to-many style socket.  The option value is an uint32_t.
69348c2ecf20Sopenharmony_ci */
69358c2ecf20Sopenharmony_cistatic int sctp_getsockopt_assoc_number(struct sock *sk, int len,
69368c2ecf20Sopenharmony_ci				    char __user *optval, int __user *optlen)
69378c2ecf20Sopenharmony_ci{
69388c2ecf20Sopenharmony_ci	struct sctp_sock *sp = sctp_sk(sk);
69398c2ecf20Sopenharmony_ci	struct sctp_association *asoc;
69408c2ecf20Sopenharmony_ci	u32 val = 0;
69418c2ecf20Sopenharmony_ci
69428c2ecf20Sopenharmony_ci	if (sctp_style(sk, TCP))
69438c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
69448c2ecf20Sopenharmony_ci
69458c2ecf20Sopenharmony_ci	if (len < sizeof(u32))
69468c2ecf20Sopenharmony_ci		return -EINVAL;
69478c2ecf20Sopenharmony_ci
69488c2ecf20Sopenharmony_ci	len = sizeof(u32);
69498c2ecf20Sopenharmony_ci
69508c2ecf20Sopenharmony_ci	list_for_each_entry(asoc, &(sp->ep->asocs), asocs) {
69518c2ecf20Sopenharmony_ci		val++;
69528c2ecf20Sopenharmony_ci	}
69538c2ecf20Sopenharmony_ci
69548c2ecf20Sopenharmony_ci	if (put_user(len, optlen))
69558c2ecf20Sopenharmony_ci		return -EFAULT;
69568c2ecf20Sopenharmony_ci	if (copy_to_user(optval, &val, len))
69578c2ecf20Sopenharmony_ci		return -EFAULT;
69588c2ecf20Sopenharmony_ci
69598c2ecf20Sopenharmony_ci	return 0;
69608c2ecf20Sopenharmony_ci}
69618c2ecf20Sopenharmony_ci
69628c2ecf20Sopenharmony_ci/*
69638c2ecf20Sopenharmony_ci * 8.1.23 SCTP_AUTO_ASCONF
69648c2ecf20Sopenharmony_ci * See the corresponding setsockopt entry as description
69658c2ecf20Sopenharmony_ci */
69668c2ecf20Sopenharmony_cistatic int sctp_getsockopt_auto_asconf(struct sock *sk, int len,
69678c2ecf20Sopenharmony_ci				   char __user *optval, int __user *optlen)
69688c2ecf20Sopenharmony_ci{
69698c2ecf20Sopenharmony_ci	int val = 0;
69708c2ecf20Sopenharmony_ci
69718c2ecf20Sopenharmony_ci	if (len < sizeof(int))
69728c2ecf20Sopenharmony_ci		return -EINVAL;
69738c2ecf20Sopenharmony_ci
69748c2ecf20Sopenharmony_ci	len = sizeof(int);
69758c2ecf20Sopenharmony_ci	if (sctp_sk(sk)->do_auto_asconf && sctp_is_ep_boundall(sk))
69768c2ecf20Sopenharmony_ci		val = 1;
69778c2ecf20Sopenharmony_ci	if (put_user(len, optlen))
69788c2ecf20Sopenharmony_ci		return -EFAULT;
69798c2ecf20Sopenharmony_ci	if (copy_to_user(optval, &val, len))
69808c2ecf20Sopenharmony_ci		return -EFAULT;
69818c2ecf20Sopenharmony_ci	return 0;
69828c2ecf20Sopenharmony_ci}
69838c2ecf20Sopenharmony_ci
69848c2ecf20Sopenharmony_ci/*
69858c2ecf20Sopenharmony_ci * 8.2.6. Get the Current Identifiers of Associations
69868c2ecf20Sopenharmony_ci *        (SCTP_GET_ASSOC_ID_LIST)
69878c2ecf20Sopenharmony_ci *
69888c2ecf20Sopenharmony_ci * This option gets the current list of SCTP association identifiers of
69898c2ecf20Sopenharmony_ci * the SCTP associations handled by a one-to-many style socket.
69908c2ecf20Sopenharmony_ci */
69918c2ecf20Sopenharmony_cistatic int sctp_getsockopt_assoc_ids(struct sock *sk, int len,
69928c2ecf20Sopenharmony_ci				    char __user *optval, int __user *optlen)
69938c2ecf20Sopenharmony_ci{
69948c2ecf20Sopenharmony_ci	struct sctp_sock *sp = sctp_sk(sk);
69958c2ecf20Sopenharmony_ci	struct sctp_association *asoc;
69968c2ecf20Sopenharmony_ci	struct sctp_assoc_ids *ids;
69978c2ecf20Sopenharmony_ci	u32 num = 0;
69988c2ecf20Sopenharmony_ci
69998c2ecf20Sopenharmony_ci	if (sctp_style(sk, TCP))
70008c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
70018c2ecf20Sopenharmony_ci
70028c2ecf20Sopenharmony_ci	if (len < sizeof(struct sctp_assoc_ids))
70038c2ecf20Sopenharmony_ci		return -EINVAL;
70048c2ecf20Sopenharmony_ci
70058c2ecf20Sopenharmony_ci	list_for_each_entry(asoc, &(sp->ep->asocs), asocs) {
70068c2ecf20Sopenharmony_ci		num++;
70078c2ecf20Sopenharmony_ci	}
70088c2ecf20Sopenharmony_ci
70098c2ecf20Sopenharmony_ci	if (len < sizeof(struct sctp_assoc_ids) + sizeof(sctp_assoc_t) * num)
70108c2ecf20Sopenharmony_ci		return -EINVAL;
70118c2ecf20Sopenharmony_ci
70128c2ecf20Sopenharmony_ci	len = sizeof(struct sctp_assoc_ids) + sizeof(sctp_assoc_t) * num;
70138c2ecf20Sopenharmony_ci
70148c2ecf20Sopenharmony_ci	ids = kmalloc(len, GFP_USER | __GFP_NOWARN);
70158c2ecf20Sopenharmony_ci	if (unlikely(!ids))
70168c2ecf20Sopenharmony_ci		return -ENOMEM;
70178c2ecf20Sopenharmony_ci
70188c2ecf20Sopenharmony_ci	ids->gaids_number_of_ids = num;
70198c2ecf20Sopenharmony_ci	num = 0;
70208c2ecf20Sopenharmony_ci	list_for_each_entry(asoc, &(sp->ep->asocs), asocs) {
70218c2ecf20Sopenharmony_ci		ids->gaids_assoc_id[num++] = asoc->assoc_id;
70228c2ecf20Sopenharmony_ci	}
70238c2ecf20Sopenharmony_ci
70248c2ecf20Sopenharmony_ci	if (put_user(len, optlen) || copy_to_user(optval, ids, len)) {
70258c2ecf20Sopenharmony_ci		kfree(ids);
70268c2ecf20Sopenharmony_ci		return -EFAULT;
70278c2ecf20Sopenharmony_ci	}
70288c2ecf20Sopenharmony_ci
70298c2ecf20Sopenharmony_ci	kfree(ids);
70308c2ecf20Sopenharmony_ci	return 0;
70318c2ecf20Sopenharmony_ci}
70328c2ecf20Sopenharmony_ci
70338c2ecf20Sopenharmony_ci/*
70348c2ecf20Sopenharmony_ci * SCTP_PEER_ADDR_THLDS
70358c2ecf20Sopenharmony_ci *
70368c2ecf20Sopenharmony_ci * This option allows us to fetch the partially failed threshold for one or all
70378c2ecf20Sopenharmony_ci * transports in an association.  See Section 6.1 of:
70388c2ecf20Sopenharmony_ci * http://www.ietf.org/id/draft-nishida-tsvwg-sctp-failover-05.txt
70398c2ecf20Sopenharmony_ci */
70408c2ecf20Sopenharmony_cistatic int sctp_getsockopt_paddr_thresholds(struct sock *sk,
70418c2ecf20Sopenharmony_ci					    char __user *optval, int len,
70428c2ecf20Sopenharmony_ci					    int __user *optlen, bool v2)
70438c2ecf20Sopenharmony_ci{
70448c2ecf20Sopenharmony_ci	struct sctp_paddrthlds_v2 val;
70458c2ecf20Sopenharmony_ci	struct sctp_transport *trans;
70468c2ecf20Sopenharmony_ci	struct sctp_association *asoc;
70478c2ecf20Sopenharmony_ci	int min;
70488c2ecf20Sopenharmony_ci
70498c2ecf20Sopenharmony_ci	min = v2 ? sizeof(val) : sizeof(struct sctp_paddrthlds);
70508c2ecf20Sopenharmony_ci	if (len < min)
70518c2ecf20Sopenharmony_ci		return -EINVAL;
70528c2ecf20Sopenharmony_ci	len = min;
70538c2ecf20Sopenharmony_ci	if (copy_from_user(&val, optval, len))
70548c2ecf20Sopenharmony_ci		return -EFAULT;
70558c2ecf20Sopenharmony_ci
70568c2ecf20Sopenharmony_ci	if (!sctp_is_any(sk, (const union sctp_addr *)&val.spt_address)) {
70578c2ecf20Sopenharmony_ci		trans = sctp_addr_id2transport(sk, &val.spt_address,
70588c2ecf20Sopenharmony_ci					       val.spt_assoc_id);
70598c2ecf20Sopenharmony_ci		if (!trans)
70608c2ecf20Sopenharmony_ci			return -ENOENT;
70618c2ecf20Sopenharmony_ci
70628c2ecf20Sopenharmony_ci		val.spt_pathmaxrxt = trans->pathmaxrxt;
70638c2ecf20Sopenharmony_ci		val.spt_pathpfthld = trans->pf_retrans;
70648c2ecf20Sopenharmony_ci		val.spt_pathcpthld = trans->ps_retrans;
70658c2ecf20Sopenharmony_ci
70668c2ecf20Sopenharmony_ci		goto out;
70678c2ecf20Sopenharmony_ci	}
70688c2ecf20Sopenharmony_ci
70698c2ecf20Sopenharmony_ci	asoc = sctp_id2assoc(sk, val.spt_assoc_id);
70708c2ecf20Sopenharmony_ci	if (!asoc && val.spt_assoc_id != SCTP_FUTURE_ASSOC &&
70718c2ecf20Sopenharmony_ci	    sctp_style(sk, UDP))
70728c2ecf20Sopenharmony_ci		return -EINVAL;
70738c2ecf20Sopenharmony_ci
70748c2ecf20Sopenharmony_ci	if (asoc) {
70758c2ecf20Sopenharmony_ci		val.spt_pathpfthld = asoc->pf_retrans;
70768c2ecf20Sopenharmony_ci		val.spt_pathmaxrxt = asoc->pathmaxrxt;
70778c2ecf20Sopenharmony_ci		val.spt_pathcpthld = asoc->ps_retrans;
70788c2ecf20Sopenharmony_ci	} else {
70798c2ecf20Sopenharmony_ci		struct sctp_sock *sp = sctp_sk(sk);
70808c2ecf20Sopenharmony_ci
70818c2ecf20Sopenharmony_ci		val.spt_pathpfthld = sp->pf_retrans;
70828c2ecf20Sopenharmony_ci		val.spt_pathmaxrxt = sp->pathmaxrxt;
70838c2ecf20Sopenharmony_ci		val.spt_pathcpthld = sp->ps_retrans;
70848c2ecf20Sopenharmony_ci	}
70858c2ecf20Sopenharmony_ci
70868c2ecf20Sopenharmony_ciout:
70878c2ecf20Sopenharmony_ci	if (put_user(len, optlen) || copy_to_user(optval, &val, len))
70888c2ecf20Sopenharmony_ci		return -EFAULT;
70898c2ecf20Sopenharmony_ci
70908c2ecf20Sopenharmony_ci	return 0;
70918c2ecf20Sopenharmony_ci}
70928c2ecf20Sopenharmony_ci
70938c2ecf20Sopenharmony_ci/*
70948c2ecf20Sopenharmony_ci * SCTP_GET_ASSOC_STATS
70958c2ecf20Sopenharmony_ci *
70968c2ecf20Sopenharmony_ci * This option retrieves local per endpoint statistics. It is modeled
70978c2ecf20Sopenharmony_ci * after OpenSolaris' implementation
70988c2ecf20Sopenharmony_ci */
70998c2ecf20Sopenharmony_cistatic int sctp_getsockopt_assoc_stats(struct sock *sk, int len,
71008c2ecf20Sopenharmony_ci				       char __user *optval,
71018c2ecf20Sopenharmony_ci				       int __user *optlen)
71028c2ecf20Sopenharmony_ci{
71038c2ecf20Sopenharmony_ci	struct sctp_assoc_stats sas;
71048c2ecf20Sopenharmony_ci	struct sctp_association *asoc = NULL;
71058c2ecf20Sopenharmony_ci
71068c2ecf20Sopenharmony_ci	/* User must provide at least the assoc id */
71078c2ecf20Sopenharmony_ci	if (len < sizeof(sctp_assoc_t))
71088c2ecf20Sopenharmony_ci		return -EINVAL;
71098c2ecf20Sopenharmony_ci
71108c2ecf20Sopenharmony_ci	/* Allow the struct to grow and fill in as much as possible */
71118c2ecf20Sopenharmony_ci	len = min_t(size_t, len, sizeof(sas));
71128c2ecf20Sopenharmony_ci
71138c2ecf20Sopenharmony_ci	if (copy_from_user(&sas, optval, len))
71148c2ecf20Sopenharmony_ci		return -EFAULT;
71158c2ecf20Sopenharmony_ci
71168c2ecf20Sopenharmony_ci	asoc = sctp_id2assoc(sk, sas.sas_assoc_id);
71178c2ecf20Sopenharmony_ci	if (!asoc)
71188c2ecf20Sopenharmony_ci		return -EINVAL;
71198c2ecf20Sopenharmony_ci
71208c2ecf20Sopenharmony_ci	sas.sas_rtxchunks = asoc->stats.rtxchunks;
71218c2ecf20Sopenharmony_ci	sas.sas_gapcnt = asoc->stats.gapcnt;
71228c2ecf20Sopenharmony_ci	sas.sas_outofseqtsns = asoc->stats.outofseqtsns;
71238c2ecf20Sopenharmony_ci	sas.sas_osacks = asoc->stats.osacks;
71248c2ecf20Sopenharmony_ci	sas.sas_isacks = asoc->stats.isacks;
71258c2ecf20Sopenharmony_ci	sas.sas_octrlchunks = asoc->stats.octrlchunks;
71268c2ecf20Sopenharmony_ci	sas.sas_ictrlchunks = asoc->stats.ictrlchunks;
71278c2ecf20Sopenharmony_ci	sas.sas_oodchunks = asoc->stats.oodchunks;
71288c2ecf20Sopenharmony_ci	sas.sas_iodchunks = asoc->stats.iodchunks;
71298c2ecf20Sopenharmony_ci	sas.sas_ouodchunks = asoc->stats.ouodchunks;
71308c2ecf20Sopenharmony_ci	sas.sas_iuodchunks = asoc->stats.iuodchunks;
71318c2ecf20Sopenharmony_ci	sas.sas_idupchunks = asoc->stats.idupchunks;
71328c2ecf20Sopenharmony_ci	sas.sas_opackets = asoc->stats.opackets;
71338c2ecf20Sopenharmony_ci	sas.sas_ipackets = asoc->stats.ipackets;
71348c2ecf20Sopenharmony_ci
71358c2ecf20Sopenharmony_ci	/* New high max rto observed, will return 0 if not a single
71368c2ecf20Sopenharmony_ci	 * RTO update took place. obs_rto_ipaddr will be bogus
71378c2ecf20Sopenharmony_ci	 * in such a case
71388c2ecf20Sopenharmony_ci	 */
71398c2ecf20Sopenharmony_ci	sas.sas_maxrto = asoc->stats.max_obs_rto;
71408c2ecf20Sopenharmony_ci	memcpy(&sas.sas_obs_rto_ipaddr, &asoc->stats.obs_rto_ipaddr,
71418c2ecf20Sopenharmony_ci		sizeof(struct sockaddr_storage));
71428c2ecf20Sopenharmony_ci
71438c2ecf20Sopenharmony_ci	/* Mark beginning of a new observation period */
71448c2ecf20Sopenharmony_ci	asoc->stats.max_obs_rto = asoc->rto_min;
71458c2ecf20Sopenharmony_ci
71468c2ecf20Sopenharmony_ci	if (put_user(len, optlen))
71478c2ecf20Sopenharmony_ci		return -EFAULT;
71488c2ecf20Sopenharmony_ci
71498c2ecf20Sopenharmony_ci	pr_debug("%s: len:%d, assoc_id:%d\n", __func__, len, sas.sas_assoc_id);
71508c2ecf20Sopenharmony_ci
71518c2ecf20Sopenharmony_ci	if (copy_to_user(optval, &sas, len))
71528c2ecf20Sopenharmony_ci		return -EFAULT;
71538c2ecf20Sopenharmony_ci
71548c2ecf20Sopenharmony_ci	return 0;
71558c2ecf20Sopenharmony_ci}
71568c2ecf20Sopenharmony_ci
71578c2ecf20Sopenharmony_cistatic int sctp_getsockopt_recvrcvinfo(struct sock *sk,	int len,
71588c2ecf20Sopenharmony_ci				       char __user *optval,
71598c2ecf20Sopenharmony_ci				       int __user *optlen)
71608c2ecf20Sopenharmony_ci{
71618c2ecf20Sopenharmony_ci	int val = 0;
71628c2ecf20Sopenharmony_ci
71638c2ecf20Sopenharmony_ci	if (len < sizeof(int))
71648c2ecf20Sopenharmony_ci		return -EINVAL;
71658c2ecf20Sopenharmony_ci
71668c2ecf20Sopenharmony_ci	len = sizeof(int);
71678c2ecf20Sopenharmony_ci	if (sctp_sk(sk)->recvrcvinfo)
71688c2ecf20Sopenharmony_ci		val = 1;
71698c2ecf20Sopenharmony_ci	if (put_user(len, optlen))
71708c2ecf20Sopenharmony_ci		return -EFAULT;
71718c2ecf20Sopenharmony_ci	if (copy_to_user(optval, &val, len))
71728c2ecf20Sopenharmony_ci		return -EFAULT;
71738c2ecf20Sopenharmony_ci
71748c2ecf20Sopenharmony_ci	return 0;
71758c2ecf20Sopenharmony_ci}
71768c2ecf20Sopenharmony_ci
71778c2ecf20Sopenharmony_cistatic int sctp_getsockopt_recvnxtinfo(struct sock *sk,	int len,
71788c2ecf20Sopenharmony_ci				       char __user *optval,
71798c2ecf20Sopenharmony_ci				       int __user *optlen)
71808c2ecf20Sopenharmony_ci{
71818c2ecf20Sopenharmony_ci	int val = 0;
71828c2ecf20Sopenharmony_ci
71838c2ecf20Sopenharmony_ci	if (len < sizeof(int))
71848c2ecf20Sopenharmony_ci		return -EINVAL;
71858c2ecf20Sopenharmony_ci
71868c2ecf20Sopenharmony_ci	len = sizeof(int);
71878c2ecf20Sopenharmony_ci	if (sctp_sk(sk)->recvnxtinfo)
71888c2ecf20Sopenharmony_ci		val = 1;
71898c2ecf20Sopenharmony_ci	if (put_user(len, optlen))
71908c2ecf20Sopenharmony_ci		return -EFAULT;
71918c2ecf20Sopenharmony_ci	if (copy_to_user(optval, &val, len))
71928c2ecf20Sopenharmony_ci		return -EFAULT;
71938c2ecf20Sopenharmony_ci
71948c2ecf20Sopenharmony_ci	return 0;
71958c2ecf20Sopenharmony_ci}
71968c2ecf20Sopenharmony_ci
71978c2ecf20Sopenharmony_cistatic int sctp_getsockopt_pr_supported(struct sock *sk, int len,
71988c2ecf20Sopenharmony_ci					char __user *optval,
71998c2ecf20Sopenharmony_ci					int __user *optlen)
72008c2ecf20Sopenharmony_ci{
72018c2ecf20Sopenharmony_ci	struct sctp_assoc_value params;
72028c2ecf20Sopenharmony_ci	struct sctp_association *asoc;
72038c2ecf20Sopenharmony_ci	int retval = -EFAULT;
72048c2ecf20Sopenharmony_ci
72058c2ecf20Sopenharmony_ci	if (len < sizeof(params)) {
72068c2ecf20Sopenharmony_ci		retval = -EINVAL;
72078c2ecf20Sopenharmony_ci		goto out;
72088c2ecf20Sopenharmony_ci	}
72098c2ecf20Sopenharmony_ci
72108c2ecf20Sopenharmony_ci	len = sizeof(params);
72118c2ecf20Sopenharmony_ci	if (copy_from_user(&params, optval, len))
72128c2ecf20Sopenharmony_ci		goto out;
72138c2ecf20Sopenharmony_ci
72148c2ecf20Sopenharmony_ci	asoc = sctp_id2assoc(sk, params.assoc_id);
72158c2ecf20Sopenharmony_ci	if (!asoc && params.assoc_id != SCTP_FUTURE_ASSOC &&
72168c2ecf20Sopenharmony_ci	    sctp_style(sk, UDP)) {
72178c2ecf20Sopenharmony_ci		retval = -EINVAL;
72188c2ecf20Sopenharmony_ci		goto out;
72198c2ecf20Sopenharmony_ci	}
72208c2ecf20Sopenharmony_ci
72218c2ecf20Sopenharmony_ci	params.assoc_value = asoc ? asoc->peer.prsctp_capable
72228c2ecf20Sopenharmony_ci				  : sctp_sk(sk)->ep->prsctp_enable;
72238c2ecf20Sopenharmony_ci
72248c2ecf20Sopenharmony_ci	if (put_user(len, optlen))
72258c2ecf20Sopenharmony_ci		goto out;
72268c2ecf20Sopenharmony_ci
72278c2ecf20Sopenharmony_ci	if (copy_to_user(optval, &params, len))
72288c2ecf20Sopenharmony_ci		goto out;
72298c2ecf20Sopenharmony_ci
72308c2ecf20Sopenharmony_ci	retval = 0;
72318c2ecf20Sopenharmony_ci
72328c2ecf20Sopenharmony_ciout:
72338c2ecf20Sopenharmony_ci	return retval;
72348c2ecf20Sopenharmony_ci}
72358c2ecf20Sopenharmony_ci
72368c2ecf20Sopenharmony_cistatic int sctp_getsockopt_default_prinfo(struct sock *sk, int len,
72378c2ecf20Sopenharmony_ci					  char __user *optval,
72388c2ecf20Sopenharmony_ci					  int __user *optlen)
72398c2ecf20Sopenharmony_ci{
72408c2ecf20Sopenharmony_ci	struct sctp_default_prinfo info;
72418c2ecf20Sopenharmony_ci	struct sctp_association *asoc;
72428c2ecf20Sopenharmony_ci	int retval = -EFAULT;
72438c2ecf20Sopenharmony_ci
72448c2ecf20Sopenharmony_ci	if (len < sizeof(info)) {
72458c2ecf20Sopenharmony_ci		retval = -EINVAL;
72468c2ecf20Sopenharmony_ci		goto out;
72478c2ecf20Sopenharmony_ci	}
72488c2ecf20Sopenharmony_ci
72498c2ecf20Sopenharmony_ci	len = sizeof(info);
72508c2ecf20Sopenharmony_ci	if (copy_from_user(&info, optval, len))
72518c2ecf20Sopenharmony_ci		goto out;
72528c2ecf20Sopenharmony_ci
72538c2ecf20Sopenharmony_ci	asoc = sctp_id2assoc(sk, info.pr_assoc_id);
72548c2ecf20Sopenharmony_ci	if (!asoc && info.pr_assoc_id != SCTP_FUTURE_ASSOC &&
72558c2ecf20Sopenharmony_ci	    sctp_style(sk, UDP)) {
72568c2ecf20Sopenharmony_ci		retval = -EINVAL;
72578c2ecf20Sopenharmony_ci		goto out;
72588c2ecf20Sopenharmony_ci	}
72598c2ecf20Sopenharmony_ci
72608c2ecf20Sopenharmony_ci	if (asoc) {
72618c2ecf20Sopenharmony_ci		info.pr_policy = SCTP_PR_POLICY(asoc->default_flags);
72628c2ecf20Sopenharmony_ci		info.pr_value = asoc->default_timetolive;
72638c2ecf20Sopenharmony_ci	} else {
72648c2ecf20Sopenharmony_ci		struct sctp_sock *sp = sctp_sk(sk);
72658c2ecf20Sopenharmony_ci
72668c2ecf20Sopenharmony_ci		info.pr_policy = SCTP_PR_POLICY(sp->default_flags);
72678c2ecf20Sopenharmony_ci		info.pr_value = sp->default_timetolive;
72688c2ecf20Sopenharmony_ci	}
72698c2ecf20Sopenharmony_ci
72708c2ecf20Sopenharmony_ci	if (put_user(len, optlen))
72718c2ecf20Sopenharmony_ci		goto out;
72728c2ecf20Sopenharmony_ci
72738c2ecf20Sopenharmony_ci	if (copy_to_user(optval, &info, len))
72748c2ecf20Sopenharmony_ci		goto out;
72758c2ecf20Sopenharmony_ci
72768c2ecf20Sopenharmony_ci	retval = 0;
72778c2ecf20Sopenharmony_ci
72788c2ecf20Sopenharmony_ciout:
72798c2ecf20Sopenharmony_ci	return retval;
72808c2ecf20Sopenharmony_ci}
72818c2ecf20Sopenharmony_ci
72828c2ecf20Sopenharmony_cistatic int sctp_getsockopt_pr_assocstatus(struct sock *sk, int len,
72838c2ecf20Sopenharmony_ci					  char __user *optval,
72848c2ecf20Sopenharmony_ci					  int __user *optlen)
72858c2ecf20Sopenharmony_ci{
72868c2ecf20Sopenharmony_ci	struct sctp_prstatus params;
72878c2ecf20Sopenharmony_ci	struct sctp_association *asoc;
72888c2ecf20Sopenharmony_ci	int policy;
72898c2ecf20Sopenharmony_ci	int retval = -EINVAL;
72908c2ecf20Sopenharmony_ci
72918c2ecf20Sopenharmony_ci	if (len < sizeof(params))
72928c2ecf20Sopenharmony_ci		goto out;
72938c2ecf20Sopenharmony_ci
72948c2ecf20Sopenharmony_ci	len = sizeof(params);
72958c2ecf20Sopenharmony_ci	if (copy_from_user(&params, optval, len)) {
72968c2ecf20Sopenharmony_ci		retval = -EFAULT;
72978c2ecf20Sopenharmony_ci		goto out;
72988c2ecf20Sopenharmony_ci	}
72998c2ecf20Sopenharmony_ci
73008c2ecf20Sopenharmony_ci	policy = params.sprstat_policy;
73018c2ecf20Sopenharmony_ci	if (!policy || (policy & ~(SCTP_PR_SCTP_MASK | SCTP_PR_SCTP_ALL)) ||
73028c2ecf20Sopenharmony_ci	    ((policy & SCTP_PR_SCTP_ALL) && (policy & SCTP_PR_SCTP_MASK)))
73038c2ecf20Sopenharmony_ci		goto out;
73048c2ecf20Sopenharmony_ci
73058c2ecf20Sopenharmony_ci	asoc = sctp_id2assoc(sk, params.sprstat_assoc_id);
73068c2ecf20Sopenharmony_ci	if (!asoc)
73078c2ecf20Sopenharmony_ci		goto out;
73088c2ecf20Sopenharmony_ci
73098c2ecf20Sopenharmony_ci	if (policy == SCTP_PR_SCTP_ALL) {
73108c2ecf20Sopenharmony_ci		params.sprstat_abandoned_unsent = 0;
73118c2ecf20Sopenharmony_ci		params.sprstat_abandoned_sent = 0;
73128c2ecf20Sopenharmony_ci		for (policy = 0; policy <= SCTP_PR_INDEX(MAX); policy++) {
73138c2ecf20Sopenharmony_ci			params.sprstat_abandoned_unsent +=
73148c2ecf20Sopenharmony_ci				asoc->abandoned_unsent[policy];
73158c2ecf20Sopenharmony_ci			params.sprstat_abandoned_sent +=
73168c2ecf20Sopenharmony_ci				asoc->abandoned_sent[policy];
73178c2ecf20Sopenharmony_ci		}
73188c2ecf20Sopenharmony_ci	} else {
73198c2ecf20Sopenharmony_ci		params.sprstat_abandoned_unsent =
73208c2ecf20Sopenharmony_ci			asoc->abandoned_unsent[__SCTP_PR_INDEX(policy)];
73218c2ecf20Sopenharmony_ci		params.sprstat_abandoned_sent =
73228c2ecf20Sopenharmony_ci			asoc->abandoned_sent[__SCTP_PR_INDEX(policy)];
73238c2ecf20Sopenharmony_ci	}
73248c2ecf20Sopenharmony_ci
73258c2ecf20Sopenharmony_ci	if (put_user(len, optlen)) {
73268c2ecf20Sopenharmony_ci		retval = -EFAULT;
73278c2ecf20Sopenharmony_ci		goto out;
73288c2ecf20Sopenharmony_ci	}
73298c2ecf20Sopenharmony_ci
73308c2ecf20Sopenharmony_ci	if (copy_to_user(optval, &params, len)) {
73318c2ecf20Sopenharmony_ci		retval = -EFAULT;
73328c2ecf20Sopenharmony_ci		goto out;
73338c2ecf20Sopenharmony_ci	}
73348c2ecf20Sopenharmony_ci
73358c2ecf20Sopenharmony_ci	retval = 0;
73368c2ecf20Sopenharmony_ci
73378c2ecf20Sopenharmony_ciout:
73388c2ecf20Sopenharmony_ci	return retval;
73398c2ecf20Sopenharmony_ci}
73408c2ecf20Sopenharmony_ci
73418c2ecf20Sopenharmony_cistatic int sctp_getsockopt_pr_streamstatus(struct sock *sk, int len,
73428c2ecf20Sopenharmony_ci					   char __user *optval,
73438c2ecf20Sopenharmony_ci					   int __user *optlen)
73448c2ecf20Sopenharmony_ci{
73458c2ecf20Sopenharmony_ci	struct sctp_stream_out_ext *streamoute;
73468c2ecf20Sopenharmony_ci	struct sctp_association *asoc;
73478c2ecf20Sopenharmony_ci	struct sctp_prstatus params;
73488c2ecf20Sopenharmony_ci	int retval = -EINVAL;
73498c2ecf20Sopenharmony_ci	int policy;
73508c2ecf20Sopenharmony_ci
73518c2ecf20Sopenharmony_ci	if (len < sizeof(params))
73528c2ecf20Sopenharmony_ci		goto out;
73538c2ecf20Sopenharmony_ci
73548c2ecf20Sopenharmony_ci	len = sizeof(params);
73558c2ecf20Sopenharmony_ci	if (copy_from_user(&params, optval, len)) {
73568c2ecf20Sopenharmony_ci		retval = -EFAULT;
73578c2ecf20Sopenharmony_ci		goto out;
73588c2ecf20Sopenharmony_ci	}
73598c2ecf20Sopenharmony_ci
73608c2ecf20Sopenharmony_ci	policy = params.sprstat_policy;
73618c2ecf20Sopenharmony_ci	if (!policy || (policy & ~(SCTP_PR_SCTP_MASK | SCTP_PR_SCTP_ALL)) ||
73628c2ecf20Sopenharmony_ci	    ((policy & SCTP_PR_SCTP_ALL) && (policy & SCTP_PR_SCTP_MASK)))
73638c2ecf20Sopenharmony_ci		goto out;
73648c2ecf20Sopenharmony_ci
73658c2ecf20Sopenharmony_ci	asoc = sctp_id2assoc(sk, params.sprstat_assoc_id);
73668c2ecf20Sopenharmony_ci	if (!asoc || params.sprstat_sid >= asoc->stream.outcnt)
73678c2ecf20Sopenharmony_ci		goto out;
73688c2ecf20Sopenharmony_ci
73698c2ecf20Sopenharmony_ci	streamoute = SCTP_SO(&asoc->stream, params.sprstat_sid)->ext;
73708c2ecf20Sopenharmony_ci	if (!streamoute) {
73718c2ecf20Sopenharmony_ci		/* Not allocated yet, means all stats are 0 */
73728c2ecf20Sopenharmony_ci		params.sprstat_abandoned_unsent = 0;
73738c2ecf20Sopenharmony_ci		params.sprstat_abandoned_sent = 0;
73748c2ecf20Sopenharmony_ci		retval = 0;
73758c2ecf20Sopenharmony_ci		goto out;
73768c2ecf20Sopenharmony_ci	}
73778c2ecf20Sopenharmony_ci
73788c2ecf20Sopenharmony_ci	if (policy == SCTP_PR_SCTP_ALL) {
73798c2ecf20Sopenharmony_ci		params.sprstat_abandoned_unsent = 0;
73808c2ecf20Sopenharmony_ci		params.sprstat_abandoned_sent = 0;
73818c2ecf20Sopenharmony_ci		for (policy = 0; policy <= SCTP_PR_INDEX(MAX); policy++) {
73828c2ecf20Sopenharmony_ci			params.sprstat_abandoned_unsent +=
73838c2ecf20Sopenharmony_ci				streamoute->abandoned_unsent[policy];
73848c2ecf20Sopenharmony_ci			params.sprstat_abandoned_sent +=
73858c2ecf20Sopenharmony_ci				streamoute->abandoned_sent[policy];
73868c2ecf20Sopenharmony_ci		}
73878c2ecf20Sopenharmony_ci	} else {
73888c2ecf20Sopenharmony_ci		params.sprstat_abandoned_unsent =
73898c2ecf20Sopenharmony_ci			streamoute->abandoned_unsent[__SCTP_PR_INDEX(policy)];
73908c2ecf20Sopenharmony_ci		params.sprstat_abandoned_sent =
73918c2ecf20Sopenharmony_ci			streamoute->abandoned_sent[__SCTP_PR_INDEX(policy)];
73928c2ecf20Sopenharmony_ci	}
73938c2ecf20Sopenharmony_ci
73948c2ecf20Sopenharmony_ci	if (put_user(len, optlen) || copy_to_user(optval, &params, len)) {
73958c2ecf20Sopenharmony_ci		retval = -EFAULT;
73968c2ecf20Sopenharmony_ci		goto out;
73978c2ecf20Sopenharmony_ci	}
73988c2ecf20Sopenharmony_ci
73998c2ecf20Sopenharmony_ci	retval = 0;
74008c2ecf20Sopenharmony_ci
74018c2ecf20Sopenharmony_ciout:
74028c2ecf20Sopenharmony_ci	return retval;
74038c2ecf20Sopenharmony_ci}
74048c2ecf20Sopenharmony_ci
74058c2ecf20Sopenharmony_cistatic int sctp_getsockopt_reconfig_supported(struct sock *sk, int len,
74068c2ecf20Sopenharmony_ci					      char __user *optval,
74078c2ecf20Sopenharmony_ci					      int __user *optlen)
74088c2ecf20Sopenharmony_ci{
74098c2ecf20Sopenharmony_ci	struct sctp_assoc_value params;
74108c2ecf20Sopenharmony_ci	struct sctp_association *asoc;
74118c2ecf20Sopenharmony_ci	int retval = -EFAULT;
74128c2ecf20Sopenharmony_ci
74138c2ecf20Sopenharmony_ci	if (len < sizeof(params)) {
74148c2ecf20Sopenharmony_ci		retval = -EINVAL;
74158c2ecf20Sopenharmony_ci		goto out;
74168c2ecf20Sopenharmony_ci	}
74178c2ecf20Sopenharmony_ci
74188c2ecf20Sopenharmony_ci	len = sizeof(params);
74198c2ecf20Sopenharmony_ci	if (copy_from_user(&params, optval, len))
74208c2ecf20Sopenharmony_ci		goto out;
74218c2ecf20Sopenharmony_ci
74228c2ecf20Sopenharmony_ci	asoc = sctp_id2assoc(sk, params.assoc_id);
74238c2ecf20Sopenharmony_ci	if (!asoc && params.assoc_id != SCTP_FUTURE_ASSOC &&
74248c2ecf20Sopenharmony_ci	    sctp_style(sk, UDP)) {
74258c2ecf20Sopenharmony_ci		retval = -EINVAL;
74268c2ecf20Sopenharmony_ci		goto out;
74278c2ecf20Sopenharmony_ci	}
74288c2ecf20Sopenharmony_ci
74298c2ecf20Sopenharmony_ci	params.assoc_value = asoc ? asoc->peer.reconf_capable
74308c2ecf20Sopenharmony_ci				  : sctp_sk(sk)->ep->reconf_enable;
74318c2ecf20Sopenharmony_ci
74328c2ecf20Sopenharmony_ci	if (put_user(len, optlen))
74338c2ecf20Sopenharmony_ci		goto out;
74348c2ecf20Sopenharmony_ci
74358c2ecf20Sopenharmony_ci	if (copy_to_user(optval, &params, len))
74368c2ecf20Sopenharmony_ci		goto out;
74378c2ecf20Sopenharmony_ci
74388c2ecf20Sopenharmony_ci	retval = 0;
74398c2ecf20Sopenharmony_ci
74408c2ecf20Sopenharmony_ciout:
74418c2ecf20Sopenharmony_ci	return retval;
74428c2ecf20Sopenharmony_ci}
74438c2ecf20Sopenharmony_ci
74448c2ecf20Sopenharmony_cistatic int sctp_getsockopt_enable_strreset(struct sock *sk, int len,
74458c2ecf20Sopenharmony_ci					   char __user *optval,
74468c2ecf20Sopenharmony_ci					   int __user *optlen)
74478c2ecf20Sopenharmony_ci{
74488c2ecf20Sopenharmony_ci	struct sctp_assoc_value params;
74498c2ecf20Sopenharmony_ci	struct sctp_association *asoc;
74508c2ecf20Sopenharmony_ci	int retval = -EFAULT;
74518c2ecf20Sopenharmony_ci
74528c2ecf20Sopenharmony_ci	if (len < sizeof(params)) {
74538c2ecf20Sopenharmony_ci		retval = -EINVAL;
74548c2ecf20Sopenharmony_ci		goto out;
74558c2ecf20Sopenharmony_ci	}
74568c2ecf20Sopenharmony_ci
74578c2ecf20Sopenharmony_ci	len = sizeof(params);
74588c2ecf20Sopenharmony_ci	if (copy_from_user(&params, optval, len))
74598c2ecf20Sopenharmony_ci		goto out;
74608c2ecf20Sopenharmony_ci
74618c2ecf20Sopenharmony_ci	asoc = sctp_id2assoc(sk, params.assoc_id);
74628c2ecf20Sopenharmony_ci	if (!asoc && params.assoc_id != SCTP_FUTURE_ASSOC &&
74638c2ecf20Sopenharmony_ci	    sctp_style(sk, UDP)) {
74648c2ecf20Sopenharmony_ci		retval = -EINVAL;
74658c2ecf20Sopenharmony_ci		goto out;
74668c2ecf20Sopenharmony_ci	}
74678c2ecf20Sopenharmony_ci
74688c2ecf20Sopenharmony_ci	params.assoc_value = asoc ? asoc->strreset_enable
74698c2ecf20Sopenharmony_ci				  : sctp_sk(sk)->ep->strreset_enable;
74708c2ecf20Sopenharmony_ci
74718c2ecf20Sopenharmony_ci	if (put_user(len, optlen))
74728c2ecf20Sopenharmony_ci		goto out;
74738c2ecf20Sopenharmony_ci
74748c2ecf20Sopenharmony_ci	if (copy_to_user(optval, &params, len))
74758c2ecf20Sopenharmony_ci		goto out;
74768c2ecf20Sopenharmony_ci
74778c2ecf20Sopenharmony_ci	retval = 0;
74788c2ecf20Sopenharmony_ci
74798c2ecf20Sopenharmony_ciout:
74808c2ecf20Sopenharmony_ci	return retval;
74818c2ecf20Sopenharmony_ci}
74828c2ecf20Sopenharmony_ci
74838c2ecf20Sopenharmony_cistatic int sctp_getsockopt_scheduler(struct sock *sk, int len,
74848c2ecf20Sopenharmony_ci				     char __user *optval,
74858c2ecf20Sopenharmony_ci				     int __user *optlen)
74868c2ecf20Sopenharmony_ci{
74878c2ecf20Sopenharmony_ci	struct sctp_assoc_value params;
74888c2ecf20Sopenharmony_ci	struct sctp_association *asoc;
74898c2ecf20Sopenharmony_ci	int retval = -EFAULT;
74908c2ecf20Sopenharmony_ci
74918c2ecf20Sopenharmony_ci	if (len < sizeof(params)) {
74928c2ecf20Sopenharmony_ci		retval = -EINVAL;
74938c2ecf20Sopenharmony_ci		goto out;
74948c2ecf20Sopenharmony_ci	}
74958c2ecf20Sopenharmony_ci
74968c2ecf20Sopenharmony_ci	len = sizeof(params);
74978c2ecf20Sopenharmony_ci	if (copy_from_user(&params, optval, len))
74988c2ecf20Sopenharmony_ci		goto out;
74998c2ecf20Sopenharmony_ci
75008c2ecf20Sopenharmony_ci	asoc = sctp_id2assoc(sk, params.assoc_id);
75018c2ecf20Sopenharmony_ci	if (!asoc && params.assoc_id != SCTP_FUTURE_ASSOC &&
75028c2ecf20Sopenharmony_ci	    sctp_style(sk, UDP)) {
75038c2ecf20Sopenharmony_ci		retval = -EINVAL;
75048c2ecf20Sopenharmony_ci		goto out;
75058c2ecf20Sopenharmony_ci	}
75068c2ecf20Sopenharmony_ci
75078c2ecf20Sopenharmony_ci	params.assoc_value = asoc ? sctp_sched_get_sched(asoc)
75088c2ecf20Sopenharmony_ci				  : sctp_sk(sk)->default_ss;
75098c2ecf20Sopenharmony_ci
75108c2ecf20Sopenharmony_ci	if (put_user(len, optlen))
75118c2ecf20Sopenharmony_ci		goto out;
75128c2ecf20Sopenharmony_ci
75138c2ecf20Sopenharmony_ci	if (copy_to_user(optval, &params, len))
75148c2ecf20Sopenharmony_ci		goto out;
75158c2ecf20Sopenharmony_ci
75168c2ecf20Sopenharmony_ci	retval = 0;
75178c2ecf20Sopenharmony_ci
75188c2ecf20Sopenharmony_ciout:
75198c2ecf20Sopenharmony_ci	return retval;
75208c2ecf20Sopenharmony_ci}
75218c2ecf20Sopenharmony_ci
75228c2ecf20Sopenharmony_cistatic int sctp_getsockopt_scheduler_value(struct sock *sk, int len,
75238c2ecf20Sopenharmony_ci					   char __user *optval,
75248c2ecf20Sopenharmony_ci					   int __user *optlen)
75258c2ecf20Sopenharmony_ci{
75268c2ecf20Sopenharmony_ci	struct sctp_stream_value params;
75278c2ecf20Sopenharmony_ci	struct sctp_association *asoc;
75288c2ecf20Sopenharmony_ci	int retval = -EFAULT;
75298c2ecf20Sopenharmony_ci
75308c2ecf20Sopenharmony_ci	if (len < sizeof(params)) {
75318c2ecf20Sopenharmony_ci		retval = -EINVAL;
75328c2ecf20Sopenharmony_ci		goto out;
75338c2ecf20Sopenharmony_ci	}
75348c2ecf20Sopenharmony_ci
75358c2ecf20Sopenharmony_ci	len = sizeof(params);
75368c2ecf20Sopenharmony_ci	if (copy_from_user(&params, optval, len))
75378c2ecf20Sopenharmony_ci		goto out;
75388c2ecf20Sopenharmony_ci
75398c2ecf20Sopenharmony_ci	asoc = sctp_id2assoc(sk, params.assoc_id);
75408c2ecf20Sopenharmony_ci	if (!asoc) {
75418c2ecf20Sopenharmony_ci		retval = -EINVAL;
75428c2ecf20Sopenharmony_ci		goto out;
75438c2ecf20Sopenharmony_ci	}
75448c2ecf20Sopenharmony_ci
75458c2ecf20Sopenharmony_ci	retval = sctp_sched_get_value(asoc, params.stream_id,
75468c2ecf20Sopenharmony_ci				      &params.stream_value);
75478c2ecf20Sopenharmony_ci	if (retval)
75488c2ecf20Sopenharmony_ci		goto out;
75498c2ecf20Sopenharmony_ci
75508c2ecf20Sopenharmony_ci	if (put_user(len, optlen)) {
75518c2ecf20Sopenharmony_ci		retval = -EFAULT;
75528c2ecf20Sopenharmony_ci		goto out;
75538c2ecf20Sopenharmony_ci	}
75548c2ecf20Sopenharmony_ci
75558c2ecf20Sopenharmony_ci	if (copy_to_user(optval, &params, len)) {
75568c2ecf20Sopenharmony_ci		retval = -EFAULT;
75578c2ecf20Sopenharmony_ci		goto out;
75588c2ecf20Sopenharmony_ci	}
75598c2ecf20Sopenharmony_ci
75608c2ecf20Sopenharmony_ciout:
75618c2ecf20Sopenharmony_ci	return retval;
75628c2ecf20Sopenharmony_ci}
75638c2ecf20Sopenharmony_ci
75648c2ecf20Sopenharmony_cistatic int sctp_getsockopt_interleaving_supported(struct sock *sk, int len,
75658c2ecf20Sopenharmony_ci						  char __user *optval,
75668c2ecf20Sopenharmony_ci						  int __user *optlen)
75678c2ecf20Sopenharmony_ci{
75688c2ecf20Sopenharmony_ci	struct sctp_assoc_value params;
75698c2ecf20Sopenharmony_ci	struct sctp_association *asoc;
75708c2ecf20Sopenharmony_ci	int retval = -EFAULT;
75718c2ecf20Sopenharmony_ci
75728c2ecf20Sopenharmony_ci	if (len < sizeof(params)) {
75738c2ecf20Sopenharmony_ci		retval = -EINVAL;
75748c2ecf20Sopenharmony_ci		goto out;
75758c2ecf20Sopenharmony_ci	}
75768c2ecf20Sopenharmony_ci
75778c2ecf20Sopenharmony_ci	len = sizeof(params);
75788c2ecf20Sopenharmony_ci	if (copy_from_user(&params, optval, len))
75798c2ecf20Sopenharmony_ci		goto out;
75808c2ecf20Sopenharmony_ci
75818c2ecf20Sopenharmony_ci	asoc = sctp_id2assoc(sk, params.assoc_id);
75828c2ecf20Sopenharmony_ci	if (!asoc && params.assoc_id != SCTP_FUTURE_ASSOC &&
75838c2ecf20Sopenharmony_ci	    sctp_style(sk, UDP)) {
75848c2ecf20Sopenharmony_ci		retval = -EINVAL;
75858c2ecf20Sopenharmony_ci		goto out;
75868c2ecf20Sopenharmony_ci	}
75878c2ecf20Sopenharmony_ci
75888c2ecf20Sopenharmony_ci	params.assoc_value = asoc ? asoc->peer.intl_capable
75898c2ecf20Sopenharmony_ci				  : sctp_sk(sk)->ep->intl_enable;
75908c2ecf20Sopenharmony_ci
75918c2ecf20Sopenharmony_ci	if (put_user(len, optlen))
75928c2ecf20Sopenharmony_ci		goto out;
75938c2ecf20Sopenharmony_ci
75948c2ecf20Sopenharmony_ci	if (copy_to_user(optval, &params, len))
75958c2ecf20Sopenharmony_ci		goto out;
75968c2ecf20Sopenharmony_ci
75978c2ecf20Sopenharmony_ci	retval = 0;
75988c2ecf20Sopenharmony_ci
75998c2ecf20Sopenharmony_ciout:
76008c2ecf20Sopenharmony_ci	return retval;
76018c2ecf20Sopenharmony_ci}
76028c2ecf20Sopenharmony_ci
76038c2ecf20Sopenharmony_cistatic int sctp_getsockopt_reuse_port(struct sock *sk, int len,
76048c2ecf20Sopenharmony_ci				      char __user *optval,
76058c2ecf20Sopenharmony_ci				      int __user *optlen)
76068c2ecf20Sopenharmony_ci{
76078c2ecf20Sopenharmony_ci	int val;
76088c2ecf20Sopenharmony_ci
76098c2ecf20Sopenharmony_ci	if (len < sizeof(int))
76108c2ecf20Sopenharmony_ci		return -EINVAL;
76118c2ecf20Sopenharmony_ci
76128c2ecf20Sopenharmony_ci	len = sizeof(int);
76138c2ecf20Sopenharmony_ci	val = sctp_sk(sk)->reuse;
76148c2ecf20Sopenharmony_ci	if (put_user(len, optlen))
76158c2ecf20Sopenharmony_ci		return -EFAULT;
76168c2ecf20Sopenharmony_ci
76178c2ecf20Sopenharmony_ci	if (copy_to_user(optval, &val, len))
76188c2ecf20Sopenharmony_ci		return -EFAULT;
76198c2ecf20Sopenharmony_ci
76208c2ecf20Sopenharmony_ci	return 0;
76218c2ecf20Sopenharmony_ci}
76228c2ecf20Sopenharmony_ci
76238c2ecf20Sopenharmony_cistatic int sctp_getsockopt_event(struct sock *sk, int len, char __user *optval,
76248c2ecf20Sopenharmony_ci				 int __user *optlen)
76258c2ecf20Sopenharmony_ci{
76268c2ecf20Sopenharmony_ci	struct sctp_association *asoc;
76278c2ecf20Sopenharmony_ci	struct sctp_event param;
76288c2ecf20Sopenharmony_ci	__u16 subscribe;
76298c2ecf20Sopenharmony_ci
76308c2ecf20Sopenharmony_ci	if (len < sizeof(param))
76318c2ecf20Sopenharmony_ci		return -EINVAL;
76328c2ecf20Sopenharmony_ci
76338c2ecf20Sopenharmony_ci	len = sizeof(param);
76348c2ecf20Sopenharmony_ci	if (copy_from_user(&param, optval, len))
76358c2ecf20Sopenharmony_ci		return -EFAULT;
76368c2ecf20Sopenharmony_ci
76378c2ecf20Sopenharmony_ci	if (param.se_type < SCTP_SN_TYPE_BASE ||
76388c2ecf20Sopenharmony_ci	    param.se_type > SCTP_SN_TYPE_MAX)
76398c2ecf20Sopenharmony_ci		return -EINVAL;
76408c2ecf20Sopenharmony_ci
76418c2ecf20Sopenharmony_ci	asoc = sctp_id2assoc(sk, param.se_assoc_id);
76428c2ecf20Sopenharmony_ci	if (!asoc && param.se_assoc_id != SCTP_FUTURE_ASSOC &&
76438c2ecf20Sopenharmony_ci	    sctp_style(sk, UDP))
76448c2ecf20Sopenharmony_ci		return -EINVAL;
76458c2ecf20Sopenharmony_ci
76468c2ecf20Sopenharmony_ci	subscribe = asoc ? asoc->subscribe : sctp_sk(sk)->subscribe;
76478c2ecf20Sopenharmony_ci	param.se_on = sctp_ulpevent_type_enabled(subscribe, param.se_type);
76488c2ecf20Sopenharmony_ci
76498c2ecf20Sopenharmony_ci	if (put_user(len, optlen))
76508c2ecf20Sopenharmony_ci		return -EFAULT;
76518c2ecf20Sopenharmony_ci
76528c2ecf20Sopenharmony_ci	if (copy_to_user(optval, &param, len))
76538c2ecf20Sopenharmony_ci		return -EFAULT;
76548c2ecf20Sopenharmony_ci
76558c2ecf20Sopenharmony_ci	return 0;
76568c2ecf20Sopenharmony_ci}
76578c2ecf20Sopenharmony_ci
76588c2ecf20Sopenharmony_cistatic int sctp_getsockopt_asconf_supported(struct sock *sk, int len,
76598c2ecf20Sopenharmony_ci					    char __user *optval,
76608c2ecf20Sopenharmony_ci					    int __user *optlen)
76618c2ecf20Sopenharmony_ci{
76628c2ecf20Sopenharmony_ci	struct sctp_assoc_value params;
76638c2ecf20Sopenharmony_ci	struct sctp_association *asoc;
76648c2ecf20Sopenharmony_ci	int retval = -EFAULT;
76658c2ecf20Sopenharmony_ci
76668c2ecf20Sopenharmony_ci	if (len < sizeof(params)) {
76678c2ecf20Sopenharmony_ci		retval = -EINVAL;
76688c2ecf20Sopenharmony_ci		goto out;
76698c2ecf20Sopenharmony_ci	}
76708c2ecf20Sopenharmony_ci
76718c2ecf20Sopenharmony_ci	len = sizeof(params);
76728c2ecf20Sopenharmony_ci	if (copy_from_user(&params, optval, len))
76738c2ecf20Sopenharmony_ci		goto out;
76748c2ecf20Sopenharmony_ci
76758c2ecf20Sopenharmony_ci	asoc = sctp_id2assoc(sk, params.assoc_id);
76768c2ecf20Sopenharmony_ci	if (!asoc && params.assoc_id != SCTP_FUTURE_ASSOC &&
76778c2ecf20Sopenharmony_ci	    sctp_style(sk, UDP)) {
76788c2ecf20Sopenharmony_ci		retval = -EINVAL;
76798c2ecf20Sopenharmony_ci		goto out;
76808c2ecf20Sopenharmony_ci	}
76818c2ecf20Sopenharmony_ci
76828c2ecf20Sopenharmony_ci	params.assoc_value = asoc ? asoc->peer.asconf_capable
76838c2ecf20Sopenharmony_ci				  : sctp_sk(sk)->ep->asconf_enable;
76848c2ecf20Sopenharmony_ci
76858c2ecf20Sopenharmony_ci	if (put_user(len, optlen))
76868c2ecf20Sopenharmony_ci		goto out;
76878c2ecf20Sopenharmony_ci
76888c2ecf20Sopenharmony_ci	if (copy_to_user(optval, &params, len))
76898c2ecf20Sopenharmony_ci		goto out;
76908c2ecf20Sopenharmony_ci
76918c2ecf20Sopenharmony_ci	retval = 0;
76928c2ecf20Sopenharmony_ci
76938c2ecf20Sopenharmony_ciout:
76948c2ecf20Sopenharmony_ci	return retval;
76958c2ecf20Sopenharmony_ci}
76968c2ecf20Sopenharmony_ci
76978c2ecf20Sopenharmony_cistatic int sctp_getsockopt_auth_supported(struct sock *sk, int len,
76988c2ecf20Sopenharmony_ci					  char __user *optval,
76998c2ecf20Sopenharmony_ci					  int __user *optlen)
77008c2ecf20Sopenharmony_ci{
77018c2ecf20Sopenharmony_ci	struct sctp_assoc_value params;
77028c2ecf20Sopenharmony_ci	struct sctp_association *asoc;
77038c2ecf20Sopenharmony_ci	int retval = -EFAULT;
77048c2ecf20Sopenharmony_ci
77058c2ecf20Sopenharmony_ci	if (len < sizeof(params)) {
77068c2ecf20Sopenharmony_ci		retval = -EINVAL;
77078c2ecf20Sopenharmony_ci		goto out;
77088c2ecf20Sopenharmony_ci	}
77098c2ecf20Sopenharmony_ci
77108c2ecf20Sopenharmony_ci	len = sizeof(params);
77118c2ecf20Sopenharmony_ci	if (copy_from_user(&params, optval, len))
77128c2ecf20Sopenharmony_ci		goto out;
77138c2ecf20Sopenharmony_ci
77148c2ecf20Sopenharmony_ci	asoc = sctp_id2assoc(sk, params.assoc_id);
77158c2ecf20Sopenharmony_ci	if (!asoc && params.assoc_id != SCTP_FUTURE_ASSOC &&
77168c2ecf20Sopenharmony_ci	    sctp_style(sk, UDP)) {
77178c2ecf20Sopenharmony_ci		retval = -EINVAL;
77188c2ecf20Sopenharmony_ci		goto out;
77198c2ecf20Sopenharmony_ci	}
77208c2ecf20Sopenharmony_ci
77218c2ecf20Sopenharmony_ci	params.assoc_value = asoc ? asoc->peer.auth_capable
77228c2ecf20Sopenharmony_ci				  : sctp_sk(sk)->ep->auth_enable;
77238c2ecf20Sopenharmony_ci
77248c2ecf20Sopenharmony_ci	if (put_user(len, optlen))
77258c2ecf20Sopenharmony_ci		goto out;
77268c2ecf20Sopenharmony_ci
77278c2ecf20Sopenharmony_ci	if (copy_to_user(optval, &params, len))
77288c2ecf20Sopenharmony_ci		goto out;
77298c2ecf20Sopenharmony_ci
77308c2ecf20Sopenharmony_ci	retval = 0;
77318c2ecf20Sopenharmony_ci
77328c2ecf20Sopenharmony_ciout:
77338c2ecf20Sopenharmony_ci	return retval;
77348c2ecf20Sopenharmony_ci}
77358c2ecf20Sopenharmony_ci
77368c2ecf20Sopenharmony_cistatic int sctp_getsockopt_ecn_supported(struct sock *sk, int len,
77378c2ecf20Sopenharmony_ci					 char __user *optval,
77388c2ecf20Sopenharmony_ci					 int __user *optlen)
77398c2ecf20Sopenharmony_ci{
77408c2ecf20Sopenharmony_ci	struct sctp_assoc_value params;
77418c2ecf20Sopenharmony_ci	struct sctp_association *asoc;
77428c2ecf20Sopenharmony_ci	int retval = -EFAULT;
77438c2ecf20Sopenharmony_ci
77448c2ecf20Sopenharmony_ci	if (len < sizeof(params)) {
77458c2ecf20Sopenharmony_ci		retval = -EINVAL;
77468c2ecf20Sopenharmony_ci		goto out;
77478c2ecf20Sopenharmony_ci	}
77488c2ecf20Sopenharmony_ci
77498c2ecf20Sopenharmony_ci	len = sizeof(params);
77508c2ecf20Sopenharmony_ci	if (copy_from_user(&params, optval, len))
77518c2ecf20Sopenharmony_ci		goto out;
77528c2ecf20Sopenharmony_ci
77538c2ecf20Sopenharmony_ci	asoc = sctp_id2assoc(sk, params.assoc_id);
77548c2ecf20Sopenharmony_ci	if (!asoc && params.assoc_id != SCTP_FUTURE_ASSOC &&
77558c2ecf20Sopenharmony_ci	    sctp_style(sk, UDP)) {
77568c2ecf20Sopenharmony_ci		retval = -EINVAL;
77578c2ecf20Sopenharmony_ci		goto out;
77588c2ecf20Sopenharmony_ci	}
77598c2ecf20Sopenharmony_ci
77608c2ecf20Sopenharmony_ci	params.assoc_value = asoc ? asoc->peer.ecn_capable
77618c2ecf20Sopenharmony_ci				  : sctp_sk(sk)->ep->ecn_enable;
77628c2ecf20Sopenharmony_ci
77638c2ecf20Sopenharmony_ci	if (put_user(len, optlen))
77648c2ecf20Sopenharmony_ci		goto out;
77658c2ecf20Sopenharmony_ci
77668c2ecf20Sopenharmony_ci	if (copy_to_user(optval, &params, len))
77678c2ecf20Sopenharmony_ci		goto out;
77688c2ecf20Sopenharmony_ci
77698c2ecf20Sopenharmony_ci	retval = 0;
77708c2ecf20Sopenharmony_ci
77718c2ecf20Sopenharmony_ciout:
77728c2ecf20Sopenharmony_ci	return retval;
77738c2ecf20Sopenharmony_ci}
77748c2ecf20Sopenharmony_ci
77758c2ecf20Sopenharmony_cistatic int sctp_getsockopt_pf_expose(struct sock *sk, int len,
77768c2ecf20Sopenharmony_ci				     char __user *optval,
77778c2ecf20Sopenharmony_ci				     int __user *optlen)
77788c2ecf20Sopenharmony_ci{
77798c2ecf20Sopenharmony_ci	struct sctp_assoc_value params;
77808c2ecf20Sopenharmony_ci	struct sctp_association *asoc;
77818c2ecf20Sopenharmony_ci	int retval = -EFAULT;
77828c2ecf20Sopenharmony_ci
77838c2ecf20Sopenharmony_ci	if (len < sizeof(params)) {
77848c2ecf20Sopenharmony_ci		retval = -EINVAL;
77858c2ecf20Sopenharmony_ci		goto out;
77868c2ecf20Sopenharmony_ci	}
77878c2ecf20Sopenharmony_ci
77888c2ecf20Sopenharmony_ci	len = sizeof(params);
77898c2ecf20Sopenharmony_ci	if (copy_from_user(&params, optval, len))
77908c2ecf20Sopenharmony_ci		goto out;
77918c2ecf20Sopenharmony_ci
77928c2ecf20Sopenharmony_ci	asoc = sctp_id2assoc(sk, params.assoc_id);
77938c2ecf20Sopenharmony_ci	if (!asoc && params.assoc_id != SCTP_FUTURE_ASSOC &&
77948c2ecf20Sopenharmony_ci	    sctp_style(sk, UDP)) {
77958c2ecf20Sopenharmony_ci		retval = -EINVAL;
77968c2ecf20Sopenharmony_ci		goto out;
77978c2ecf20Sopenharmony_ci	}
77988c2ecf20Sopenharmony_ci
77998c2ecf20Sopenharmony_ci	params.assoc_value = asoc ? asoc->pf_expose
78008c2ecf20Sopenharmony_ci				  : sctp_sk(sk)->pf_expose;
78018c2ecf20Sopenharmony_ci
78028c2ecf20Sopenharmony_ci	if (put_user(len, optlen))
78038c2ecf20Sopenharmony_ci		goto out;
78048c2ecf20Sopenharmony_ci
78058c2ecf20Sopenharmony_ci	if (copy_to_user(optval, &params, len))
78068c2ecf20Sopenharmony_ci		goto out;
78078c2ecf20Sopenharmony_ci
78088c2ecf20Sopenharmony_ci	retval = 0;
78098c2ecf20Sopenharmony_ci
78108c2ecf20Sopenharmony_ciout:
78118c2ecf20Sopenharmony_ci	return retval;
78128c2ecf20Sopenharmony_ci}
78138c2ecf20Sopenharmony_ci
78148c2ecf20Sopenharmony_cistatic int sctp_getsockopt(struct sock *sk, int level, int optname,
78158c2ecf20Sopenharmony_ci			   char __user *optval, int __user *optlen)
78168c2ecf20Sopenharmony_ci{
78178c2ecf20Sopenharmony_ci	int retval = 0;
78188c2ecf20Sopenharmony_ci	int len;
78198c2ecf20Sopenharmony_ci
78208c2ecf20Sopenharmony_ci	pr_debug("%s: sk:%p, optname:%d\n", __func__, sk, optname);
78218c2ecf20Sopenharmony_ci
78228c2ecf20Sopenharmony_ci	/* I can hardly begin to describe how wrong this is.  This is
78238c2ecf20Sopenharmony_ci	 * so broken as to be worse than useless.  The API draft
78248c2ecf20Sopenharmony_ci	 * REALLY is NOT helpful here...  I am not convinced that the
78258c2ecf20Sopenharmony_ci	 * semantics of getsockopt() with a level OTHER THAN SOL_SCTP
78268c2ecf20Sopenharmony_ci	 * are at all well-founded.
78278c2ecf20Sopenharmony_ci	 */
78288c2ecf20Sopenharmony_ci	if (level != SOL_SCTP) {
78298c2ecf20Sopenharmony_ci		struct sctp_af *af = sctp_sk(sk)->pf->af;
78308c2ecf20Sopenharmony_ci
78318c2ecf20Sopenharmony_ci		retval = af->getsockopt(sk, level, optname, optval, optlen);
78328c2ecf20Sopenharmony_ci		return retval;
78338c2ecf20Sopenharmony_ci	}
78348c2ecf20Sopenharmony_ci
78358c2ecf20Sopenharmony_ci	if (get_user(len, optlen))
78368c2ecf20Sopenharmony_ci		return -EFAULT;
78378c2ecf20Sopenharmony_ci
78388c2ecf20Sopenharmony_ci	if (len < 0)
78398c2ecf20Sopenharmony_ci		return -EINVAL;
78408c2ecf20Sopenharmony_ci
78418c2ecf20Sopenharmony_ci	lock_sock(sk);
78428c2ecf20Sopenharmony_ci
78438c2ecf20Sopenharmony_ci	switch (optname) {
78448c2ecf20Sopenharmony_ci	case SCTP_STATUS:
78458c2ecf20Sopenharmony_ci		retval = sctp_getsockopt_sctp_status(sk, len, optval, optlen);
78468c2ecf20Sopenharmony_ci		break;
78478c2ecf20Sopenharmony_ci	case SCTP_DISABLE_FRAGMENTS:
78488c2ecf20Sopenharmony_ci		retval = sctp_getsockopt_disable_fragments(sk, len, optval,
78498c2ecf20Sopenharmony_ci							   optlen);
78508c2ecf20Sopenharmony_ci		break;
78518c2ecf20Sopenharmony_ci	case SCTP_EVENTS:
78528c2ecf20Sopenharmony_ci		retval = sctp_getsockopt_events(sk, len, optval, optlen);
78538c2ecf20Sopenharmony_ci		break;
78548c2ecf20Sopenharmony_ci	case SCTP_AUTOCLOSE:
78558c2ecf20Sopenharmony_ci		retval = sctp_getsockopt_autoclose(sk, len, optval, optlen);
78568c2ecf20Sopenharmony_ci		break;
78578c2ecf20Sopenharmony_ci	case SCTP_SOCKOPT_PEELOFF:
78588c2ecf20Sopenharmony_ci		retval = sctp_getsockopt_peeloff(sk, len, optval, optlen);
78598c2ecf20Sopenharmony_ci		break;
78608c2ecf20Sopenharmony_ci	case SCTP_SOCKOPT_PEELOFF_FLAGS:
78618c2ecf20Sopenharmony_ci		retval = sctp_getsockopt_peeloff_flags(sk, len, optval, optlen);
78628c2ecf20Sopenharmony_ci		break;
78638c2ecf20Sopenharmony_ci	case SCTP_PEER_ADDR_PARAMS:
78648c2ecf20Sopenharmony_ci		retval = sctp_getsockopt_peer_addr_params(sk, len, optval,
78658c2ecf20Sopenharmony_ci							  optlen);
78668c2ecf20Sopenharmony_ci		break;
78678c2ecf20Sopenharmony_ci	case SCTP_DELAYED_SACK:
78688c2ecf20Sopenharmony_ci		retval = sctp_getsockopt_delayed_ack(sk, len, optval,
78698c2ecf20Sopenharmony_ci							  optlen);
78708c2ecf20Sopenharmony_ci		break;
78718c2ecf20Sopenharmony_ci	case SCTP_INITMSG:
78728c2ecf20Sopenharmony_ci		retval = sctp_getsockopt_initmsg(sk, len, optval, optlen);
78738c2ecf20Sopenharmony_ci		break;
78748c2ecf20Sopenharmony_ci	case SCTP_GET_PEER_ADDRS:
78758c2ecf20Sopenharmony_ci		retval = sctp_getsockopt_peer_addrs(sk, len, optval,
78768c2ecf20Sopenharmony_ci						    optlen);
78778c2ecf20Sopenharmony_ci		break;
78788c2ecf20Sopenharmony_ci	case SCTP_GET_LOCAL_ADDRS:
78798c2ecf20Sopenharmony_ci		retval = sctp_getsockopt_local_addrs(sk, len, optval,
78808c2ecf20Sopenharmony_ci						     optlen);
78818c2ecf20Sopenharmony_ci		break;
78828c2ecf20Sopenharmony_ci	case SCTP_SOCKOPT_CONNECTX3:
78838c2ecf20Sopenharmony_ci		retval = sctp_getsockopt_connectx3(sk, len, optval, optlen);
78848c2ecf20Sopenharmony_ci		break;
78858c2ecf20Sopenharmony_ci	case SCTP_DEFAULT_SEND_PARAM:
78868c2ecf20Sopenharmony_ci		retval = sctp_getsockopt_default_send_param(sk, len,
78878c2ecf20Sopenharmony_ci							    optval, optlen);
78888c2ecf20Sopenharmony_ci		break;
78898c2ecf20Sopenharmony_ci	case SCTP_DEFAULT_SNDINFO:
78908c2ecf20Sopenharmony_ci		retval = sctp_getsockopt_default_sndinfo(sk, len,
78918c2ecf20Sopenharmony_ci							 optval, optlen);
78928c2ecf20Sopenharmony_ci		break;
78938c2ecf20Sopenharmony_ci	case SCTP_PRIMARY_ADDR:
78948c2ecf20Sopenharmony_ci		retval = sctp_getsockopt_primary_addr(sk, len, optval, optlen);
78958c2ecf20Sopenharmony_ci		break;
78968c2ecf20Sopenharmony_ci	case SCTP_NODELAY:
78978c2ecf20Sopenharmony_ci		retval = sctp_getsockopt_nodelay(sk, len, optval, optlen);
78988c2ecf20Sopenharmony_ci		break;
78998c2ecf20Sopenharmony_ci	case SCTP_RTOINFO:
79008c2ecf20Sopenharmony_ci		retval = sctp_getsockopt_rtoinfo(sk, len, optval, optlen);
79018c2ecf20Sopenharmony_ci		break;
79028c2ecf20Sopenharmony_ci	case SCTP_ASSOCINFO:
79038c2ecf20Sopenharmony_ci		retval = sctp_getsockopt_associnfo(sk, len, optval, optlen);
79048c2ecf20Sopenharmony_ci		break;
79058c2ecf20Sopenharmony_ci	case SCTP_I_WANT_MAPPED_V4_ADDR:
79068c2ecf20Sopenharmony_ci		retval = sctp_getsockopt_mappedv4(sk, len, optval, optlen);
79078c2ecf20Sopenharmony_ci		break;
79088c2ecf20Sopenharmony_ci	case SCTP_MAXSEG:
79098c2ecf20Sopenharmony_ci		retval = sctp_getsockopt_maxseg(sk, len, optval, optlen);
79108c2ecf20Sopenharmony_ci		break;
79118c2ecf20Sopenharmony_ci	case SCTP_GET_PEER_ADDR_INFO:
79128c2ecf20Sopenharmony_ci		retval = sctp_getsockopt_peer_addr_info(sk, len, optval,
79138c2ecf20Sopenharmony_ci							optlen);
79148c2ecf20Sopenharmony_ci		break;
79158c2ecf20Sopenharmony_ci	case SCTP_ADAPTATION_LAYER:
79168c2ecf20Sopenharmony_ci		retval = sctp_getsockopt_adaptation_layer(sk, len, optval,
79178c2ecf20Sopenharmony_ci							optlen);
79188c2ecf20Sopenharmony_ci		break;
79198c2ecf20Sopenharmony_ci	case SCTP_CONTEXT:
79208c2ecf20Sopenharmony_ci		retval = sctp_getsockopt_context(sk, len, optval, optlen);
79218c2ecf20Sopenharmony_ci		break;
79228c2ecf20Sopenharmony_ci	case SCTP_FRAGMENT_INTERLEAVE:
79238c2ecf20Sopenharmony_ci		retval = sctp_getsockopt_fragment_interleave(sk, len, optval,
79248c2ecf20Sopenharmony_ci							     optlen);
79258c2ecf20Sopenharmony_ci		break;
79268c2ecf20Sopenharmony_ci	case SCTP_PARTIAL_DELIVERY_POINT:
79278c2ecf20Sopenharmony_ci		retval = sctp_getsockopt_partial_delivery_point(sk, len, optval,
79288c2ecf20Sopenharmony_ci								optlen);
79298c2ecf20Sopenharmony_ci		break;
79308c2ecf20Sopenharmony_ci	case SCTP_MAX_BURST:
79318c2ecf20Sopenharmony_ci		retval = sctp_getsockopt_maxburst(sk, len, optval, optlen);
79328c2ecf20Sopenharmony_ci		break;
79338c2ecf20Sopenharmony_ci	case SCTP_AUTH_KEY:
79348c2ecf20Sopenharmony_ci	case SCTP_AUTH_CHUNK:
79358c2ecf20Sopenharmony_ci	case SCTP_AUTH_DELETE_KEY:
79368c2ecf20Sopenharmony_ci	case SCTP_AUTH_DEACTIVATE_KEY:
79378c2ecf20Sopenharmony_ci		retval = -EOPNOTSUPP;
79388c2ecf20Sopenharmony_ci		break;
79398c2ecf20Sopenharmony_ci	case SCTP_HMAC_IDENT:
79408c2ecf20Sopenharmony_ci		retval = sctp_getsockopt_hmac_ident(sk, len, optval, optlen);
79418c2ecf20Sopenharmony_ci		break;
79428c2ecf20Sopenharmony_ci	case SCTP_AUTH_ACTIVE_KEY:
79438c2ecf20Sopenharmony_ci		retval = sctp_getsockopt_active_key(sk, len, optval, optlen);
79448c2ecf20Sopenharmony_ci		break;
79458c2ecf20Sopenharmony_ci	case SCTP_PEER_AUTH_CHUNKS:
79468c2ecf20Sopenharmony_ci		retval = sctp_getsockopt_peer_auth_chunks(sk, len, optval,
79478c2ecf20Sopenharmony_ci							optlen);
79488c2ecf20Sopenharmony_ci		break;
79498c2ecf20Sopenharmony_ci	case SCTP_LOCAL_AUTH_CHUNKS:
79508c2ecf20Sopenharmony_ci		retval = sctp_getsockopt_local_auth_chunks(sk, len, optval,
79518c2ecf20Sopenharmony_ci							optlen);
79528c2ecf20Sopenharmony_ci		break;
79538c2ecf20Sopenharmony_ci	case SCTP_GET_ASSOC_NUMBER:
79548c2ecf20Sopenharmony_ci		retval = sctp_getsockopt_assoc_number(sk, len, optval, optlen);
79558c2ecf20Sopenharmony_ci		break;
79568c2ecf20Sopenharmony_ci	case SCTP_GET_ASSOC_ID_LIST:
79578c2ecf20Sopenharmony_ci		retval = sctp_getsockopt_assoc_ids(sk, len, optval, optlen);
79588c2ecf20Sopenharmony_ci		break;
79598c2ecf20Sopenharmony_ci	case SCTP_AUTO_ASCONF:
79608c2ecf20Sopenharmony_ci		retval = sctp_getsockopt_auto_asconf(sk, len, optval, optlen);
79618c2ecf20Sopenharmony_ci		break;
79628c2ecf20Sopenharmony_ci	case SCTP_PEER_ADDR_THLDS:
79638c2ecf20Sopenharmony_ci		retval = sctp_getsockopt_paddr_thresholds(sk, optval, len,
79648c2ecf20Sopenharmony_ci							  optlen, false);
79658c2ecf20Sopenharmony_ci		break;
79668c2ecf20Sopenharmony_ci	case SCTP_PEER_ADDR_THLDS_V2:
79678c2ecf20Sopenharmony_ci		retval = sctp_getsockopt_paddr_thresholds(sk, optval, len,
79688c2ecf20Sopenharmony_ci							  optlen, true);
79698c2ecf20Sopenharmony_ci		break;
79708c2ecf20Sopenharmony_ci	case SCTP_GET_ASSOC_STATS:
79718c2ecf20Sopenharmony_ci		retval = sctp_getsockopt_assoc_stats(sk, len, optval, optlen);
79728c2ecf20Sopenharmony_ci		break;
79738c2ecf20Sopenharmony_ci	case SCTP_RECVRCVINFO:
79748c2ecf20Sopenharmony_ci		retval = sctp_getsockopt_recvrcvinfo(sk, len, optval, optlen);
79758c2ecf20Sopenharmony_ci		break;
79768c2ecf20Sopenharmony_ci	case SCTP_RECVNXTINFO:
79778c2ecf20Sopenharmony_ci		retval = sctp_getsockopt_recvnxtinfo(sk, len, optval, optlen);
79788c2ecf20Sopenharmony_ci		break;
79798c2ecf20Sopenharmony_ci	case SCTP_PR_SUPPORTED:
79808c2ecf20Sopenharmony_ci		retval = sctp_getsockopt_pr_supported(sk, len, optval, optlen);
79818c2ecf20Sopenharmony_ci		break;
79828c2ecf20Sopenharmony_ci	case SCTP_DEFAULT_PRINFO:
79838c2ecf20Sopenharmony_ci		retval = sctp_getsockopt_default_prinfo(sk, len, optval,
79848c2ecf20Sopenharmony_ci							optlen);
79858c2ecf20Sopenharmony_ci		break;
79868c2ecf20Sopenharmony_ci	case SCTP_PR_ASSOC_STATUS:
79878c2ecf20Sopenharmony_ci		retval = sctp_getsockopt_pr_assocstatus(sk, len, optval,
79888c2ecf20Sopenharmony_ci							optlen);
79898c2ecf20Sopenharmony_ci		break;
79908c2ecf20Sopenharmony_ci	case SCTP_PR_STREAM_STATUS:
79918c2ecf20Sopenharmony_ci		retval = sctp_getsockopt_pr_streamstatus(sk, len, optval,
79928c2ecf20Sopenharmony_ci							 optlen);
79938c2ecf20Sopenharmony_ci		break;
79948c2ecf20Sopenharmony_ci	case SCTP_RECONFIG_SUPPORTED:
79958c2ecf20Sopenharmony_ci		retval = sctp_getsockopt_reconfig_supported(sk, len, optval,
79968c2ecf20Sopenharmony_ci							    optlen);
79978c2ecf20Sopenharmony_ci		break;
79988c2ecf20Sopenharmony_ci	case SCTP_ENABLE_STREAM_RESET:
79998c2ecf20Sopenharmony_ci		retval = sctp_getsockopt_enable_strreset(sk, len, optval,
80008c2ecf20Sopenharmony_ci							 optlen);
80018c2ecf20Sopenharmony_ci		break;
80028c2ecf20Sopenharmony_ci	case SCTP_STREAM_SCHEDULER:
80038c2ecf20Sopenharmony_ci		retval = sctp_getsockopt_scheduler(sk, len, optval,
80048c2ecf20Sopenharmony_ci						   optlen);
80058c2ecf20Sopenharmony_ci		break;
80068c2ecf20Sopenharmony_ci	case SCTP_STREAM_SCHEDULER_VALUE:
80078c2ecf20Sopenharmony_ci		retval = sctp_getsockopt_scheduler_value(sk, len, optval,
80088c2ecf20Sopenharmony_ci							 optlen);
80098c2ecf20Sopenharmony_ci		break;
80108c2ecf20Sopenharmony_ci	case SCTP_INTERLEAVING_SUPPORTED:
80118c2ecf20Sopenharmony_ci		retval = sctp_getsockopt_interleaving_supported(sk, len, optval,
80128c2ecf20Sopenharmony_ci								optlen);
80138c2ecf20Sopenharmony_ci		break;
80148c2ecf20Sopenharmony_ci	case SCTP_REUSE_PORT:
80158c2ecf20Sopenharmony_ci		retval = sctp_getsockopt_reuse_port(sk, len, optval, optlen);
80168c2ecf20Sopenharmony_ci		break;
80178c2ecf20Sopenharmony_ci	case SCTP_EVENT:
80188c2ecf20Sopenharmony_ci		retval = sctp_getsockopt_event(sk, len, optval, optlen);
80198c2ecf20Sopenharmony_ci		break;
80208c2ecf20Sopenharmony_ci	case SCTP_ASCONF_SUPPORTED:
80218c2ecf20Sopenharmony_ci		retval = sctp_getsockopt_asconf_supported(sk, len, optval,
80228c2ecf20Sopenharmony_ci							  optlen);
80238c2ecf20Sopenharmony_ci		break;
80248c2ecf20Sopenharmony_ci	case SCTP_AUTH_SUPPORTED:
80258c2ecf20Sopenharmony_ci		retval = sctp_getsockopt_auth_supported(sk, len, optval,
80268c2ecf20Sopenharmony_ci							optlen);
80278c2ecf20Sopenharmony_ci		break;
80288c2ecf20Sopenharmony_ci	case SCTP_ECN_SUPPORTED:
80298c2ecf20Sopenharmony_ci		retval = sctp_getsockopt_ecn_supported(sk, len, optval, optlen);
80308c2ecf20Sopenharmony_ci		break;
80318c2ecf20Sopenharmony_ci	case SCTP_EXPOSE_POTENTIALLY_FAILED_STATE:
80328c2ecf20Sopenharmony_ci		retval = sctp_getsockopt_pf_expose(sk, len, optval, optlen);
80338c2ecf20Sopenharmony_ci		break;
80348c2ecf20Sopenharmony_ci	default:
80358c2ecf20Sopenharmony_ci		retval = -ENOPROTOOPT;
80368c2ecf20Sopenharmony_ci		break;
80378c2ecf20Sopenharmony_ci	}
80388c2ecf20Sopenharmony_ci
80398c2ecf20Sopenharmony_ci	release_sock(sk);
80408c2ecf20Sopenharmony_ci	return retval;
80418c2ecf20Sopenharmony_ci}
80428c2ecf20Sopenharmony_ci
80438c2ecf20Sopenharmony_cistatic bool sctp_bpf_bypass_getsockopt(int level, int optname)
80448c2ecf20Sopenharmony_ci{
80458c2ecf20Sopenharmony_ci	if (level == SOL_SCTP) {
80468c2ecf20Sopenharmony_ci		switch (optname) {
80478c2ecf20Sopenharmony_ci		case SCTP_SOCKOPT_PEELOFF:
80488c2ecf20Sopenharmony_ci		case SCTP_SOCKOPT_PEELOFF_FLAGS:
80498c2ecf20Sopenharmony_ci		case SCTP_SOCKOPT_CONNECTX3:
80508c2ecf20Sopenharmony_ci			return true;
80518c2ecf20Sopenharmony_ci		default:
80528c2ecf20Sopenharmony_ci			return false;
80538c2ecf20Sopenharmony_ci		}
80548c2ecf20Sopenharmony_ci	}
80558c2ecf20Sopenharmony_ci
80568c2ecf20Sopenharmony_ci	return false;
80578c2ecf20Sopenharmony_ci}
80588c2ecf20Sopenharmony_ci
80598c2ecf20Sopenharmony_cistatic int sctp_hash(struct sock *sk)
80608c2ecf20Sopenharmony_ci{
80618c2ecf20Sopenharmony_ci	/* STUB */
80628c2ecf20Sopenharmony_ci	return 0;
80638c2ecf20Sopenharmony_ci}
80648c2ecf20Sopenharmony_ci
80658c2ecf20Sopenharmony_cistatic void sctp_unhash(struct sock *sk)
80668c2ecf20Sopenharmony_ci{
80678c2ecf20Sopenharmony_ci	/* STUB */
80688c2ecf20Sopenharmony_ci}
80698c2ecf20Sopenharmony_ci
80708c2ecf20Sopenharmony_ci/* Check if port is acceptable.  Possibly find first available port.
80718c2ecf20Sopenharmony_ci *
80728c2ecf20Sopenharmony_ci * The port hash table (contained in the 'global' SCTP protocol storage
80738c2ecf20Sopenharmony_ci * returned by struct sctp_protocol *sctp_get_protocol()). The hash
80748c2ecf20Sopenharmony_ci * table is an array of 4096 lists (sctp_bind_hashbucket). Each
80758c2ecf20Sopenharmony_ci * list (the list number is the port number hashed out, so as you
80768c2ecf20Sopenharmony_ci * would expect from a hash function, all the ports in a given list have
80778c2ecf20Sopenharmony_ci * such a number that hashes out to the same list number; you were
80788c2ecf20Sopenharmony_ci * expecting that, right?); so each list has a set of ports, with a
80798c2ecf20Sopenharmony_ci * link to the socket (struct sock) that uses it, the port number and
80808c2ecf20Sopenharmony_ci * a fastreuse flag (FIXME: NPI ipg).
80818c2ecf20Sopenharmony_ci */
80828c2ecf20Sopenharmony_cistatic struct sctp_bind_bucket *sctp_bucket_create(
80838c2ecf20Sopenharmony_ci	struct sctp_bind_hashbucket *head, struct net *, unsigned short snum);
80848c2ecf20Sopenharmony_ci
80858c2ecf20Sopenharmony_cistatic int sctp_get_port_local(struct sock *sk, union sctp_addr *addr)
80868c2ecf20Sopenharmony_ci{
80878c2ecf20Sopenharmony_ci	struct sctp_sock *sp = sctp_sk(sk);
80888c2ecf20Sopenharmony_ci	bool reuse = (sk->sk_reuse || sp->reuse);
80898c2ecf20Sopenharmony_ci	struct sctp_bind_hashbucket *head; /* hash list */
80908c2ecf20Sopenharmony_ci	struct net *net = sock_net(sk);
80918c2ecf20Sopenharmony_ci	kuid_t uid = sock_i_uid(sk);
80928c2ecf20Sopenharmony_ci	struct sctp_bind_bucket *pp;
80938c2ecf20Sopenharmony_ci	unsigned short snum;
80948c2ecf20Sopenharmony_ci	int ret;
80958c2ecf20Sopenharmony_ci
80968c2ecf20Sopenharmony_ci	snum = ntohs(addr->v4.sin_port);
80978c2ecf20Sopenharmony_ci
80988c2ecf20Sopenharmony_ci	pr_debug("%s: begins, snum:%d\n", __func__, snum);
80998c2ecf20Sopenharmony_ci
81008c2ecf20Sopenharmony_ci	if (snum == 0) {
81018c2ecf20Sopenharmony_ci		/* Search for an available port. */
81028c2ecf20Sopenharmony_ci		int low, high, remaining, index;
81038c2ecf20Sopenharmony_ci		unsigned int rover;
81048c2ecf20Sopenharmony_ci
81058c2ecf20Sopenharmony_ci		inet_get_local_port_range(net, &low, &high);
81068c2ecf20Sopenharmony_ci		remaining = (high - low) + 1;
81078c2ecf20Sopenharmony_ci		rover = prandom_u32() % remaining + low;
81088c2ecf20Sopenharmony_ci
81098c2ecf20Sopenharmony_ci		do {
81108c2ecf20Sopenharmony_ci			rover++;
81118c2ecf20Sopenharmony_ci			if ((rover < low) || (rover > high))
81128c2ecf20Sopenharmony_ci				rover = low;
81138c2ecf20Sopenharmony_ci			if (inet_is_local_reserved_port(net, rover))
81148c2ecf20Sopenharmony_ci				continue;
81158c2ecf20Sopenharmony_ci			index = sctp_phashfn(net, rover);
81168c2ecf20Sopenharmony_ci			head = &sctp_port_hashtable[index];
81178c2ecf20Sopenharmony_ci			spin_lock_bh(&head->lock);
81188c2ecf20Sopenharmony_ci			sctp_for_each_hentry(pp, &head->chain)
81198c2ecf20Sopenharmony_ci				if ((pp->port == rover) &&
81208c2ecf20Sopenharmony_ci				    net_eq(net, pp->net))
81218c2ecf20Sopenharmony_ci					goto next;
81228c2ecf20Sopenharmony_ci			break;
81238c2ecf20Sopenharmony_ci		next:
81248c2ecf20Sopenharmony_ci			spin_unlock_bh(&head->lock);
81258c2ecf20Sopenharmony_ci			cond_resched();
81268c2ecf20Sopenharmony_ci		} while (--remaining > 0);
81278c2ecf20Sopenharmony_ci
81288c2ecf20Sopenharmony_ci		/* Exhausted local port range during search? */
81298c2ecf20Sopenharmony_ci		ret = 1;
81308c2ecf20Sopenharmony_ci		if (remaining <= 0)
81318c2ecf20Sopenharmony_ci			return ret;
81328c2ecf20Sopenharmony_ci
81338c2ecf20Sopenharmony_ci		/* OK, here is the one we will use.  HEAD (the port
81348c2ecf20Sopenharmony_ci		 * hash table list entry) is non-NULL and we hold it's
81358c2ecf20Sopenharmony_ci		 * mutex.
81368c2ecf20Sopenharmony_ci		 */
81378c2ecf20Sopenharmony_ci		snum = rover;
81388c2ecf20Sopenharmony_ci	} else {
81398c2ecf20Sopenharmony_ci		/* We are given an specific port number; we verify
81408c2ecf20Sopenharmony_ci		 * that it is not being used. If it is used, we will
81418c2ecf20Sopenharmony_ci		 * exahust the search in the hash list corresponding
81428c2ecf20Sopenharmony_ci		 * to the port number (snum) - we detect that with the
81438c2ecf20Sopenharmony_ci		 * port iterator, pp being NULL.
81448c2ecf20Sopenharmony_ci		 */
81458c2ecf20Sopenharmony_ci		head = &sctp_port_hashtable[sctp_phashfn(net, snum)];
81468c2ecf20Sopenharmony_ci		spin_lock_bh(&head->lock);
81478c2ecf20Sopenharmony_ci		sctp_for_each_hentry(pp, &head->chain) {
81488c2ecf20Sopenharmony_ci			if ((pp->port == snum) && net_eq(pp->net, net))
81498c2ecf20Sopenharmony_ci				goto pp_found;
81508c2ecf20Sopenharmony_ci		}
81518c2ecf20Sopenharmony_ci	}
81528c2ecf20Sopenharmony_ci	pp = NULL;
81538c2ecf20Sopenharmony_ci	goto pp_not_found;
81548c2ecf20Sopenharmony_cipp_found:
81558c2ecf20Sopenharmony_ci	if (!hlist_empty(&pp->owner)) {
81568c2ecf20Sopenharmony_ci		/* We had a port hash table hit - there is an
81578c2ecf20Sopenharmony_ci		 * available port (pp != NULL) and it is being
81588c2ecf20Sopenharmony_ci		 * used by other socket (pp->owner not empty); that other
81598c2ecf20Sopenharmony_ci		 * socket is going to be sk2.
81608c2ecf20Sopenharmony_ci		 */
81618c2ecf20Sopenharmony_ci		struct sock *sk2;
81628c2ecf20Sopenharmony_ci
81638c2ecf20Sopenharmony_ci		pr_debug("%s: found a possible match\n", __func__);
81648c2ecf20Sopenharmony_ci
81658c2ecf20Sopenharmony_ci		if ((pp->fastreuse && reuse &&
81668c2ecf20Sopenharmony_ci		     sk->sk_state != SCTP_SS_LISTENING) ||
81678c2ecf20Sopenharmony_ci		    (pp->fastreuseport && sk->sk_reuseport &&
81688c2ecf20Sopenharmony_ci		     uid_eq(pp->fastuid, uid)))
81698c2ecf20Sopenharmony_ci			goto success;
81708c2ecf20Sopenharmony_ci
81718c2ecf20Sopenharmony_ci		/* Run through the list of sockets bound to the port
81728c2ecf20Sopenharmony_ci		 * (pp->port) [via the pointers bind_next and
81738c2ecf20Sopenharmony_ci		 * bind_pprev in the struct sock *sk2 (pp->sk)]. On each one,
81748c2ecf20Sopenharmony_ci		 * we get the endpoint they describe and run through
81758c2ecf20Sopenharmony_ci		 * the endpoint's list of IP (v4 or v6) addresses,
81768c2ecf20Sopenharmony_ci		 * comparing each of the addresses with the address of
81778c2ecf20Sopenharmony_ci		 * the socket sk. If we find a match, then that means
81788c2ecf20Sopenharmony_ci		 * that this port/socket (sk) combination are already
81798c2ecf20Sopenharmony_ci		 * in an endpoint.
81808c2ecf20Sopenharmony_ci		 */
81818c2ecf20Sopenharmony_ci		sk_for_each_bound(sk2, &pp->owner) {
81828c2ecf20Sopenharmony_ci			struct sctp_sock *sp2 = sctp_sk(sk2);
81838c2ecf20Sopenharmony_ci			struct sctp_endpoint *ep2 = sp2->ep;
81848c2ecf20Sopenharmony_ci
81858c2ecf20Sopenharmony_ci			if (sk == sk2 ||
81868c2ecf20Sopenharmony_ci			    (reuse && (sk2->sk_reuse || sp2->reuse) &&
81878c2ecf20Sopenharmony_ci			     sk2->sk_state != SCTP_SS_LISTENING) ||
81888c2ecf20Sopenharmony_ci			    (sk->sk_reuseport && sk2->sk_reuseport &&
81898c2ecf20Sopenharmony_ci			     uid_eq(uid, sock_i_uid(sk2))))
81908c2ecf20Sopenharmony_ci				continue;
81918c2ecf20Sopenharmony_ci
81928c2ecf20Sopenharmony_ci			if (sctp_bind_addr_conflict(&ep2->base.bind_addr,
81938c2ecf20Sopenharmony_ci						    addr, sp2, sp)) {
81948c2ecf20Sopenharmony_ci				ret = 1;
81958c2ecf20Sopenharmony_ci				goto fail_unlock;
81968c2ecf20Sopenharmony_ci			}
81978c2ecf20Sopenharmony_ci		}
81988c2ecf20Sopenharmony_ci
81998c2ecf20Sopenharmony_ci		pr_debug("%s: found a match\n", __func__);
82008c2ecf20Sopenharmony_ci	}
82018c2ecf20Sopenharmony_cipp_not_found:
82028c2ecf20Sopenharmony_ci	/* If there was a hash table miss, create a new port.  */
82038c2ecf20Sopenharmony_ci	ret = 1;
82048c2ecf20Sopenharmony_ci	if (!pp && !(pp = sctp_bucket_create(head, net, snum)))
82058c2ecf20Sopenharmony_ci		goto fail_unlock;
82068c2ecf20Sopenharmony_ci
82078c2ecf20Sopenharmony_ci	/* In either case (hit or miss), make sure fastreuse is 1 only
82088c2ecf20Sopenharmony_ci	 * if sk->sk_reuse is too (that is, if the caller requested
82098c2ecf20Sopenharmony_ci	 * SO_REUSEADDR on this socket -sk-).
82108c2ecf20Sopenharmony_ci	 */
82118c2ecf20Sopenharmony_ci	if (hlist_empty(&pp->owner)) {
82128c2ecf20Sopenharmony_ci		if (reuse && sk->sk_state != SCTP_SS_LISTENING)
82138c2ecf20Sopenharmony_ci			pp->fastreuse = 1;
82148c2ecf20Sopenharmony_ci		else
82158c2ecf20Sopenharmony_ci			pp->fastreuse = 0;
82168c2ecf20Sopenharmony_ci
82178c2ecf20Sopenharmony_ci		if (sk->sk_reuseport) {
82188c2ecf20Sopenharmony_ci			pp->fastreuseport = 1;
82198c2ecf20Sopenharmony_ci			pp->fastuid = uid;
82208c2ecf20Sopenharmony_ci		} else {
82218c2ecf20Sopenharmony_ci			pp->fastreuseport = 0;
82228c2ecf20Sopenharmony_ci		}
82238c2ecf20Sopenharmony_ci	} else {
82248c2ecf20Sopenharmony_ci		if (pp->fastreuse &&
82258c2ecf20Sopenharmony_ci		    (!reuse || sk->sk_state == SCTP_SS_LISTENING))
82268c2ecf20Sopenharmony_ci			pp->fastreuse = 0;
82278c2ecf20Sopenharmony_ci
82288c2ecf20Sopenharmony_ci		if (pp->fastreuseport &&
82298c2ecf20Sopenharmony_ci		    (!sk->sk_reuseport || !uid_eq(pp->fastuid, uid)))
82308c2ecf20Sopenharmony_ci			pp->fastreuseport = 0;
82318c2ecf20Sopenharmony_ci	}
82328c2ecf20Sopenharmony_ci
82338c2ecf20Sopenharmony_ci	/* We are set, so fill up all the data in the hash table
82348c2ecf20Sopenharmony_ci	 * entry, tie the socket list information with the rest of the
82358c2ecf20Sopenharmony_ci	 * sockets FIXME: Blurry, NPI (ipg).
82368c2ecf20Sopenharmony_ci	 */
82378c2ecf20Sopenharmony_cisuccess:
82388c2ecf20Sopenharmony_ci	if (!sp->bind_hash) {
82398c2ecf20Sopenharmony_ci		inet_sk(sk)->inet_num = snum;
82408c2ecf20Sopenharmony_ci		sk_add_bind_node(sk, &pp->owner);
82418c2ecf20Sopenharmony_ci		sp->bind_hash = pp;
82428c2ecf20Sopenharmony_ci	}
82438c2ecf20Sopenharmony_ci	ret = 0;
82448c2ecf20Sopenharmony_ci
82458c2ecf20Sopenharmony_cifail_unlock:
82468c2ecf20Sopenharmony_ci	spin_unlock_bh(&head->lock);
82478c2ecf20Sopenharmony_ci	return ret;
82488c2ecf20Sopenharmony_ci}
82498c2ecf20Sopenharmony_ci
82508c2ecf20Sopenharmony_ci/* Assign a 'snum' port to the socket.  If snum == 0, an ephemeral
82518c2ecf20Sopenharmony_ci * port is requested.
82528c2ecf20Sopenharmony_ci */
82538c2ecf20Sopenharmony_cistatic int sctp_get_port(struct sock *sk, unsigned short snum)
82548c2ecf20Sopenharmony_ci{
82558c2ecf20Sopenharmony_ci	union sctp_addr addr;
82568c2ecf20Sopenharmony_ci	struct sctp_af *af = sctp_sk(sk)->pf->af;
82578c2ecf20Sopenharmony_ci
82588c2ecf20Sopenharmony_ci	/* Set up a dummy address struct from the sk. */
82598c2ecf20Sopenharmony_ci	af->from_sk(&addr, sk);
82608c2ecf20Sopenharmony_ci	addr.v4.sin_port = htons(snum);
82618c2ecf20Sopenharmony_ci
82628c2ecf20Sopenharmony_ci	/* Note: sk->sk_num gets filled in if ephemeral port request. */
82638c2ecf20Sopenharmony_ci	return sctp_get_port_local(sk, &addr);
82648c2ecf20Sopenharmony_ci}
82658c2ecf20Sopenharmony_ci
82668c2ecf20Sopenharmony_ci/*
82678c2ecf20Sopenharmony_ci *  Move a socket to LISTENING state.
82688c2ecf20Sopenharmony_ci */
82698c2ecf20Sopenharmony_cistatic int sctp_listen_start(struct sock *sk, int backlog)
82708c2ecf20Sopenharmony_ci{
82718c2ecf20Sopenharmony_ci	struct sctp_sock *sp = sctp_sk(sk);
82728c2ecf20Sopenharmony_ci	struct sctp_endpoint *ep = sp->ep;
82738c2ecf20Sopenharmony_ci	struct crypto_shash *tfm = NULL;
82748c2ecf20Sopenharmony_ci	char alg[32];
82758c2ecf20Sopenharmony_ci
82768c2ecf20Sopenharmony_ci	/* Allocate HMAC for generating cookie. */
82778c2ecf20Sopenharmony_ci	if (!sp->hmac && sp->sctp_hmac_alg) {
82788c2ecf20Sopenharmony_ci		sprintf(alg, "hmac(%s)", sp->sctp_hmac_alg);
82798c2ecf20Sopenharmony_ci		tfm = crypto_alloc_shash(alg, 0, 0);
82808c2ecf20Sopenharmony_ci		if (IS_ERR(tfm)) {
82818c2ecf20Sopenharmony_ci			net_info_ratelimited("failed to load transform for %s: %ld\n",
82828c2ecf20Sopenharmony_ci					     sp->sctp_hmac_alg, PTR_ERR(tfm));
82838c2ecf20Sopenharmony_ci			return -ENOSYS;
82848c2ecf20Sopenharmony_ci		}
82858c2ecf20Sopenharmony_ci		sctp_sk(sk)->hmac = tfm;
82868c2ecf20Sopenharmony_ci	}
82878c2ecf20Sopenharmony_ci
82888c2ecf20Sopenharmony_ci	/*
82898c2ecf20Sopenharmony_ci	 * If a bind() or sctp_bindx() is not called prior to a listen()
82908c2ecf20Sopenharmony_ci	 * call that allows new associations to be accepted, the system
82918c2ecf20Sopenharmony_ci	 * picks an ephemeral port and will choose an address set equivalent
82928c2ecf20Sopenharmony_ci	 * to binding with a wildcard address.
82938c2ecf20Sopenharmony_ci	 *
82948c2ecf20Sopenharmony_ci	 * This is not currently spelled out in the SCTP sockets
82958c2ecf20Sopenharmony_ci	 * extensions draft, but follows the practice as seen in TCP
82968c2ecf20Sopenharmony_ci	 * sockets.
82978c2ecf20Sopenharmony_ci	 *
82988c2ecf20Sopenharmony_ci	 */
82998c2ecf20Sopenharmony_ci	inet_sk_set_state(sk, SCTP_SS_LISTENING);
83008c2ecf20Sopenharmony_ci	if (!ep->base.bind_addr.port) {
83018c2ecf20Sopenharmony_ci		if (sctp_autobind(sk))
83028c2ecf20Sopenharmony_ci			return -EAGAIN;
83038c2ecf20Sopenharmony_ci	} else {
83048c2ecf20Sopenharmony_ci		if (sctp_get_port(sk, inet_sk(sk)->inet_num)) {
83058c2ecf20Sopenharmony_ci			inet_sk_set_state(sk, SCTP_SS_CLOSED);
83068c2ecf20Sopenharmony_ci			return -EADDRINUSE;
83078c2ecf20Sopenharmony_ci		}
83088c2ecf20Sopenharmony_ci	}
83098c2ecf20Sopenharmony_ci
83108c2ecf20Sopenharmony_ci	WRITE_ONCE(sk->sk_max_ack_backlog, backlog);
83118c2ecf20Sopenharmony_ci	return sctp_hash_endpoint(ep);
83128c2ecf20Sopenharmony_ci}
83138c2ecf20Sopenharmony_ci
83148c2ecf20Sopenharmony_ci/*
83158c2ecf20Sopenharmony_ci * 4.1.3 / 5.1.3 listen()
83168c2ecf20Sopenharmony_ci *
83178c2ecf20Sopenharmony_ci *   By default, new associations are not accepted for UDP style sockets.
83188c2ecf20Sopenharmony_ci *   An application uses listen() to mark a socket as being able to
83198c2ecf20Sopenharmony_ci *   accept new associations.
83208c2ecf20Sopenharmony_ci *
83218c2ecf20Sopenharmony_ci *   On TCP style sockets, applications use listen() to ready the SCTP
83228c2ecf20Sopenharmony_ci *   endpoint for accepting inbound associations.
83238c2ecf20Sopenharmony_ci *
83248c2ecf20Sopenharmony_ci *   On both types of endpoints a backlog of '0' disables listening.
83258c2ecf20Sopenharmony_ci *
83268c2ecf20Sopenharmony_ci *  Move a socket to LISTENING state.
83278c2ecf20Sopenharmony_ci */
83288c2ecf20Sopenharmony_ciint sctp_inet_listen(struct socket *sock, int backlog)
83298c2ecf20Sopenharmony_ci{
83308c2ecf20Sopenharmony_ci	struct sock *sk = sock->sk;
83318c2ecf20Sopenharmony_ci	struct sctp_endpoint *ep = sctp_sk(sk)->ep;
83328c2ecf20Sopenharmony_ci	int err = -EINVAL;
83338c2ecf20Sopenharmony_ci
83348c2ecf20Sopenharmony_ci	if (unlikely(backlog < 0))
83358c2ecf20Sopenharmony_ci		return err;
83368c2ecf20Sopenharmony_ci
83378c2ecf20Sopenharmony_ci	lock_sock(sk);
83388c2ecf20Sopenharmony_ci
83398c2ecf20Sopenharmony_ci	/* Peeled-off sockets are not allowed to listen().  */
83408c2ecf20Sopenharmony_ci	if (sctp_style(sk, UDP_HIGH_BANDWIDTH))
83418c2ecf20Sopenharmony_ci		goto out;
83428c2ecf20Sopenharmony_ci
83438c2ecf20Sopenharmony_ci	if (sock->state != SS_UNCONNECTED)
83448c2ecf20Sopenharmony_ci		goto out;
83458c2ecf20Sopenharmony_ci
83468c2ecf20Sopenharmony_ci	if (!sctp_sstate(sk, LISTENING) && !sctp_sstate(sk, CLOSED))
83478c2ecf20Sopenharmony_ci		goto out;
83488c2ecf20Sopenharmony_ci
83498c2ecf20Sopenharmony_ci	/* If backlog is zero, disable listening. */
83508c2ecf20Sopenharmony_ci	if (!backlog) {
83518c2ecf20Sopenharmony_ci		if (sctp_sstate(sk, CLOSED))
83528c2ecf20Sopenharmony_ci			goto out;
83538c2ecf20Sopenharmony_ci
83548c2ecf20Sopenharmony_ci		err = 0;
83558c2ecf20Sopenharmony_ci		sctp_unhash_endpoint(ep);
83568c2ecf20Sopenharmony_ci		sk->sk_state = SCTP_SS_CLOSED;
83578c2ecf20Sopenharmony_ci		if (sk->sk_reuse || sctp_sk(sk)->reuse)
83588c2ecf20Sopenharmony_ci			sctp_sk(sk)->bind_hash->fastreuse = 1;
83598c2ecf20Sopenharmony_ci		goto out;
83608c2ecf20Sopenharmony_ci	}
83618c2ecf20Sopenharmony_ci
83628c2ecf20Sopenharmony_ci	/* If we are already listening, just update the backlog */
83638c2ecf20Sopenharmony_ci	if (sctp_sstate(sk, LISTENING))
83648c2ecf20Sopenharmony_ci		WRITE_ONCE(sk->sk_max_ack_backlog, backlog);
83658c2ecf20Sopenharmony_ci	else {
83668c2ecf20Sopenharmony_ci		err = sctp_listen_start(sk, backlog);
83678c2ecf20Sopenharmony_ci		if (err)
83688c2ecf20Sopenharmony_ci			goto out;
83698c2ecf20Sopenharmony_ci	}
83708c2ecf20Sopenharmony_ci
83718c2ecf20Sopenharmony_ci	err = 0;
83728c2ecf20Sopenharmony_ciout:
83738c2ecf20Sopenharmony_ci	release_sock(sk);
83748c2ecf20Sopenharmony_ci	return err;
83758c2ecf20Sopenharmony_ci}
83768c2ecf20Sopenharmony_ci
83778c2ecf20Sopenharmony_ci/*
83788c2ecf20Sopenharmony_ci * This function is done by modeling the current datagram_poll() and the
83798c2ecf20Sopenharmony_ci * tcp_poll().  Note that, based on these implementations, we don't
83808c2ecf20Sopenharmony_ci * lock the socket in this function, even though it seems that,
83818c2ecf20Sopenharmony_ci * ideally, locking or some other mechanisms can be used to ensure
83828c2ecf20Sopenharmony_ci * the integrity of the counters (sndbuf and wmem_alloc) used
83838c2ecf20Sopenharmony_ci * in this place.  We assume that we don't need locks either until proven
83848c2ecf20Sopenharmony_ci * otherwise.
83858c2ecf20Sopenharmony_ci *
83868c2ecf20Sopenharmony_ci * Another thing to note is that we include the Async I/O support
83878c2ecf20Sopenharmony_ci * here, again, by modeling the current TCP/UDP code.  We don't have
83888c2ecf20Sopenharmony_ci * a good way to test with it yet.
83898c2ecf20Sopenharmony_ci */
83908c2ecf20Sopenharmony_ci__poll_t sctp_poll(struct file *file, struct socket *sock, poll_table *wait)
83918c2ecf20Sopenharmony_ci{
83928c2ecf20Sopenharmony_ci	struct sock *sk = sock->sk;
83938c2ecf20Sopenharmony_ci	struct sctp_sock *sp = sctp_sk(sk);
83948c2ecf20Sopenharmony_ci	__poll_t mask;
83958c2ecf20Sopenharmony_ci
83968c2ecf20Sopenharmony_ci	poll_wait(file, sk_sleep(sk), wait);
83978c2ecf20Sopenharmony_ci
83988c2ecf20Sopenharmony_ci	sock_rps_record_flow(sk);
83998c2ecf20Sopenharmony_ci
84008c2ecf20Sopenharmony_ci	/* A TCP-style listening socket becomes readable when the accept queue
84018c2ecf20Sopenharmony_ci	 * is not empty.
84028c2ecf20Sopenharmony_ci	 */
84038c2ecf20Sopenharmony_ci	if (sctp_style(sk, TCP) && sctp_sstate(sk, LISTENING))
84048c2ecf20Sopenharmony_ci		return (!list_empty(&sp->ep->asocs)) ?
84058c2ecf20Sopenharmony_ci			(EPOLLIN | EPOLLRDNORM) : 0;
84068c2ecf20Sopenharmony_ci
84078c2ecf20Sopenharmony_ci	mask = 0;
84088c2ecf20Sopenharmony_ci
84098c2ecf20Sopenharmony_ci	/* Is there any exceptional events?  */
84108c2ecf20Sopenharmony_ci	if (sk->sk_err || !skb_queue_empty_lockless(&sk->sk_error_queue))
84118c2ecf20Sopenharmony_ci		mask |= EPOLLERR |
84128c2ecf20Sopenharmony_ci			(sock_flag(sk, SOCK_SELECT_ERR_QUEUE) ? EPOLLPRI : 0);
84138c2ecf20Sopenharmony_ci	if (sk->sk_shutdown & RCV_SHUTDOWN)
84148c2ecf20Sopenharmony_ci		mask |= EPOLLRDHUP | EPOLLIN | EPOLLRDNORM;
84158c2ecf20Sopenharmony_ci	if (sk->sk_shutdown == SHUTDOWN_MASK)
84168c2ecf20Sopenharmony_ci		mask |= EPOLLHUP;
84178c2ecf20Sopenharmony_ci
84188c2ecf20Sopenharmony_ci	/* Is it readable?  Reconsider this code with TCP-style support.  */
84198c2ecf20Sopenharmony_ci	if (!skb_queue_empty_lockless(&sk->sk_receive_queue))
84208c2ecf20Sopenharmony_ci		mask |= EPOLLIN | EPOLLRDNORM;
84218c2ecf20Sopenharmony_ci
84228c2ecf20Sopenharmony_ci	/* The association is either gone or not ready.  */
84238c2ecf20Sopenharmony_ci	if (!sctp_style(sk, UDP) && sctp_sstate(sk, CLOSED))
84248c2ecf20Sopenharmony_ci		return mask;
84258c2ecf20Sopenharmony_ci
84268c2ecf20Sopenharmony_ci	/* Is it writable?  */
84278c2ecf20Sopenharmony_ci	if (sctp_writeable(sk)) {
84288c2ecf20Sopenharmony_ci		mask |= EPOLLOUT | EPOLLWRNORM;
84298c2ecf20Sopenharmony_ci	} else {
84308c2ecf20Sopenharmony_ci		sk_set_bit(SOCKWQ_ASYNC_NOSPACE, sk);
84318c2ecf20Sopenharmony_ci		/*
84328c2ecf20Sopenharmony_ci		 * Since the socket is not locked, the buffer
84338c2ecf20Sopenharmony_ci		 * might be made available after the writeable check and
84348c2ecf20Sopenharmony_ci		 * before the bit is set.  This could cause a lost I/O
84358c2ecf20Sopenharmony_ci		 * signal.  tcp_poll() has a race breaker for this race
84368c2ecf20Sopenharmony_ci		 * condition.  Based on their implementation, we put
84378c2ecf20Sopenharmony_ci		 * in the following code to cover it as well.
84388c2ecf20Sopenharmony_ci		 */
84398c2ecf20Sopenharmony_ci		if (sctp_writeable(sk))
84408c2ecf20Sopenharmony_ci			mask |= EPOLLOUT | EPOLLWRNORM;
84418c2ecf20Sopenharmony_ci	}
84428c2ecf20Sopenharmony_ci	return mask;
84438c2ecf20Sopenharmony_ci}
84448c2ecf20Sopenharmony_ci
84458c2ecf20Sopenharmony_ci/********************************************************************
84468c2ecf20Sopenharmony_ci * 2nd Level Abstractions
84478c2ecf20Sopenharmony_ci ********************************************************************/
84488c2ecf20Sopenharmony_ci
84498c2ecf20Sopenharmony_cistatic struct sctp_bind_bucket *sctp_bucket_create(
84508c2ecf20Sopenharmony_ci	struct sctp_bind_hashbucket *head, struct net *net, unsigned short snum)
84518c2ecf20Sopenharmony_ci{
84528c2ecf20Sopenharmony_ci	struct sctp_bind_bucket *pp;
84538c2ecf20Sopenharmony_ci
84548c2ecf20Sopenharmony_ci	pp = kmem_cache_alloc(sctp_bucket_cachep, GFP_ATOMIC);
84558c2ecf20Sopenharmony_ci	if (pp) {
84568c2ecf20Sopenharmony_ci		SCTP_DBG_OBJCNT_INC(bind_bucket);
84578c2ecf20Sopenharmony_ci		pp->port = snum;
84588c2ecf20Sopenharmony_ci		pp->fastreuse = 0;
84598c2ecf20Sopenharmony_ci		INIT_HLIST_HEAD(&pp->owner);
84608c2ecf20Sopenharmony_ci		pp->net = net;
84618c2ecf20Sopenharmony_ci		hlist_add_head(&pp->node, &head->chain);
84628c2ecf20Sopenharmony_ci	}
84638c2ecf20Sopenharmony_ci	return pp;
84648c2ecf20Sopenharmony_ci}
84658c2ecf20Sopenharmony_ci
84668c2ecf20Sopenharmony_ci/* Caller must hold hashbucket lock for this tb with local BH disabled */
84678c2ecf20Sopenharmony_cistatic void sctp_bucket_destroy(struct sctp_bind_bucket *pp)
84688c2ecf20Sopenharmony_ci{
84698c2ecf20Sopenharmony_ci	if (pp && hlist_empty(&pp->owner)) {
84708c2ecf20Sopenharmony_ci		__hlist_del(&pp->node);
84718c2ecf20Sopenharmony_ci		kmem_cache_free(sctp_bucket_cachep, pp);
84728c2ecf20Sopenharmony_ci		SCTP_DBG_OBJCNT_DEC(bind_bucket);
84738c2ecf20Sopenharmony_ci	}
84748c2ecf20Sopenharmony_ci}
84758c2ecf20Sopenharmony_ci
84768c2ecf20Sopenharmony_ci/* Release this socket's reference to a local port.  */
84778c2ecf20Sopenharmony_cistatic inline void __sctp_put_port(struct sock *sk)
84788c2ecf20Sopenharmony_ci{
84798c2ecf20Sopenharmony_ci	struct sctp_bind_hashbucket *head =
84808c2ecf20Sopenharmony_ci		&sctp_port_hashtable[sctp_phashfn(sock_net(sk),
84818c2ecf20Sopenharmony_ci						  inet_sk(sk)->inet_num)];
84828c2ecf20Sopenharmony_ci	struct sctp_bind_bucket *pp;
84838c2ecf20Sopenharmony_ci
84848c2ecf20Sopenharmony_ci	spin_lock(&head->lock);
84858c2ecf20Sopenharmony_ci	pp = sctp_sk(sk)->bind_hash;
84868c2ecf20Sopenharmony_ci	__sk_del_bind_node(sk);
84878c2ecf20Sopenharmony_ci	sctp_sk(sk)->bind_hash = NULL;
84888c2ecf20Sopenharmony_ci	inet_sk(sk)->inet_num = 0;
84898c2ecf20Sopenharmony_ci	sctp_bucket_destroy(pp);
84908c2ecf20Sopenharmony_ci	spin_unlock(&head->lock);
84918c2ecf20Sopenharmony_ci}
84928c2ecf20Sopenharmony_ci
84938c2ecf20Sopenharmony_civoid sctp_put_port(struct sock *sk)
84948c2ecf20Sopenharmony_ci{
84958c2ecf20Sopenharmony_ci	local_bh_disable();
84968c2ecf20Sopenharmony_ci	__sctp_put_port(sk);
84978c2ecf20Sopenharmony_ci	local_bh_enable();
84988c2ecf20Sopenharmony_ci}
84998c2ecf20Sopenharmony_ci
85008c2ecf20Sopenharmony_ci/*
85018c2ecf20Sopenharmony_ci * The system picks an ephemeral port and choose an address set equivalent
85028c2ecf20Sopenharmony_ci * to binding with a wildcard address.
85038c2ecf20Sopenharmony_ci * One of those addresses will be the primary address for the association.
85048c2ecf20Sopenharmony_ci * This automatically enables the multihoming capability of SCTP.
85058c2ecf20Sopenharmony_ci */
85068c2ecf20Sopenharmony_cistatic int sctp_autobind(struct sock *sk)
85078c2ecf20Sopenharmony_ci{
85088c2ecf20Sopenharmony_ci	union sctp_addr autoaddr;
85098c2ecf20Sopenharmony_ci	struct sctp_af *af;
85108c2ecf20Sopenharmony_ci	__be16 port;
85118c2ecf20Sopenharmony_ci
85128c2ecf20Sopenharmony_ci	/* Initialize a local sockaddr structure to INADDR_ANY. */
85138c2ecf20Sopenharmony_ci	af = sctp_sk(sk)->pf->af;
85148c2ecf20Sopenharmony_ci
85158c2ecf20Sopenharmony_ci	port = htons(inet_sk(sk)->inet_num);
85168c2ecf20Sopenharmony_ci	af->inaddr_any(&autoaddr, port);
85178c2ecf20Sopenharmony_ci
85188c2ecf20Sopenharmony_ci	return sctp_do_bind(sk, &autoaddr, af->sockaddr_len);
85198c2ecf20Sopenharmony_ci}
85208c2ecf20Sopenharmony_ci
85218c2ecf20Sopenharmony_ci/* Parse out IPPROTO_SCTP CMSG headers.  Perform only minimal validation.
85228c2ecf20Sopenharmony_ci *
85238c2ecf20Sopenharmony_ci * From RFC 2292
85248c2ecf20Sopenharmony_ci * 4.2 The cmsghdr Structure *
85258c2ecf20Sopenharmony_ci *
85268c2ecf20Sopenharmony_ci * When ancillary data is sent or received, any number of ancillary data
85278c2ecf20Sopenharmony_ci * objects can be specified by the msg_control and msg_controllen members of
85288c2ecf20Sopenharmony_ci * the msghdr structure, because each object is preceded by
85298c2ecf20Sopenharmony_ci * a cmsghdr structure defining the object's length (the cmsg_len member).
85308c2ecf20Sopenharmony_ci * Historically Berkeley-derived implementations have passed only one object
85318c2ecf20Sopenharmony_ci * at a time, but this API allows multiple objects to be
85328c2ecf20Sopenharmony_ci * passed in a single call to sendmsg() or recvmsg(). The following example
85338c2ecf20Sopenharmony_ci * shows two ancillary data objects in a control buffer.
85348c2ecf20Sopenharmony_ci *
85358c2ecf20Sopenharmony_ci *   |<--------------------------- msg_controllen -------------------------->|
85368c2ecf20Sopenharmony_ci *   |                                                                       |
85378c2ecf20Sopenharmony_ci *
85388c2ecf20Sopenharmony_ci *   |<----- ancillary data object ----->|<----- ancillary data object ----->|
85398c2ecf20Sopenharmony_ci *
85408c2ecf20Sopenharmony_ci *   |<---------- CMSG_SPACE() --------->|<---------- CMSG_SPACE() --------->|
85418c2ecf20Sopenharmony_ci *   |                                   |                                   |
85428c2ecf20Sopenharmony_ci *
85438c2ecf20Sopenharmony_ci *   |<---------- cmsg_len ---------->|  |<--------- cmsg_len ----------->|  |
85448c2ecf20Sopenharmony_ci *
85458c2ecf20Sopenharmony_ci *   |<--------- CMSG_LEN() --------->|  |<-------- CMSG_LEN() ---------->|  |
85468c2ecf20Sopenharmony_ci *   |                                |  |                                |  |
85478c2ecf20Sopenharmony_ci *
85488c2ecf20Sopenharmony_ci *   +-----+-----+-----+--+-----------+--+-----+-----+-----+--+-----------+--+
85498c2ecf20Sopenharmony_ci *   |cmsg_|cmsg_|cmsg_|XX|           |XX|cmsg_|cmsg_|cmsg_|XX|           |XX|
85508c2ecf20Sopenharmony_ci *
85518c2ecf20Sopenharmony_ci *   |len  |level|type |XX|cmsg_data[]|XX|len  |level|type |XX|cmsg_data[]|XX|
85528c2ecf20Sopenharmony_ci *
85538c2ecf20Sopenharmony_ci *   +-----+-----+-----+--+-----------+--+-----+-----+-----+--+-----------+--+
85548c2ecf20Sopenharmony_ci *    ^
85558c2ecf20Sopenharmony_ci *    |
85568c2ecf20Sopenharmony_ci *
85578c2ecf20Sopenharmony_ci * msg_control
85588c2ecf20Sopenharmony_ci * points here
85598c2ecf20Sopenharmony_ci */
85608c2ecf20Sopenharmony_cistatic int sctp_msghdr_parse(const struct msghdr *msg, struct sctp_cmsgs *cmsgs)
85618c2ecf20Sopenharmony_ci{
85628c2ecf20Sopenharmony_ci	struct msghdr *my_msg = (struct msghdr *)msg;
85638c2ecf20Sopenharmony_ci	struct cmsghdr *cmsg;
85648c2ecf20Sopenharmony_ci
85658c2ecf20Sopenharmony_ci	for_each_cmsghdr(cmsg, my_msg) {
85668c2ecf20Sopenharmony_ci		if (!CMSG_OK(my_msg, cmsg))
85678c2ecf20Sopenharmony_ci			return -EINVAL;
85688c2ecf20Sopenharmony_ci
85698c2ecf20Sopenharmony_ci		/* Should we parse this header or ignore?  */
85708c2ecf20Sopenharmony_ci		if (cmsg->cmsg_level != IPPROTO_SCTP)
85718c2ecf20Sopenharmony_ci			continue;
85728c2ecf20Sopenharmony_ci
85738c2ecf20Sopenharmony_ci		/* Strictly check lengths following example in SCM code.  */
85748c2ecf20Sopenharmony_ci		switch (cmsg->cmsg_type) {
85758c2ecf20Sopenharmony_ci		case SCTP_INIT:
85768c2ecf20Sopenharmony_ci			/* SCTP Socket API Extension
85778c2ecf20Sopenharmony_ci			 * 5.3.1 SCTP Initiation Structure (SCTP_INIT)
85788c2ecf20Sopenharmony_ci			 *
85798c2ecf20Sopenharmony_ci			 * This cmsghdr structure provides information for
85808c2ecf20Sopenharmony_ci			 * initializing new SCTP associations with sendmsg().
85818c2ecf20Sopenharmony_ci			 * The SCTP_INITMSG socket option uses this same data
85828c2ecf20Sopenharmony_ci			 * structure.  This structure is not used for
85838c2ecf20Sopenharmony_ci			 * recvmsg().
85848c2ecf20Sopenharmony_ci			 *
85858c2ecf20Sopenharmony_ci			 * cmsg_level    cmsg_type      cmsg_data[]
85868c2ecf20Sopenharmony_ci			 * ------------  ------------   ----------------------
85878c2ecf20Sopenharmony_ci			 * IPPROTO_SCTP  SCTP_INIT      struct sctp_initmsg
85888c2ecf20Sopenharmony_ci			 */
85898c2ecf20Sopenharmony_ci			if (cmsg->cmsg_len != CMSG_LEN(sizeof(struct sctp_initmsg)))
85908c2ecf20Sopenharmony_ci				return -EINVAL;
85918c2ecf20Sopenharmony_ci
85928c2ecf20Sopenharmony_ci			cmsgs->init = CMSG_DATA(cmsg);
85938c2ecf20Sopenharmony_ci			break;
85948c2ecf20Sopenharmony_ci
85958c2ecf20Sopenharmony_ci		case SCTP_SNDRCV:
85968c2ecf20Sopenharmony_ci			/* SCTP Socket API Extension
85978c2ecf20Sopenharmony_ci			 * 5.3.2 SCTP Header Information Structure(SCTP_SNDRCV)
85988c2ecf20Sopenharmony_ci			 *
85998c2ecf20Sopenharmony_ci			 * This cmsghdr structure specifies SCTP options for
86008c2ecf20Sopenharmony_ci			 * sendmsg() and describes SCTP header information
86018c2ecf20Sopenharmony_ci			 * about a received message through recvmsg().
86028c2ecf20Sopenharmony_ci			 *
86038c2ecf20Sopenharmony_ci			 * cmsg_level    cmsg_type      cmsg_data[]
86048c2ecf20Sopenharmony_ci			 * ------------  ------------   ----------------------
86058c2ecf20Sopenharmony_ci			 * IPPROTO_SCTP  SCTP_SNDRCV    struct sctp_sndrcvinfo
86068c2ecf20Sopenharmony_ci			 */
86078c2ecf20Sopenharmony_ci			if (cmsg->cmsg_len != CMSG_LEN(sizeof(struct sctp_sndrcvinfo)))
86088c2ecf20Sopenharmony_ci				return -EINVAL;
86098c2ecf20Sopenharmony_ci
86108c2ecf20Sopenharmony_ci			cmsgs->srinfo = CMSG_DATA(cmsg);
86118c2ecf20Sopenharmony_ci
86128c2ecf20Sopenharmony_ci			if (cmsgs->srinfo->sinfo_flags &
86138c2ecf20Sopenharmony_ci			    ~(SCTP_UNORDERED | SCTP_ADDR_OVER |
86148c2ecf20Sopenharmony_ci			      SCTP_SACK_IMMEDIATELY | SCTP_SENDALL |
86158c2ecf20Sopenharmony_ci			      SCTP_PR_SCTP_MASK | SCTP_ABORT | SCTP_EOF))
86168c2ecf20Sopenharmony_ci				return -EINVAL;
86178c2ecf20Sopenharmony_ci			break;
86188c2ecf20Sopenharmony_ci
86198c2ecf20Sopenharmony_ci		case SCTP_SNDINFO:
86208c2ecf20Sopenharmony_ci			/* SCTP Socket API Extension
86218c2ecf20Sopenharmony_ci			 * 5.3.4 SCTP Send Information Structure (SCTP_SNDINFO)
86228c2ecf20Sopenharmony_ci			 *
86238c2ecf20Sopenharmony_ci			 * This cmsghdr structure specifies SCTP options for
86248c2ecf20Sopenharmony_ci			 * sendmsg(). This structure and SCTP_RCVINFO replaces
86258c2ecf20Sopenharmony_ci			 * SCTP_SNDRCV which has been deprecated.
86268c2ecf20Sopenharmony_ci			 *
86278c2ecf20Sopenharmony_ci			 * cmsg_level    cmsg_type      cmsg_data[]
86288c2ecf20Sopenharmony_ci			 * ------------  ------------   ---------------------
86298c2ecf20Sopenharmony_ci			 * IPPROTO_SCTP  SCTP_SNDINFO    struct sctp_sndinfo
86308c2ecf20Sopenharmony_ci			 */
86318c2ecf20Sopenharmony_ci			if (cmsg->cmsg_len != CMSG_LEN(sizeof(struct sctp_sndinfo)))
86328c2ecf20Sopenharmony_ci				return -EINVAL;
86338c2ecf20Sopenharmony_ci
86348c2ecf20Sopenharmony_ci			cmsgs->sinfo = CMSG_DATA(cmsg);
86358c2ecf20Sopenharmony_ci
86368c2ecf20Sopenharmony_ci			if (cmsgs->sinfo->snd_flags &
86378c2ecf20Sopenharmony_ci			    ~(SCTP_UNORDERED | SCTP_ADDR_OVER |
86388c2ecf20Sopenharmony_ci			      SCTP_SACK_IMMEDIATELY | SCTP_SENDALL |
86398c2ecf20Sopenharmony_ci			      SCTP_PR_SCTP_MASK | SCTP_ABORT | SCTP_EOF))
86408c2ecf20Sopenharmony_ci				return -EINVAL;
86418c2ecf20Sopenharmony_ci			break;
86428c2ecf20Sopenharmony_ci		case SCTP_PRINFO:
86438c2ecf20Sopenharmony_ci			/* SCTP Socket API Extension
86448c2ecf20Sopenharmony_ci			 * 5.3.7 SCTP PR-SCTP Information Structure (SCTP_PRINFO)
86458c2ecf20Sopenharmony_ci			 *
86468c2ecf20Sopenharmony_ci			 * This cmsghdr structure specifies SCTP options for sendmsg().
86478c2ecf20Sopenharmony_ci			 *
86488c2ecf20Sopenharmony_ci			 * cmsg_level    cmsg_type      cmsg_data[]
86498c2ecf20Sopenharmony_ci			 * ------------  ------------   ---------------------
86508c2ecf20Sopenharmony_ci			 * IPPROTO_SCTP  SCTP_PRINFO    struct sctp_prinfo
86518c2ecf20Sopenharmony_ci			 */
86528c2ecf20Sopenharmony_ci			if (cmsg->cmsg_len != CMSG_LEN(sizeof(struct sctp_prinfo)))
86538c2ecf20Sopenharmony_ci				return -EINVAL;
86548c2ecf20Sopenharmony_ci
86558c2ecf20Sopenharmony_ci			cmsgs->prinfo = CMSG_DATA(cmsg);
86568c2ecf20Sopenharmony_ci			if (cmsgs->prinfo->pr_policy & ~SCTP_PR_SCTP_MASK)
86578c2ecf20Sopenharmony_ci				return -EINVAL;
86588c2ecf20Sopenharmony_ci
86598c2ecf20Sopenharmony_ci			if (cmsgs->prinfo->pr_policy == SCTP_PR_SCTP_NONE)
86608c2ecf20Sopenharmony_ci				cmsgs->prinfo->pr_value = 0;
86618c2ecf20Sopenharmony_ci			break;
86628c2ecf20Sopenharmony_ci		case SCTP_AUTHINFO:
86638c2ecf20Sopenharmony_ci			/* SCTP Socket API Extension
86648c2ecf20Sopenharmony_ci			 * 5.3.8 SCTP AUTH Information Structure (SCTP_AUTHINFO)
86658c2ecf20Sopenharmony_ci			 *
86668c2ecf20Sopenharmony_ci			 * This cmsghdr structure specifies SCTP options for sendmsg().
86678c2ecf20Sopenharmony_ci			 *
86688c2ecf20Sopenharmony_ci			 * cmsg_level    cmsg_type      cmsg_data[]
86698c2ecf20Sopenharmony_ci			 * ------------  ------------   ---------------------
86708c2ecf20Sopenharmony_ci			 * IPPROTO_SCTP  SCTP_AUTHINFO  struct sctp_authinfo
86718c2ecf20Sopenharmony_ci			 */
86728c2ecf20Sopenharmony_ci			if (cmsg->cmsg_len != CMSG_LEN(sizeof(struct sctp_authinfo)))
86738c2ecf20Sopenharmony_ci				return -EINVAL;
86748c2ecf20Sopenharmony_ci
86758c2ecf20Sopenharmony_ci			cmsgs->authinfo = CMSG_DATA(cmsg);
86768c2ecf20Sopenharmony_ci			break;
86778c2ecf20Sopenharmony_ci		case SCTP_DSTADDRV4:
86788c2ecf20Sopenharmony_ci		case SCTP_DSTADDRV6:
86798c2ecf20Sopenharmony_ci			/* SCTP Socket API Extension
86808c2ecf20Sopenharmony_ci			 * 5.3.9/10 SCTP Destination IPv4/6 Address Structure (SCTP_DSTADDRV4/6)
86818c2ecf20Sopenharmony_ci			 *
86828c2ecf20Sopenharmony_ci			 * This cmsghdr structure specifies SCTP options for sendmsg().
86838c2ecf20Sopenharmony_ci			 *
86848c2ecf20Sopenharmony_ci			 * cmsg_level    cmsg_type         cmsg_data[]
86858c2ecf20Sopenharmony_ci			 * ------------  ------------   ---------------------
86868c2ecf20Sopenharmony_ci			 * IPPROTO_SCTP  SCTP_DSTADDRV4 struct in_addr
86878c2ecf20Sopenharmony_ci			 * ------------  ------------   ---------------------
86888c2ecf20Sopenharmony_ci			 * IPPROTO_SCTP  SCTP_DSTADDRV6 struct in6_addr
86898c2ecf20Sopenharmony_ci			 */
86908c2ecf20Sopenharmony_ci			cmsgs->addrs_msg = my_msg;
86918c2ecf20Sopenharmony_ci			break;
86928c2ecf20Sopenharmony_ci		default:
86938c2ecf20Sopenharmony_ci			return -EINVAL;
86948c2ecf20Sopenharmony_ci		}
86958c2ecf20Sopenharmony_ci	}
86968c2ecf20Sopenharmony_ci
86978c2ecf20Sopenharmony_ci	return 0;
86988c2ecf20Sopenharmony_ci}
86998c2ecf20Sopenharmony_ci
87008c2ecf20Sopenharmony_ci/*
87018c2ecf20Sopenharmony_ci * Wait for a packet..
87028c2ecf20Sopenharmony_ci * Note: This function is the same function as in core/datagram.c
87038c2ecf20Sopenharmony_ci * with a few modifications to make lksctp work.
87048c2ecf20Sopenharmony_ci */
87058c2ecf20Sopenharmony_cistatic int sctp_wait_for_packet(struct sock *sk, int *err, long *timeo_p)
87068c2ecf20Sopenharmony_ci{
87078c2ecf20Sopenharmony_ci	int error;
87088c2ecf20Sopenharmony_ci	DEFINE_WAIT(wait);
87098c2ecf20Sopenharmony_ci
87108c2ecf20Sopenharmony_ci	prepare_to_wait_exclusive(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE);
87118c2ecf20Sopenharmony_ci
87128c2ecf20Sopenharmony_ci	/* Socket errors? */
87138c2ecf20Sopenharmony_ci	error = sock_error(sk);
87148c2ecf20Sopenharmony_ci	if (error)
87158c2ecf20Sopenharmony_ci		goto out;
87168c2ecf20Sopenharmony_ci
87178c2ecf20Sopenharmony_ci	if (!skb_queue_empty(&sk->sk_receive_queue))
87188c2ecf20Sopenharmony_ci		goto ready;
87198c2ecf20Sopenharmony_ci
87208c2ecf20Sopenharmony_ci	/* Socket shut down?  */
87218c2ecf20Sopenharmony_ci	if (sk->sk_shutdown & RCV_SHUTDOWN)
87228c2ecf20Sopenharmony_ci		goto out;
87238c2ecf20Sopenharmony_ci
87248c2ecf20Sopenharmony_ci	/* Sequenced packets can come disconnected.  If so we report the
87258c2ecf20Sopenharmony_ci	 * problem.
87268c2ecf20Sopenharmony_ci	 */
87278c2ecf20Sopenharmony_ci	error = -ENOTCONN;
87288c2ecf20Sopenharmony_ci
87298c2ecf20Sopenharmony_ci	/* Is there a good reason to think that we may receive some data?  */
87308c2ecf20Sopenharmony_ci	if (list_empty(&sctp_sk(sk)->ep->asocs) && !sctp_sstate(sk, LISTENING))
87318c2ecf20Sopenharmony_ci		goto out;
87328c2ecf20Sopenharmony_ci
87338c2ecf20Sopenharmony_ci	/* Handle signals.  */
87348c2ecf20Sopenharmony_ci	if (signal_pending(current))
87358c2ecf20Sopenharmony_ci		goto interrupted;
87368c2ecf20Sopenharmony_ci
87378c2ecf20Sopenharmony_ci	/* Let another process have a go.  Since we are going to sleep
87388c2ecf20Sopenharmony_ci	 * anyway.  Note: This may cause odd behaviors if the message
87398c2ecf20Sopenharmony_ci	 * does not fit in the user's buffer, but this seems to be the
87408c2ecf20Sopenharmony_ci	 * only way to honor MSG_DONTWAIT realistically.
87418c2ecf20Sopenharmony_ci	 */
87428c2ecf20Sopenharmony_ci	release_sock(sk);
87438c2ecf20Sopenharmony_ci	*timeo_p = schedule_timeout(*timeo_p);
87448c2ecf20Sopenharmony_ci	lock_sock(sk);
87458c2ecf20Sopenharmony_ci
87468c2ecf20Sopenharmony_ciready:
87478c2ecf20Sopenharmony_ci	finish_wait(sk_sleep(sk), &wait);
87488c2ecf20Sopenharmony_ci	return 0;
87498c2ecf20Sopenharmony_ci
87508c2ecf20Sopenharmony_ciinterrupted:
87518c2ecf20Sopenharmony_ci	error = sock_intr_errno(*timeo_p);
87528c2ecf20Sopenharmony_ci
87538c2ecf20Sopenharmony_ciout:
87548c2ecf20Sopenharmony_ci	finish_wait(sk_sleep(sk), &wait);
87558c2ecf20Sopenharmony_ci	*err = error;
87568c2ecf20Sopenharmony_ci	return error;
87578c2ecf20Sopenharmony_ci}
87588c2ecf20Sopenharmony_ci
87598c2ecf20Sopenharmony_ci/* Receive a datagram.
87608c2ecf20Sopenharmony_ci * Note: This is pretty much the same routine as in core/datagram.c
87618c2ecf20Sopenharmony_ci * with a few changes to make lksctp work.
87628c2ecf20Sopenharmony_ci */
87638c2ecf20Sopenharmony_cistruct sk_buff *sctp_skb_recv_datagram(struct sock *sk, int flags,
87648c2ecf20Sopenharmony_ci				       int noblock, int *err)
87658c2ecf20Sopenharmony_ci{
87668c2ecf20Sopenharmony_ci	int error;
87678c2ecf20Sopenharmony_ci	struct sk_buff *skb;
87688c2ecf20Sopenharmony_ci	long timeo;
87698c2ecf20Sopenharmony_ci
87708c2ecf20Sopenharmony_ci	timeo = sock_rcvtimeo(sk, noblock);
87718c2ecf20Sopenharmony_ci
87728c2ecf20Sopenharmony_ci	pr_debug("%s: timeo:%ld, max:%ld\n", __func__, timeo,
87738c2ecf20Sopenharmony_ci		 MAX_SCHEDULE_TIMEOUT);
87748c2ecf20Sopenharmony_ci
87758c2ecf20Sopenharmony_ci	do {
87768c2ecf20Sopenharmony_ci		/* Again only user level code calls this function,
87778c2ecf20Sopenharmony_ci		 * so nothing interrupt level
87788c2ecf20Sopenharmony_ci		 * will suddenly eat the receive_queue.
87798c2ecf20Sopenharmony_ci		 *
87808c2ecf20Sopenharmony_ci		 *  Look at current nfs client by the way...
87818c2ecf20Sopenharmony_ci		 *  However, this function was correct in any case. 8)
87828c2ecf20Sopenharmony_ci		 */
87838c2ecf20Sopenharmony_ci		if (flags & MSG_PEEK) {
87848c2ecf20Sopenharmony_ci			skb = skb_peek(&sk->sk_receive_queue);
87858c2ecf20Sopenharmony_ci			if (skb)
87868c2ecf20Sopenharmony_ci				refcount_inc(&skb->users);
87878c2ecf20Sopenharmony_ci		} else {
87888c2ecf20Sopenharmony_ci			skb = __skb_dequeue(&sk->sk_receive_queue);
87898c2ecf20Sopenharmony_ci		}
87908c2ecf20Sopenharmony_ci
87918c2ecf20Sopenharmony_ci		if (skb)
87928c2ecf20Sopenharmony_ci			return skb;
87938c2ecf20Sopenharmony_ci
87948c2ecf20Sopenharmony_ci		/* Caller is allowed not to check sk->sk_err before calling. */
87958c2ecf20Sopenharmony_ci		error = sock_error(sk);
87968c2ecf20Sopenharmony_ci		if (error)
87978c2ecf20Sopenharmony_ci			goto no_packet;
87988c2ecf20Sopenharmony_ci
87998c2ecf20Sopenharmony_ci		if (sk->sk_shutdown & RCV_SHUTDOWN)
88008c2ecf20Sopenharmony_ci			break;
88018c2ecf20Sopenharmony_ci
88028c2ecf20Sopenharmony_ci		if (sk_can_busy_loop(sk)) {
88038c2ecf20Sopenharmony_ci			sk_busy_loop(sk, noblock);
88048c2ecf20Sopenharmony_ci
88058c2ecf20Sopenharmony_ci			if (!skb_queue_empty_lockless(&sk->sk_receive_queue))
88068c2ecf20Sopenharmony_ci				continue;
88078c2ecf20Sopenharmony_ci		}
88088c2ecf20Sopenharmony_ci
88098c2ecf20Sopenharmony_ci		/* User doesn't want to wait.  */
88108c2ecf20Sopenharmony_ci		error = -EAGAIN;
88118c2ecf20Sopenharmony_ci		if (!timeo)
88128c2ecf20Sopenharmony_ci			goto no_packet;
88138c2ecf20Sopenharmony_ci	} while (sctp_wait_for_packet(sk, err, &timeo) == 0);
88148c2ecf20Sopenharmony_ci
88158c2ecf20Sopenharmony_ci	return NULL;
88168c2ecf20Sopenharmony_ci
88178c2ecf20Sopenharmony_cino_packet:
88188c2ecf20Sopenharmony_ci	*err = error;
88198c2ecf20Sopenharmony_ci	return NULL;
88208c2ecf20Sopenharmony_ci}
88218c2ecf20Sopenharmony_ci
88228c2ecf20Sopenharmony_ci/* If sndbuf has changed, wake up per association sndbuf waiters.  */
88238c2ecf20Sopenharmony_cistatic void __sctp_write_space(struct sctp_association *asoc)
88248c2ecf20Sopenharmony_ci{
88258c2ecf20Sopenharmony_ci	struct sock *sk = asoc->base.sk;
88268c2ecf20Sopenharmony_ci
88278c2ecf20Sopenharmony_ci	if (sctp_wspace(asoc) <= 0)
88288c2ecf20Sopenharmony_ci		return;
88298c2ecf20Sopenharmony_ci
88308c2ecf20Sopenharmony_ci	if (waitqueue_active(&asoc->wait))
88318c2ecf20Sopenharmony_ci		wake_up_interruptible(&asoc->wait);
88328c2ecf20Sopenharmony_ci
88338c2ecf20Sopenharmony_ci	if (sctp_writeable(sk)) {
88348c2ecf20Sopenharmony_ci		struct socket_wq *wq;
88358c2ecf20Sopenharmony_ci
88368c2ecf20Sopenharmony_ci		rcu_read_lock();
88378c2ecf20Sopenharmony_ci		wq = rcu_dereference(sk->sk_wq);
88388c2ecf20Sopenharmony_ci		if (wq) {
88398c2ecf20Sopenharmony_ci			if (waitqueue_active(&wq->wait))
88408c2ecf20Sopenharmony_ci				wake_up_interruptible(&wq->wait);
88418c2ecf20Sopenharmony_ci
88428c2ecf20Sopenharmony_ci			/* Note that we try to include the Async I/O support
88438c2ecf20Sopenharmony_ci			 * here by modeling from the current TCP/UDP code.
88448c2ecf20Sopenharmony_ci			 * We have not tested with it yet.
88458c2ecf20Sopenharmony_ci			 */
88468c2ecf20Sopenharmony_ci			if (!(sk->sk_shutdown & SEND_SHUTDOWN))
88478c2ecf20Sopenharmony_ci				sock_wake_async(wq, SOCK_WAKE_SPACE, POLL_OUT);
88488c2ecf20Sopenharmony_ci		}
88498c2ecf20Sopenharmony_ci		rcu_read_unlock();
88508c2ecf20Sopenharmony_ci	}
88518c2ecf20Sopenharmony_ci}
88528c2ecf20Sopenharmony_ci
88538c2ecf20Sopenharmony_cistatic void sctp_wake_up_waiters(struct sock *sk,
88548c2ecf20Sopenharmony_ci				 struct sctp_association *asoc)
88558c2ecf20Sopenharmony_ci{
88568c2ecf20Sopenharmony_ci	struct sctp_association *tmp = asoc;
88578c2ecf20Sopenharmony_ci
88588c2ecf20Sopenharmony_ci	/* We do accounting for the sndbuf space per association,
88598c2ecf20Sopenharmony_ci	 * so we only need to wake our own association.
88608c2ecf20Sopenharmony_ci	 */
88618c2ecf20Sopenharmony_ci	if (asoc->ep->sndbuf_policy)
88628c2ecf20Sopenharmony_ci		return __sctp_write_space(asoc);
88638c2ecf20Sopenharmony_ci
88648c2ecf20Sopenharmony_ci	/* If association goes down and is just flushing its
88658c2ecf20Sopenharmony_ci	 * outq, then just normally notify others.
88668c2ecf20Sopenharmony_ci	 */
88678c2ecf20Sopenharmony_ci	if (asoc->base.dead)
88688c2ecf20Sopenharmony_ci		return sctp_write_space(sk);
88698c2ecf20Sopenharmony_ci
88708c2ecf20Sopenharmony_ci	/* Accounting for the sndbuf space is per socket, so we
88718c2ecf20Sopenharmony_ci	 * need to wake up others, try to be fair and in case of
88728c2ecf20Sopenharmony_ci	 * other associations, let them have a go first instead
88738c2ecf20Sopenharmony_ci	 * of just doing a sctp_write_space() call.
88748c2ecf20Sopenharmony_ci	 *
88758c2ecf20Sopenharmony_ci	 * Note that we reach sctp_wake_up_waiters() only when
88768c2ecf20Sopenharmony_ci	 * associations free up queued chunks, thus we are under
88778c2ecf20Sopenharmony_ci	 * lock and the list of associations on a socket is
88788c2ecf20Sopenharmony_ci	 * guaranteed not to change.
88798c2ecf20Sopenharmony_ci	 */
88808c2ecf20Sopenharmony_ci	for (tmp = list_next_entry(tmp, asocs); 1;
88818c2ecf20Sopenharmony_ci	     tmp = list_next_entry(tmp, asocs)) {
88828c2ecf20Sopenharmony_ci		/* Manually skip the head element. */
88838c2ecf20Sopenharmony_ci		if (&tmp->asocs == &((sctp_sk(sk))->ep->asocs))
88848c2ecf20Sopenharmony_ci			continue;
88858c2ecf20Sopenharmony_ci		/* Wake up association. */
88868c2ecf20Sopenharmony_ci		__sctp_write_space(tmp);
88878c2ecf20Sopenharmony_ci		/* We've reached the end. */
88888c2ecf20Sopenharmony_ci		if (tmp == asoc)
88898c2ecf20Sopenharmony_ci			break;
88908c2ecf20Sopenharmony_ci	}
88918c2ecf20Sopenharmony_ci}
88928c2ecf20Sopenharmony_ci
88938c2ecf20Sopenharmony_ci/* Do accounting for the sndbuf space.
88948c2ecf20Sopenharmony_ci * Decrement the used sndbuf space of the corresponding association by the
88958c2ecf20Sopenharmony_ci * data size which was just transmitted(freed).
88968c2ecf20Sopenharmony_ci */
88978c2ecf20Sopenharmony_cistatic void sctp_wfree(struct sk_buff *skb)
88988c2ecf20Sopenharmony_ci{
88998c2ecf20Sopenharmony_ci	struct sctp_chunk *chunk = skb_shinfo(skb)->destructor_arg;
89008c2ecf20Sopenharmony_ci	struct sctp_association *asoc = chunk->asoc;
89018c2ecf20Sopenharmony_ci	struct sock *sk = asoc->base.sk;
89028c2ecf20Sopenharmony_ci
89038c2ecf20Sopenharmony_ci	sk_mem_uncharge(sk, skb->truesize);
89048c2ecf20Sopenharmony_ci	sk_wmem_queued_add(sk, -(skb->truesize + sizeof(struct sctp_chunk)));
89058c2ecf20Sopenharmony_ci	asoc->sndbuf_used -= skb->truesize + sizeof(struct sctp_chunk);
89068c2ecf20Sopenharmony_ci	WARN_ON(refcount_sub_and_test(sizeof(struct sctp_chunk),
89078c2ecf20Sopenharmony_ci				      &sk->sk_wmem_alloc));
89088c2ecf20Sopenharmony_ci
89098c2ecf20Sopenharmony_ci	if (chunk->shkey) {
89108c2ecf20Sopenharmony_ci		struct sctp_shared_key *shkey = chunk->shkey;
89118c2ecf20Sopenharmony_ci
89128c2ecf20Sopenharmony_ci		/* refcnt == 2 and !list_empty mean after this release, it's
89138c2ecf20Sopenharmony_ci		 * not being used anywhere, and it's time to notify userland
89148c2ecf20Sopenharmony_ci		 * that this shkey can be freed if it's been deactivated.
89158c2ecf20Sopenharmony_ci		 */
89168c2ecf20Sopenharmony_ci		if (shkey->deactivated && !list_empty(&shkey->key_list) &&
89178c2ecf20Sopenharmony_ci		    refcount_read(&shkey->refcnt) == 2) {
89188c2ecf20Sopenharmony_ci			struct sctp_ulpevent *ev;
89198c2ecf20Sopenharmony_ci
89208c2ecf20Sopenharmony_ci			ev = sctp_ulpevent_make_authkey(asoc, shkey->key_id,
89218c2ecf20Sopenharmony_ci							SCTP_AUTH_FREE_KEY,
89228c2ecf20Sopenharmony_ci							GFP_KERNEL);
89238c2ecf20Sopenharmony_ci			if (ev)
89248c2ecf20Sopenharmony_ci				asoc->stream.si->enqueue_event(&asoc->ulpq, ev);
89258c2ecf20Sopenharmony_ci		}
89268c2ecf20Sopenharmony_ci		sctp_auth_shkey_release(chunk->shkey);
89278c2ecf20Sopenharmony_ci	}
89288c2ecf20Sopenharmony_ci
89298c2ecf20Sopenharmony_ci	sock_wfree(skb);
89308c2ecf20Sopenharmony_ci	sctp_wake_up_waiters(sk, asoc);
89318c2ecf20Sopenharmony_ci
89328c2ecf20Sopenharmony_ci	sctp_association_put(asoc);
89338c2ecf20Sopenharmony_ci}
89348c2ecf20Sopenharmony_ci
89358c2ecf20Sopenharmony_ci/* Do accounting for the receive space on the socket.
89368c2ecf20Sopenharmony_ci * Accounting for the association is done in ulpevent.c
89378c2ecf20Sopenharmony_ci * We set this as a destructor for the cloned data skbs so that
89388c2ecf20Sopenharmony_ci * accounting is done at the correct time.
89398c2ecf20Sopenharmony_ci */
89408c2ecf20Sopenharmony_civoid sctp_sock_rfree(struct sk_buff *skb)
89418c2ecf20Sopenharmony_ci{
89428c2ecf20Sopenharmony_ci	struct sock *sk = skb->sk;
89438c2ecf20Sopenharmony_ci	struct sctp_ulpevent *event = sctp_skb2event(skb);
89448c2ecf20Sopenharmony_ci
89458c2ecf20Sopenharmony_ci	atomic_sub(event->rmem_len, &sk->sk_rmem_alloc);
89468c2ecf20Sopenharmony_ci
89478c2ecf20Sopenharmony_ci	/*
89488c2ecf20Sopenharmony_ci	 * Mimic the behavior of sock_rfree
89498c2ecf20Sopenharmony_ci	 */
89508c2ecf20Sopenharmony_ci	sk_mem_uncharge(sk, event->rmem_len);
89518c2ecf20Sopenharmony_ci}
89528c2ecf20Sopenharmony_ci
89538c2ecf20Sopenharmony_ci
89548c2ecf20Sopenharmony_ci/* Helper function to wait for space in the sndbuf.  */
89558c2ecf20Sopenharmony_cistatic int sctp_wait_for_sndbuf(struct sctp_association *asoc, long *timeo_p,
89568c2ecf20Sopenharmony_ci				size_t msg_len)
89578c2ecf20Sopenharmony_ci{
89588c2ecf20Sopenharmony_ci	struct sock *sk = asoc->base.sk;
89598c2ecf20Sopenharmony_ci	long current_timeo = *timeo_p;
89608c2ecf20Sopenharmony_ci	DEFINE_WAIT(wait);
89618c2ecf20Sopenharmony_ci	int err = 0;
89628c2ecf20Sopenharmony_ci
89638c2ecf20Sopenharmony_ci	pr_debug("%s: asoc:%p, timeo:%ld, msg_len:%zu\n", __func__, asoc,
89648c2ecf20Sopenharmony_ci		 *timeo_p, msg_len);
89658c2ecf20Sopenharmony_ci
89668c2ecf20Sopenharmony_ci	/* Increment the association's refcnt.  */
89678c2ecf20Sopenharmony_ci	sctp_association_hold(asoc);
89688c2ecf20Sopenharmony_ci
89698c2ecf20Sopenharmony_ci	/* Wait on the association specific sndbuf space. */
89708c2ecf20Sopenharmony_ci	for (;;) {
89718c2ecf20Sopenharmony_ci		prepare_to_wait_exclusive(&asoc->wait, &wait,
89728c2ecf20Sopenharmony_ci					  TASK_INTERRUPTIBLE);
89738c2ecf20Sopenharmony_ci		if (asoc->base.dead)
89748c2ecf20Sopenharmony_ci			goto do_dead;
89758c2ecf20Sopenharmony_ci		if (!*timeo_p)
89768c2ecf20Sopenharmony_ci			goto do_nonblock;
89778c2ecf20Sopenharmony_ci		if (sk->sk_err || asoc->state >= SCTP_STATE_SHUTDOWN_PENDING)
89788c2ecf20Sopenharmony_ci			goto do_error;
89798c2ecf20Sopenharmony_ci		if (signal_pending(current))
89808c2ecf20Sopenharmony_ci			goto do_interrupted;
89818c2ecf20Sopenharmony_ci		if (sk_under_memory_pressure(sk))
89828c2ecf20Sopenharmony_ci			sk_mem_reclaim(sk);
89838c2ecf20Sopenharmony_ci		if ((int)msg_len <= sctp_wspace(asoc) &&
89848c2ecf20Sopenharmony_ci		    sk_wmem_schedule(sk, msg_len))
89858c2ecf20Sopenharmony_ci			break;
89868c2ecf20Sopenharmony_ci
89878c2ecf20Sopenharmony_ci		/* Let another process have a go.  Since we are going
89888c2ecf20Sopenharmony_ci		 * to sleep anyway.
89898c2ecf20Sopenharmony_ci		 */
89908c2ecf20Sopenharmony_ci		release_sock(sk);
89918c2ecf20Sopenharmony_ci		current_timeo = schedule_timeout(current_timeo);
89928c2ecf20Sopenharmony_ci		lock_sock(sk);
89938c2ecf20Sopenharmony_ci		if (sk != asoc->base.sk)
89948c2ecf20Sopenharmony_ci			goto do_error;
89958c2ecf20Sopenharmony_ci
89968c2ecf20Sopenharmony_ci		*timeo_p = current_timeo;
89978c2ecf20Sopenharmony_ci	}
89988c2ecf20Sopenharmony_ci
89998c2ecf20Sopenharmony_ciout:
90008c2ecf20Sopenharmony_ci	finish_wait(&asoc->wait, &wait);
90018c2ecf20Sopenharmony_ci
90028c2ecf20Sopenharmony_ci	/* Release the association's refcnt.  */
90038c2ecf20Sopenharmony_ci	sctp_association_put(asoc);
90048c2ecf20Sopenharmony_ci
90058c2ecf20Sopenharmony_ci	return err;
90068c2ecf20Sopenharmony_ci
90078c2ecf20Sopenharmony_cido_dead:
90088c2ecf20Sopenharmony_ci	err = -ESRCH;
90098c2ecf20Sopenharmony_ci	goto out;
90108c2ecf20Sopenharmony_ci
90118c2ecf20Sopenharmony_cido_error:
90128c2ecf20Sopenharmony_ci	err = -EPIPE;
90138c2ecf20Sopenharmony_ci	goto out;
90148c2ecf20Sopenharmony_ci
90158c2ecf20Sopenharmony_cido_interrupted:
90168c2ecf20Sopenharmony_ci	err = sock_intr_errno(*timeo_p);
90178c2ecf20Sopenharmony_ci	goto out;
90188c2ecf20Sopenharmony_ci
90198c2ecf20Sopenharmony_cido_nonblock:
90208c2ecf20Sopenharmony_ci	err = -EAGAIN;
90218c2ecf20Sopenharmony_ci	goto out;
90228c2ecf20Sopenharmony_ci}
90238c2ecf20Sopenharmony_ci
90248c2ecf20Sopenharmony_civoid sctp_data_ready(struct sock *sk)
90258c2ecf20Sopenharmony_ci{
90268c2ecf20Sopenharmony_ci	struct socket_wq *wq;
90278c2ecf20Sopenharmony_ci
90288c2ecf20Sopenharmony_ci	rcu_read_lock();
90298c2ecf20Sopenharmony_ci	wq = rcu_dereference(sk->sk_wq);
90308c2ecf20Sopenharmony_ci	if (skwq_has_sleeper(wq))
90318c2ecf20Sopenharmony_ci		wake_up_interruptible_sync_poll(&wq->wait, EPOLLIN |
90328c2ecf20Sopenharmony_ci						EPOLLRDNORM | EPOLLRDBAND);
90338c2ecf20Sopenharmony_ci	sk_wake_async(sk, SOCK_WAKE_WAITD, POLL_IN);
90348c2ecf20Sopenharmony_ci	rcu_read_unlock();
90358c2ecf20Sopenharmony_ci}
90368c2ecf20Sopenharmony_ci
90378c2ecf20Sopenharmony_ci/* If socket sndbuf has changed, wake up all per association waiters.  */
90388c2ecf20Sopenharmony_civoid sctp_write_space(struct sock *sk)
90398c2ecf20Sopenharmony_ci{
90408c2ecf20Sopenharmony_ci	struct sctp_association *asoc;
90418c2ecf20Sopenharmony_ci
90428c2ecf20Sopenharmony_ci	/* Wake up the tasks in each wait queue.  */
90438c2ecf20Sopenharmony_ci	list_for_each_entry(asoc, &((sctp_sk(sk))->ep->asocs), asocs) {
90448c2ecf20Sopenharmony_ci		__sctp_write_space(asoc);
90458c2ecf20Sopenharmony_ci	}
90468c2ecf20Sopenharmony_ci}
90478c2ecf20Sopenharmony_ci
90488c2ecf20Sopenharmony_ci/* Is there any sndbuf space available on the socket?
90498c2ecf20Sopenharmony_ci *
90508c2ecf20Sopenharmony_ci * Note that sk_wmem_alloc is the sum of the send buffers on all of the
90518c2ecf20Sopenharmony_ci * associations on the same socket.  For a UDP-style socket with
90528c2ecf20Sopenharmony_ci * multiple associations, it is possible for it to be "unwriteable"
90538c2ecf20Sopenharmony_ci * prematurely.  I assume that this is acceptable because
90548c2ecf20Sopenharmony_ci * a premature "unwriteable" is better than an accidental "writeable" which
90558c2ecf20Sopenharmony_ci * would cause an unwanted block under certain circumstances.  For the 1-1
90568c2ecf20Sopenharmony_ci * UDP-style sockets or TCP-style sockets, this code should work.
90578c2ecf20Sopenharmony_ci *  - Daisy
90588c2ecf20Sopenharmony_ci */
90598c2ecf20Sopenharmony_cistatic bool sctp_writeable(const struct sock *sk)
90608c2ecf20Sopenharmony_ci{
90618c2ecf20Sopenharmony_ci	return READ_ONCE(sk->sk_sndbuf) > READ_ONCE(sk->sk_wmem_queued);
90628c2ecf20Sopenharmony_ci}
90638c2ecf20Sopenharmony_ci
90648c2ecf20Sopenharmony_ci/* Wait for an association to go into ESTABLISHED state. If timeout is 0,
90658c2ecf20Sopenharmony_ci * returns immediately with EINPROGRESS.
90668c2ecf20Sopenharmony_ci */
90678c2ecf20Sopenharmony_cistatic int sctp_wait_for_connect(struct sctp_association *asoc, long *timeo_p)
90688c2ecf20Sopenharmony_ci{
90698c2ecf20Sopenharmony_ci	struct sock *sk = asoc->base.sk;
90708c2ecf20Sopenharmony_ci	int err = 0;
90718c2ecf20Sopenharmony_ci	long current_timeo = *timeo_p;
90728c2ecf20Sopenharmony_ci	DEFINE_WAIT(wait);
90738c2ecf20Sopenharmony_ci
90748c2ecf20Sopenharmony_ci	pr_debug("%s: asoc:%p, timeo:%ld\n", __func__, asoc, *timeo_p);
90758c2ecf20Sopenharmony_ci
90768c2ecf20Sopenharmony_ci	/* Increment the association's refcnt.  */
90778c2ecf20Sopenharmony_ci	sctp_association_hold(asoc);
90788c2ecf20Sopenharmony_ci
90798c2ecf20Sopenharmony_ci	for (;;) {
90808c2ecf20Sopenharmony_ci		prepare_to_wait_exclusive(&asoc->wait, &wait,
90818c2ecf20Sopenharmony_ci					  TASK_INTERRUPTIBLE);
90828c2ecf20Sopenharmony_ci		if (!*timeo_p)
90838c2ecf20Sopenharmony_ci			goto do_nonblock;
90848c2ecf20Sopenharmony_ci		if (sk->sk_shutdown & RCV_SHUTDOWN)
90858c2ecf20Sopenharmony_ci			break;
90868c2ecf20Sopenharmony_ci		if (sk->sk_err || asoc->state >= SCTP_STATE_SHUTDOWN_PENDING ||
90878c2ecf20Sopenharmony_ci		    asoc->base.dead)
90888c2ecf20Sopenharmony_ci			goto do_error;
90898c2ecf20Sopenharmony_ci		if (signal_pending(current))
90908c2ecf20Sopenharmony_ci			goto do_interrupted;
90918c2ecf20Sopenharmony_ci
90928c2ecf20Sopenharmony_ci		if (sctp_state(asoc, ESTABLISHED))
90938c2ecf20Sopenharmony_ci			break;
90948c2ecf20Sopenharmony_ci
90958c2ecf20Sopenharmony_ci		/* Let another process have a go.  Since we are going
90968c2ecf20Sopenharmony_ci		 * to sleep anyway.
90978c2ecf20Sopenharmony_ci		 */
90988c2ecf20Sopenharmony_ci		release_sock(sk);
90998c2ecf20Sopenharmony_ci		current_timeo = schedule_timeout(current_timeo);
91008c2ecf20Sopenharmony_ci		lock_sock(sk);
91018c2ecf20Sopenharmony_ci
91028c2ecf20Sopenharmony_ci		*timeo_p = current_timeo;
91038c2ecf20Sopenharmony_ci	}
91048c2ecf20Sopenharmony_ci
91058c2ecf20Sopenharmony_ciout:
91068c2ecf20Sopenharmony_ci	finish_wait(&asoc->wait, &wait);
91078c2ecf20Sopenharmony_ci
91088c2ecf20Sopenharmony_ci	/* Release the association's refcnt.  */
91098c2ecf20Sopenharmony_ci	sctp_association_put(asoc);
91108c2ecf20Sopenharmony_ci
91118c2ecf20Sopenharmony_ci	return err;
91128c2ecf20Sopenharmony_ci
91138c2ecf20Sopenharmony_cido_error:
91148c2ecf20Sopenharmony_ci	if (asoc->init_err_counter + 1 > asoc->max_init_attempts)
91158c2ecf20Sopenharmony_ci		err = -ETIMEDOUT;
91168c2ecf20Sopenharmony_ci	else
91178c2ecf20Sopenharmony_ci		err = -ECONNREFUSED;
91188c2ecf20Sopenharmony_ci	goto out;
91198c2ecf20Sopenharmony_ci
91208c2ecf20Sopenharmony_cido_interrupted:
91218c2ecf20Sopenharmony_ci	err = sock_intr_errno(*timeo_p);
91228c2ecf20Sopenharmony_ci	goto out;
91238c2ecf20Sopenharmony_ci
91248c2ecf20Sopenharmony_cido_nonblock:
91258c2ecf20Sopenharmony_ci	err = -EINPROGRESS;
91268c2ecf20Sopenharmony_ci	goto out;
91278c2ecf20Sopenharmony_ci}
91288c2ecf20Sopenharmony_ci
91298c2ecf20Sopenharmony_cistatic int sctp_wait_for_accept(struct sock *sk, long timeo)
91308c2ecf20Sopenharmony_ci{
91318c2ecf20Sopenharmony_ci	struct sctp_endpoint *ep;
91328c2ecf20Sopenharmony_ci	int err = 0;
91338c2ecf20Sopenharmony_ci	DEFINE_WAIT(wait);
91348c2ecf20Sopenharmony_ci
91358c2ecf20Sopenharmony_ci	ep = sctp_sk(sk)->ep;
91368c2ecf20Sopenharmony_ci
91378c2ecf20Sopenharmony_ci
91388c2ecf20Sopenharmony_ci	for (;;) {
91398c2ecf20Sopenharmony_ci		prepare_to_wait_exclusive(sk_sleep(sk), &wait,
91408c2ecf20Sopenharmony_ci					  TASK_INTERRUPTIBLE);
91418c2ecf20Sopenharmony_ci
91428c2ecf20Sopenharmony_ci		if (list_empty(&ep->asocs)) {
91438c2ecf20Sopenharmony_ci			release_sock(sk);
91448c2ecf20Sopenharmony_ci			timeo = schedule_timeout(timeo);
91458c2ecf20Sopenharmony_ci			lock_sock(sk);
91468c2ecf20Sopenharmony_ci		}
91478c2ecf20Sopenharmony_ci
91488c2ecf20Sopenharmony_ci		err = -EINVAL;
91498c2ecf20Sopenharmony_ci		if (!sctp_sstate(sk, LISTENING))
91508c2ecf20Sopenharmony_ci			break;
91518c2ecf20Sopenharmony_ci
91528c2ecf20Sopenharmony_ci		err = 0;
91538c2ecf20Sopenharmony_ci		if (!list_empty(&ep->asocs))
91548c2ecf20Sopenharmony_ci			break;
91558c2ecf20Sopenharmony_ci
91568c2ecf20Sopenharmony_ci		err = sock_intr_errno(timeo);
91578c2ecf20Sopenharmony_ci		if (signal_pending(current))
91588c2ecf20Sopenharmony_ci			break;
91598c2ecf20Sopenharmony_ci
91608c2ecf20Sopenharmony_ci		err = -EAGAIN;
91618c2ecf20Sopenharmony_ci		if (!timeo)
91628c2ecf20Sopenharmony_ci			break;
91638c2ecf20Sopenharmony_ci	}
91648c2ecf20Sopenharmony_ci
91658c2ecf20Sopenharmony_ci	finish_wait(sk_sleep(sk), &wait);
91668c2ecf20Sopenharmony_ci
91678c2ecf20Sopenharmony_ci	return err;
91688c2ecf20Sopenharmony_ci}
91698c2ecf20Sopenharmony_ci
91708c2ecf20Sopenharmony_cistatic void sctp_wait_for_close(struct sock *sk, long timeout)
91718c2ecf20Sopenharmony_ci{
91728c2ecf20Sopenharmony_ci	DEFINE_WAIT(wait);
91738c2ecf20Sopenharmony_ci
91748c2ecf20Sopenharmony_ci	do {
91758c2ecf20Sopenharmony_ci		prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE);
91768c2ecf20Sopenharmony_ci		if (list_empty(&sctp_sk(sk)->ep->asocs))
91778c2ecf20Sopenharmony_ci			break;
91788c2ecf20Sopenharmony_ci		release_sock(sk);
91798c2ecf20Sopenharmony_ci		timeout = schedule_timeout(timeout);
91808c2ecf20Sopenharmony_ci		lock_sock(sk);
91818c2ecf20Sopenharmony_ci	} while (!signal_pending(current) && timeout);
91828c2ecf20Sopenharmony_ci
91838c2ecf20Sopenharmony_ci	finish_wait(sk_sleep(sk), &wait);
91848c2ecf20Sopenharmony_ci}
91858c2ecf20Sopenharmony_ci
91868c2ecf20Sopenharmony_cistatic void sctp_skb_set_owner_r_frag(struct sk_buff *skb, struct sock *sk)
91878c2ecf20Sopenharmony_ci{
91888c2ecf20Sopenharmony_ci	struct sk_buff *frag;
91898c2ecf20Sopenharmony_ci
91908c2ecf20Sopenharmony_ci	if (!skb->data_len)
91918c2ecf20Sopenharmony_ci		goto done;
91928c2ecf20Sopenharmony_ci
91938c2ecf20Sopenharmony_ci	/* Don't forget the fragments. */
91948c2ecf20Sopenharmony_ci	skb_walk_frags(skb, frag)
91958c2ecf20Sopenharmony_ci		sctp_skb_set_owner_r_frag(frag, sk);
91968c2ecf20Sopenharmony_ci
91978c2ecf20Sopenharmony_cidone:
91988c2ecf20Sopenharmony_ci	sctp_skb_set_owner_r(skb, sk);
91998c2ecf20Sopenharmony_ci}
92008c2ecf20Sopenharmony_ci
92018c2ecf20Sopenharmony_civoid sctp_copy_sock(struct sock *newsk, struct sock *sk,
92028c2ecf20Sopenharmony_ci		    struct sctp_association *asoc)
92038c2ecf20Sopenharmony_ci{
92048c2ecf20Sopenharmony_ci	struct inet_sock *inet = inet_sk(sk);
92058c2ecf20Sopenharmony_ci	struct inet_sock *newinet;
92068c2ecf20Sopenharmony_ci	struct sctp_sock *sp = sctp_sk(sk);
92078c2ecf20Sopenharmony_ci	struct sctp_endpoint *ep = sp->ep;
92088c2ecf20Sopenharmony_ci
92098c2ecf20Sopenharmony_ci	newsk->sk_type = sk->sk_type;
92108c2ecf20Sopenharmony_ci	newsk->sk_bound_dev_if = sk->sk_bound_dev_if;
92118c2ecf20Sopenharmony_ci	newsk->sk_flags = sk->sk_flags;
92128c2ecf20Sopenharmony_ci	newsk->sk_tsflags = sk->sk_tsflags;
92138c2ecf20Sopenharmony_ci	newsk->sk_no_check_tx = sk->sk_no_check_tx;
92148c2ecf20Sopenharmony_ci	newsk->sk_no_check_rx = sk->sk_no_check_rx;
92158c2ecf20Sopenharmony_ci	newsk->sk_reuse = sk->sk_reuse;
92168c2ecf20Sopenharmony_ci	sctp_sk(newsk)->reuse = sp->reuse;
92178c2ecf20Sopenharmony_ci
92188c2ecf20Sopenharmony_ci	newsk->sk_shutdown = sk->sk_shutdown;
92198c2ecf20Sopenharmony_ci	newsk->sk_destruct = sk->sk_destruct;
92208c2ecf20Sopenharmony_ci	newsk->sk_family = sk->sk_family;
92218c2ecf20Sopenharmony_ci	newsk->sk_protocol = IPPROTO_SCTP;
92228c2ecf20Sopenharmony_ci	newsk->sk_backlog_rcv = sk->sk_prot->backlog_rcv;
92238c2ecf20Sopenharmony_ci	newsk->sk_sndbuf = sk->sk_sndbuf;
92248c2ecf20Sopenharmony_ci	newsk->sk_rcvbuf = sk->sk_rcvbuf;
92258c2ecf20Sopenharmony_ci	newsk->sk_lingertime = sk->sk_lingertime;
92268c2ecf20Sopenharmony_ci	newsk->sk_rcvtimeo = sk->sk_rcvtimeo;
92278c2ecf20Sopenharmony_ci	newsk->sk_sndtimeo = sk->sk_sndtimeo;
92288c2ecf20Sopenharmony_ci	newsk->sk_rxhash = sk->sk_rxhash;
92298c2ecf20Sopenharmony_ci
92308c2ecf20Sopenharmony_ci	newinet = inet_sk(newsk);
92318c2ecf20Sopenharmony_ci
92328c2ecf20Sopenharmony_ci	/* Initialize sk's sport, dport, rcv_saddr and daddr for
92338c2ecf20Sopenharmony_ci	 * getsockname() and getpeername()
92348c2ecf20Sopenharmony_ci	 */
92358c2ecf20Sopenharmony_ci	newinet->inet_sport = inet->inet_sport;
92368c2ecf20Sopenharmony_ci	newinet->inet_saddr = inet->inet_saddr;
92378c2ecf20Sopenharmony_ci	newinet->inet_rcv_saddr = inet->inet_rcv_saddr;
92388c2ecf20Sopenharmony_ci	newinet->inet_dport = htons(asoc->peer.port);
92398c2ecf20Sopenharmony_ci	newinet->pmtudisc = inet->pmtudisc;
92408c2ecf20Sopenharmony_ci	newinet->inet_id = prandom_u32();
92418c2ecf20Sopenharmony_ci
92428c2ecf20Sopenharmony_ci	newinet->uc_ttl = inet->uc_ttl;
92438c2ecf20Sopenharmony_ci	newinet->mc_loop = 1;
92448c2ecf20Sopenharmony_ci	newinet->mc_ttl = 1;
92458c2ecf20Sopenharmony_ci	newinet->mc_index = 0;
92468c2ecf20Sopenharmony_ci	newinet->mc_list = NULL;
92478c2ecf20Sopenharmony_ci
92488c2ecf20Sopenharmony_ci	if (newsk->sk_flags & SK_FLAGS_TIMESTAMP)
92498c2ecf20Sopenharmony_ci		net_enable_timestamp();
92508c2ecf20Sopenharmony_ci
92518c2ecf20Sopenharmony_ci	/* Set newsk security attributes from orginal sk and connection
92528c2ecf20Sopenharmony_ci	 * security attribute from ep.
92538c2ecf20Sopenharmony_ci	 */
92548c2ecf20Sopenharmony_ci	security_sctp_sk_clone(ep, sk, newsk);
92558c2ecf20Sopenharmony_ci}
92568c2ecf20Sopenharmony_ci
92578c2ecf20Sopenharmony_cistatic inline void sctp_copy_descendant(struct sock *sk_to,
92588c2ecf20Sopenharmony_ci					const struct sock *sk_from)
92598c2ecf20Sopenharmony_ci{
92608c2ecf20Sopenharmony_ci	size_t ancestor_size = sizeof(struct inet_sock);
92618c2ecf20Sopenharmony_ci
92628c2ecf20Sopenharmony_ci	ancestor_size += sk_from->sk_prot->obj_size;
92638c2ecf20Sopenharmony_ci	ancestor_size -= offsetof(struct sctp_sock, pd_lobby);
92648c2ecf20Sopenharmony_ci	__inet_sk_copy_descendant(sk_to, sk_from, ancestor_size);
92658c2ecf20Sopenharmony_ci}
92668c2ecf20Sopenharmony_ci
92678c2ecf20Sopenharmony_ci/* Populate the fields of the newsk from the oldsk and migrate the assoc
92688c2ecf20Sopenharmony_ci * and its messages to the newsk.
92698c2ecf20Sopenharmony_ci */
92708c2ecf20Sopenharmony_cistatic int sctp_sock_migrate(struct sock *oldsk, struct sock *newsk,
92718c2ecf20Sopenharmony_ci			     struct sctp_association *assoc,
92728c2ecf20Sopenharmony_ci			     enum sctp_socket_type type)
92738c2ecf20Sopenharmony_ci{
92748c2ecf20Sopenharmony_ci	struct sctp_sock *oldsp = sctp_sk(oldsk);
92758c2ecf20Sopenharmony_ci	struct sctp_sock *newsp = sctp_sk(newsk);
92768c2ecf20Sopenharmony_ci	struct sctp_bind_bucket *pp; /* hash list port iterator */
92778c2ecf20Sopenharmony_ci	struct sctp_endpoint *newep = newsp->ep;
92788c2ecf20Sopenharmony_ci	struct sk_buff *skb, *tmp;
92798c2ecf20Sopenharmony_ci	struct sctp_ulpevent *event;
92808c2ecf20Sopenharmony_ci	struct sctp_bind_hashbucket *head;
92818c2ecf20Sopenharmony_ci	int err;
92828c2ecf20Sopenharmony_ci
92838c2ecf20Sopenharmony_ci	/* Migrate socket buffer sizes and all the socket level options to the
92848c2ecf20Sopenharmony_ci	 * new socket.
92858c2ecf20Sopenharmony_ci	 */
92868c2ecf20Sopenharmony_ci	newsk->sk_sndbuf = oldsk->sk_sndbuf;
92878c2ecf20Sopenharmony_ci	newsk->sk_rcvbuf = oldsk->sk_rcvbuf;
92888c2ecf20Sopenharmony_ci	/* Brute force copy old sctp opt. */
92898c2ecf20Sopenharmony_ci	sctp_copy_descendant(newsk, oldsk);
92908c2ecf20Sopenharmony_ci
92918c2ecf20Sopenharmony_ci	/* Restore the ep value that was overwritten with the above structure
92928c2ecf20Sopenharmony_ci	 * copy.
92938c2ecf20Sopenharmony_ci	 */
92948c2ecf20Sopenharmony_ci	newsp->ep = newep;
92958c2ecf20Sopenharmony_ci	newsp->hmac = NULL;
92968c2ecf20Sopenharmony_ci
92978c2ecf20Sopenharmony_ci	/* Hook this new socket in to the bind_hash list. */
92988c2ecf20Sopenharmony_ci	head = &sctp_port_hashtable[sctp_phashfn(sock_net(oldsk),
92998c2ecf20Sopenharmony_ci						 inet_sk(oldsk)->inet_num)];
93008c2ecf20Sopenharmony_ci	spin_lock_bh(&head->lock);
93018c2ecf20Sopenharmony_ci	pp = sctp_sk(oldsk)->bind_hash;
93028c2ecf20Sopenharmony_ci	sk_add_bind_node(newsk, &pp->owner);
93038c2ecf20Sopenharmony_ci	sctp_sk(newsk)->bind_hash = pp;
93048c2ecf20Sopenharmony_ci	inet_sk(newsk)->inet_num = inet_sk(oldsk)->inet_num;
93058c2ecf20Sopenharmony_ci	spin_unlock_bh(&head->lock);
93068c2ecf20Sopenharmony_ci
93078c2ecf20Sopenharmony_ci	/* Copy the bind_addr list from the original endpoint to the new
93088c2ecf20Sopenharmony_ci	 * endpoint so that we can handle restarts properly
93098c2ecf20Sopenharmony_ci	 */
93108c2ecf20Sopenharmony_ci	err = sctp_bind_addr_dup(&newsp->ep->base.bind_addr,
93118c2ecf20Sopenharmony_ci				 &oldsp->ep->base.bind_addr, GFP_KERNEL);
93128c2ecf20Sopenharmony_ci	if (err)
93138c2ecf20Sopenharmony_ci		return err;
93148c2ecf20Sopenharmony_ci
93158c2ecf20Sopenharmony_ci	/* New ep's auth_hmacs should be set if old ep's is set, in case
93168c2ecf20Sopenharmony_ci	 * that net->sctp.auth_enable has been changed to 0 by users and
93178c2ecf20Sopenharmony_ci	 * new ep's auth_hmacs couldn't be set in sctp_endpoint_init().
93188c2ecf20Sopenharmony_ci	 */
93198c2ecf20Sopenharmony_ci	if (oldsp->ep->auth_hmacs) {
93208c2ecf20Sopenharmony_ci		err = sctp_auth_init_hmacs(newsp->ep, GFP_KERNEL);
93218c2ecf20Sopenharmony_ci		if (err)
93228c2ecf20Sopenharmony_ci			return err;
93238c2ecf20Sopenharmony_ci	}
93248c2ecf20Sopenharmony_ci
93258c2ecf20Sopenharmony_ci	sctp_auto_asconf_init(newsp);
93268c2ecf20Sopenharmony_ci
93278c2ecf20Sopenharmony_ci	/* Move any messages in the old socket's receive queue that are for the
93288c2ecf20Sopenharmony_ci	 * peeled off association to the new socket's receive queue.
93298c2ecf20Sopenharmony_ci	 */
93308c2ecf20Sopenharmony_ci	sctp_skb_for_each(skb, &oldsk->sk_receive_queue, tmp) {
93318c2ecf20Sopenharmony_ci		event = sctp_skb2event(skb);
93328c2ecf20Sopenharmony_ci		if (event->asoc == assoc) {
93338c2ecf20Sopenharmony_ci			__skb_unlink(skb, &oldsk->sk_receive_queue);
93348c2ecf20Sopenharmony_ci			__skb_queue_tail(&newsk->sk_receive_queue, skb);
93358c2ecf20Sopenharmony_ci			sctp_skb_set_owner_r_frag(skb, newsk);
93368c2ecf20Sopenharmony_ci		}
93378c2ecf20Sopenharmony_ci	}
93388c2ecf20Sopenharmony_ci
93398c2ecf20Sopenharmony_ci	/* Clean up any messages pending delivery due to partial
93408c2ecf20Sopenharmony_ci	 * delivery.   Three cases:
93418c2ecf20Sopenharmony_ci	 * 1) No partial deliver;  no work.
93428c2ecf20Sopenharmony_ci	 * 2) Peeling off partial delivery; keep pd_lobby in new pd_lobby.
93438c2ecf20Sopenharmony_ci	 * 3) Peeling off non-partial delivery; move pd_lobby to receive_queue.
93448c2ecf20Sopenharmony_ci	 */
93458c2ecf20Sopenharmony_ci	atomic_set(&sctp_sk(newsk)->pd_mode, assoc->ulpq.pd_mode);
93468c2ecf20Sopenharmony_ci
93478c2ecf20Sopenharmony_ci	if (atomic_read(&sctp_sk(oldsk)->pd_mode)) {
93488c2ecf20Sopenharmony_ci		struct sk_buff_head *queue;
93498c2ecf20Sopenharmony_ci
93508c2ecf20Sopenharmony_ci		/* Decide which queue to move pd_lobby skbs to. */
93518c2ecf20Sopenharmony_ci		if (assoc->ulpq.pd_mode) {
93528c2ecf20Sopenharmony_ci			queue = &newsp->pd_lobby;
93538c2ecf20Sopenharmony_ci		} else
93548c2ecf20Sopenharmony_ci			queue = &newsk->sk_receive_queue;
93558c2ecf20Sopenharmony_ci
93568c2ecf20Sopenharmony_ci		/* Walk through the pd_lobby, looking for skbs that
93578c2ecf20Sopenharmony_ci		 * need moved to the new socket.
93588c2ecf20Sopenharmony_ci		 */
93598c2ecf20Sopenharmony_ci		sctp_skb_for_each(skb, &oldsp->pd_lobby, tmp) {
93608c2ecf20Sopenharmony_ci			event = sctp_skb2event(skb);
93618c2ecf20Sopenharmony_ci			if (event->asoc == assoc) {
93628c2ecf20Sopenharmony_ci				__skb_unlink(skb, &oldsp->pd_lobby);
93638c2ecf20Sopenharmony_ci				__skb_queue_tail(queue, skb);
93648c2ecf20Sopenharmony_ci				sctp_skb_set_owner_r_frag(skb, newsk);
93658c2ecf20Sopenharmony_ci			}
93668c2ecf20Sopenharmony_ci		}
93678c2ecf20Sopenharmony_ci
93688c2ecf20Sopenharmony_ci		/* Clear up any skbs waiting for the partial
93698c2ecf20Sopenharmony_ci		 * delivery to finish.
93708c2ecf20Sopenharmony_ci		 */
93718c2ecf20Sopenharmony_ci		if (assoc->ulpq.pd_mode)
93728c2ecf20Sopenharmony_ci			sctp_clear_pd(oldsk, NULL);
93738c2ecf20Sopenharmony_ci
93748c2ecf20Sopenharmony_ci	}
93758c2ecf20Sopenharmony_ci
93768c2ecf20Sopenharmony_ci	sctp_for_each_rx_skb(assoc, newsk, sctp_skb_set_owner_r_frag);
93778c2ecf20Sopenharmony_ci
93788c2ecf20Sopenharmony_ci	/* Set the type of socket to indicate that it is peeled off from the
93798c2ecf20Sopenharmony_ci	 * original UDP-style socket or created with the accept() call on a
93808c2ecf20Sopenharmony_ci	 * TCP-style socket..
93818c2ecf20Sopenharmony_ci	 */
93828c2ecf20Sopenharmony_ci	newsp->type = type;
93838c2ecf20Sopenharmony_ci
93848c2ecf20Sopenharmony_ci	/* Mark the new socket "in-use" by the user so that any packets
93858c2ecf20Sopenharmony_ci	 * that may arrive on the association after we've moved it are
93868c2ecf20Sopenharmony_ci	 * queued to the backlog.  This prevents a potential race between
93878c2ecf20Sopenharmony_ci	 * backlog processing on the old socket and new-packet processing
93888c2ecf20Sopenharmony_ci	 * on the new socket.
93898c2ecf20Sopenharmony_ci	 *
93908c2ecf20Sopenharmony_ci	 * The caller has just allocated newsk so we can guarantee that other
93918c2ecf20Sopenharmony_ci	 * paths won't try to lock it and then oldsk.
93928c2ecf20Sopenharmony_ci	 */
93938c2ecf20Sopenharmony_ci	lock_sock_nested(newsk, SINGLE_DEPTH_NESTING);
93948c2ecf20Sopenharmony_ci	sctp_for_each_tx_datachunk(assoc, true, sctp_clear_owner_w);
93958c2ecf20Sopenharmony_ci	sctp_assoc_migrate(assoc, newsk);
93968c2ecf20Sopenharmony_ci	sctp_for_each_tx_datachunk(assoc, false, sctp_set_owner_w);
93978c2ecf20Sopenharmony_ci
93988c2ecf20Sopenharmony_ci	/* If the association on the newsk is already closed before accept()
93998c2ecf20Sopenharmony_ci	 * is called, set RCV_SHUTDOWN flag.
94008c2ecf20Sopenharmony_ci	 */
94018c2ecf20Sopenharmony_ci	if (sctp_state(assoc, CLOSED) && sctp_style(newsk, TCP)) {
94028c2ecf20Sopenharmony_ci		inet_sk_set_state(newsk, SCTP_SS_CLOSED);
94038c2ecf20Sopenharmony_ci		newsk->sk_shutdown |= RCV_SHUTDOWN;
94048c2ecf20Sopenharmony_ci	} else {
94058c2ecf20Sopenharmony_ci		inet_sk_set_state(newsk, SCTP_SS_ESTABLISHED);
94068c2ecf20Sopenharmony_ci	}
94078c2ecf20Sopenharmony_ci
94088c2ecf20Sopenharmony_ci	release_sock(newsk);
94098c2ecf20Sopenharmony_ci
94108c2ecf20Sopenharmony_ci	return 0;
94118c2ecf20Sopenharmony_ci}
94128c2ecf20Sopenharmony_ci
94138c2ecf20Sopenharmony_ci
94148c2ecf20Sopenharmony_ci/* This proto struct describes the ULP interface for SCTP.  */
94158c2ecf20Sopenharmony_cistruct proto sctp_prot = {
94168c2ecf20Sopenharmony_ci	.name        =	"SCTP",
94178c2ecf20Sopenharmony_ci	.owner       =	THIS_MODULE,
94188c2ecf20Sopenharmony_ci	.close       =	sctp_close,
94198c2ecf20Sopenharmony_ci	.disconnect  =	sctp_disconnect,
94208c2ecf20Sopenharmony_ci	.accept      =	sctp_accept,
94218c2ecf20Sopenharmony_ci	.ioctl       =	sctp_ioctl,
94228c2ecf20Sopenharmony_ci	.init        =	sctp_init_sock,
94238c2ecf20Sopenharmony_ci	.destroy     =	sctp_destroy_sock,
94248c2ecf20Sopenharmony_ci	.shutdown    =	sctp_shutdown,
94258c2ecf20Sopenharmony_ci	.setsockopt  =	sctp_setsockopt,
94268c2ecf20Sopenharmony_ci	.getsockopt  =	sctp_getsockopt,
94278c2ecf20Sopenharmony_ci	.bpf_bypass_getsockopt	= sctp_bpf_bypass_getsockopt,
94288c2ecf20Sopenharmony_ci	.sendmsg     =	sctp_sendmsg,
94298c2ecf20Sopenharmony_ci	.recvmsg     =	sctp_recvmsg,
94308c2ecf20Sopenharmony_ci	.bind        =	sctp_bind,
94318c2ecf20Sopenharmony_ci	.bind_add    =  sctp_bind_add,
94328c2ecf20Sopenharmony_ci	.backlog_rcv =	sctp_backlog_rcv,
94338c2ecf20Sopenharmony_ci	.hash        =	sctp_hash,
94348c2ecf20Sopenharmony_ci	.unhash      =	sctp_unhash,
94358c2ecf20Sopenharmony_ci	.no_autobind =	true,
94368c2ecf20Sopenharmony_ci	.obj_size    =  sizeof(struct sctp_sock),
94378c2ecf20Sopenharmony_ci	.useroffset  =  offsetof(struct sctp_sock, subscribe),
94388c2ecf20Sopenharmony_ci	.usersize    =  offsetof(struct sctp_sock, initmsg) -
94398c2ecf20Sopenharmony_ci				offsetof(struct sctp_sock, subscribe) +
94408c2ecf20Sopenharmony_ci				sizeof_field(struct sctp_sock, initmsg),
94418c2ecf20Sopenharmony_ci	.sysctl_mem  =  sysctl_sctp_mem,
94428c2ecf20Sopenharmony_ci	.sysctl_rmem =  sysctl_sctp_rmem,
94438c2ecf20Sopenharmony_ci	.sysctl_wmem =  sysctl_sctp_wmem,
94448c2ecf20Sopenharmony_ci	.memory_pressure = &sctp_memory_pressure,
94458c2ecf20Sopenharmony_ci	.enter_memory_pressure = sctp_enter_memory_pressure,
94468c2ecf20Sopenharmony_ci	.memory_allocated = &sctp_memory_allocated,
94478c2ecf20Sopenharmony_ci	.sockets_allocated = &sctp_sockets_allocated,
94488c2ecf20Sopenharmony_ci};
94498c2ecf20Sopenharmony_ci
94508c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
94518c2ecf20Sopenharmony_ci
94528c2ecf20Sopenharmony_cistatic void sctp_v6_destruct_sock(struct sock *sk)
94538c2ecf20Sopenharmony_ci{
94548c2ecf20Sopenharmony_ci	sctp_destruct_common(sk);
94558c2ecf20Sopenharmony_ci	inet6_sock_destruct(sk);
94568c2ecf20Sopenharmony_ci}
94578c2ecf20Sopenharmony_ci
94588c2ecf20Sopenharmony_cistatic int sctp_v6_init_sock(struct sock *sk)
94598c2ecf20Sopenharmony_ci{
94608c2ecf20Sopenharmony_ci	int ret = sctp_init_sock(sk);
94618c2ecf20Sopenharmony_ci
94628c2ecf20Sopenharmony_ci	if (!ret)
94638c2ecf20Sopenharmony_ci		sk->sk_destruct = sctp_v6_destruct_sock;
94648c2ecf20Sopenharmony_ci
94658c2ecf20Sopenharmony_ci	return ret;
94668c2ecf20Sopenharmony_ci}
94678c2ecf20Sopenharmony_ci
94688c2ecf20Sopenharmony_cistruct proto sctpv6_prot = {
94698c2ecf20Sopenharmony_ci	.name		= "SCTPv6",
94708c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
94718c2ecf20Sopenharmony_ci	.close		= sctp_close,
94728c2ecf20Sopenharmony_ci	.disconnect	= sctp_disconnect,
94738c2ecf20Sopenharmony_ci	.accept		= sctp_accept,
94748c2ecf20Sopenharmony_ci	.ioctl		= sctp_ioctl,
94758c2ecf20Sopenharmony_ci	.init		= sctp_v6_init_sock,
94768c2ecf20Sopenharmony_ci	.destroy	= sctp_destroy_sock,
94778c2ecf20Sopenharmony_ci	.shutdown	= sctp_shutdown,
94788c2ecf20Sopenharmony_ci	.setsockopt	= sctp_setsockopt,
94798c2ecf20Sopenharmony_ci	.getsockopt	= sctp_getsockopt,
94808c2ecf20Sopenharmony_ci	.bpf_bypass_getsockopt	= sctp_bpf_bypass_getsockopt,
94818c2ecf20Sopenharmony_ci	.sendmsg	= sctp_sendmsg,
94828c2ecf20Sopenharmony_ci	.recvmsg	= sctp_recvmsg,
94838c2ecf20Sopenharmony_ci	.bind		= sctp_bind,
94848c2ecf20Sopenharmony_ci	.bind_add	= sctp_bind_add,
94858c2ecf20Sopenharmony_ci	.backlog_rcv	= sctp_backlog_rcv,
94868c2ecf20Sopenharmony_ci	.hash		= sctp_hash,
94878c2ecf20Sopenharmony_ci	.unhash		= sctp_unhash,
94888c2ecf20Sopenharmony_ci	.no_autobind	= true,
94898c2ecf20Sopenharmony_ci	.obj_size	= sizeof(struct sctp6_sock),
94908c2ecf20Sopenharmony_ci	.useroffset	= offsetof(struct sctp6_sock, sctp.subscribe),
94918c2ecf20Sopenharmony_ci	.usersize	= offsetof(struct sctp6_sock, sctp.initmsg) -
94928c2ecf20Sopenharmony_ci				offsetof(struct sctp6_sock, sctp.subscribe) +
94938c2ecf20Sopenharmony_ci				sizeof_field(struct sctp6_sock, sctp.initmsg),
94948c2ecf20Sopenharmony_ci	.sysctl_mem	= sysctl_sctp_mem,
94958c2ecf20Sopenharmony_ci	.sysctl_rmem	= sysctl_sctp_rmem,
94968c2ecf20Sopenharmony_ci	.sysctl_wmem	= sysctl_sctp_wmem,
94978c2ecf20Sopenharmony_ci	.memory_pressure = &sctp_memory_pressure,
94988c2ecf20Sopenharmony_ci	.enter_memory_pressure = sctp_enter_memory_pressure,
94998c2ecf20Sopenharmony_ci	.memory_allocated = &sctp_memory_allocated,
95008c2ecf20Sopenharmony_ci	.sockets_allocated = &sctp_sockets_allocated,
95018c2ecf20Sopenharmony_ci};
95028c2ecf20Sopenharmony_ci#endif /* IS_ENABLED(CONFIG_IPV6) */
9503