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