// SPDX-License-Identifier: GPL-2.0-or-later /* SCTP kernel implementation * (C) Copyright IBM Corp. 2001, 2004 * Copyright (c) 1999-2000 Cisco, Inc. * Copyright (c) 1999-2001 Motorola, Inc. * Copyright (c) 2001-2003 Intel Corp. * Copyright (c) 2001-2002 Nokia, Inc. * Copyright (c) 2001 La Monte H.P. Yarroll * * This file is part of the SCTP kernel implementation * * These functions interface with the sockets layer to implement the * SCTP Extensions for the Sockets API. * * Note that the descriptions from the specification are USER level * functions--this file is the functions which populate the struct proto * for SCTP which is the BOTTOM of the sockets interface. * * Please send any bug reports or fixes you make to the * email address(es): * lksctp developers <linux-sctp@vger.kernel.org> * * Written or modified by: * La Monte H.P. Yarroll <piggy@acm.org> * Narasimha Budihal <narsi@refcode.org> * Karl Knutson <karl@athena.chicago.il.us> * Jon Grimm <jgrimm@us.ibm.com> * Xingang Guo <xingang.guo@intel.com> * Daisy Chang <daisyc@us.ibm.com> * Sridhar Samudrala <samudrala@us.ibm.com> * Inaky Perez-Gonzalez <inaky.gonzalez@intel.com> * Ardelle Fan <ardelle.fan@intel.com> * Ryan Layer <rmlayer@us.ibm.com> * Anup Pemmaiah <pemmaiah@cc.usu.edu> * Kevin Gao <kevin.gao@intel.com>
*/
/* Get the sndbuf space available at the time on the association. */ staticinlineint sctp_wspace(struct sctp_association *asoc)
{ struct sock *sk = asoc->base.sk;
/* Increment the used sndbuf space count of the corresponding association by * the size of the outgoing data chunk. * Also, set the skb destructor for sndbuf accounting later. * * Since it is always 1-1 between chunk and skb, and also a new skb is always * allocated for chunk bundling in sctp_packet_transmit(), we can use the * destructor in the data chunk skb for the purpose of the sndbuf space * tracking.
*/ staticinlinevoid sctp_set_owner_w(struct sctp_chunk *chunk)
{ struct sctp_association *asoc = chunk->asoc; struct sock *sk = asoc->base.sk;
/* The sndbuf space is tracked per association. */
sctp_association_hold(asoc);
if (chunk->shkey)
sctp_auth_shkey_hold(chunk->shkey);
skb_set_owner_w(chunk->skb, sk);
chunk->skb->destructor = sctp_wfree; /* Save the chunk pointer in skb for sctp_wfree to use later. */
skb_shinfo(chunk->skb)->destructor_arg = chunk;
/* Verify that this is a valid address. */ staticinlineint sctp_verify_addr(struct sock *sk, union sctp_addr *addr, int len)
{ struct sctp_af *af;
/* Verify basic sockaddr. */
af = sctp_sockaddr_af(sctp_sk(sk), addr, len); if (!af) return -EINVAL;
/* Is this a valid SCTP address? */ if (!af->addr_valid(addr, sctp_sk(sk), NULL)) return -EINVAL;
if (!sctp_sk(sk)->pf->send_verify(sctp_sk(sk), (addr))) return -EINVAL;
return 0;
}
/* Look up the association by its id. If this is not a UDP-style * socket, the ID field is always ignored.
*/ struct sctp_association *sctp_id2assoc(struct sock *sk, sctp_assoc_t id)
{ struct sctp_association *asoc = NULL;
/* If this is not a UDP-style socket, assoc id should be ignored. */ if (!sctp_style(sk, UDP)) { /* Return NULL if the socket state is not ESTABLISHED. It * could be a TCP-style listening socket or a socket which * hasn't yet called connect() to establish an association.
*/ if (!sctp_sstate(sk, ESTABLISHED) && !sctp_sstate(sk, CLOSING)) return NULL;
/* Get the first and the only association from the list. */ if (!list_empty(&sctp_sk(sk)->ep->asocs))
asoc = list_entry(sctp_sk(sk)->ep->asocs.next, struct sctp_association, asocs); return asoc;
}
/* Otherwise this is a UDP-style socket. */ if (id <= SCTP_ALL_ASSOC) return NULL;
spin_lock_bh(&sctp_assocs_id_lock);
asoc = (struct sctp_association *)idr_find(&sctp_assocs_id, (int)id); if (asoc && (asoc->base.sk != sk || asoc->base.dead))
asoc = NULL;
spin_unlock_bh(&sctp_assocs_id_lock);
return asoc;
}
/* Look up the transport from an address and an assoc id. If both address and * id are specified, the associations matching the address and the id should be * the same.
*/ staticstruct sctp_transport *sctp_addr_id2transport(struct sock *sk, struct sockaddr_storage *addr,
sctp_assoc_t id)
{ struct sctp_association *addr_asoc = NULL, *id_asoc = NULL; struct sctp_af *af = sctp_get_af_specific(addr->ss_family); union sctp_addr *laddr = (union sctp_addr *)addr; struct sctp_transport *transport;
if (!af || sctp_verify_addr(sk, laddr, af->sockaddr_len)) return NULL;
/* API 3.1.2 bind() - UDP Style Syntax * The syntax of bind() is, * * ret = bind(int sd, struct sockaddr *addr, int addrlen); * * sd - the socket descriptor returned by socket(). * addr - the address structure (struct sockaddr_in or struct * sockaddr_in6 [RFC 2553]), * addr_len - the size of the address structure.
*/ staticint sctp_bind(struct sock *sk, struct sockaddr *addr, int addr_len)
{ int retval = 0;
staticint sctp_get_port_local(struct sock *, union sctp_addr *);
/* Verify this is a valid sockaddr. */ staticstruct sctp_af *sctp_sockaddr_af(struct sctp_sock *opt, union sctp_addr *addr, int len)
{ struct sctp_af *af;
if (!opt->pf->af_supported(addr->sa.sa_family, opt)) return NULL;
if (addr->sa.sa_family == AF_INET6) { if (len < SIN6_LEN_RFC2133) return NULL; /* V4 mapped address are really of AF_INET family */ if (ipv6_addr_v4mapped(&addr->v6.sin6_addr) &&
!opt->pf->af_supported(AF_INET, opt)) return NULL;
}
/* If we get this far, af is valid. */
af = sctp_get_af_specific(addr->sa.sa_family);
if (len < af->sockaddr_len) return NULL;
return af;
}
staticvoid sctp_auto_asconf_init(struct sctp_sock *sp)
{ struct net *net = sock_net(&sp->inet.sk);
/* Bind a local address either to an endpoint or to an association. */ staticint sctp_do_bind(struct sock *sk, union sctp_addr *addr, int len)
{ struct net *net = sock_net(sk); struct sctp_sock *sp = sctp_sk(sk); struct sctp_endpoint *ep = sp->ep; struct sctp_bind_addr *bp = &ep->base.bind_addr; struct sctp_af *af; unsignedshort snum; int ret = 0;
/* Common sockaddr verification. */
af = sctp_sockaddr_af(sp, addr, len); if (!af) {
pr_debug("%s: sk:%p, newaddr:%p, len:%d EINVAL\n",
__func__, sk, addr, len); return -EINVAL;
}
snum = ntohs(addr->v4.sin_port);
pr_debug("%s: sk:%p, new addr:%pISc, port:%d, new port:%d, len:%d\n",
__func__, sk, &addr->sa, bp->port, snum, len);
/* PF specific bind() address verification. */ if (!sp->pf->bind_verify(sp, addr)) return -EADDRNOTAVAIL;
/* We must either be unbound, or bind to the same port. * It's OK to allow 0 ports if we are already bound. * We'll just inhert an already bound port in this case
*/ if (bp->port) { if (!snum)
snum = bp->port; elseif (snum != bp->port) {
pr_debug("%s: new port %d doesn't match existing port " "%d\n", __func__, snum, bp->port); return -EINVAL;
}
}
if (snum && inet_port_requires_bind_service(net, snum) &&
!ns_capable(net->user_ns, CAP_NET_BIND_SERVICE)) return -EACCES;
/* See if the address matches any of the addresses we may have * already bound before checking against other endpoints.
*/ if (sctp_bind_addr_match(bp, addr, sp)) return -EINVAL;
/* Make sure we are allowed to bind here. * The function sctp_get_port_local() does duplicate address * detection.
*/
addr->v4.sin_port = htons(snum); if (sctp_get_port_local(sk, addr)) return -EADDRINUSE;
/* Add the address to the bind address list. * Use GFP_ATOMIC since BHs will be disabled.
*/
ret = sctp_add_bind_addr(bp, addr, af->sockaddr_len,
SCTP_ADDR_SRC, GFP_ATOMIC);
if (ret) {
sctp_put_port(sk); return ret;
} /* Copy back into socket for getsockname() use. */
inet_sk(sk)->inet_sport = htons(inet_sk(sk)->inet_num);
sp->pf->to_sk_saddr(addr, sk);
return ret;
}
/* ADDIP Section 4.1.1 Congestion Control of ASCONF Chunks * * R1) One and only one ASCONF Chunk MAY be in transit and unacknowledged * at any one time. If a sender, after sending an ASCONF chunk, decides * it needs to transfer another ASCONF Chunk, it MUST wait until the * ASCONF-ACK Chunk returns from the previous ASCONF Chunk before sending a * subsequent ASCONF. Note this restriction binds each side, so at any * time two ASCONF may be in-transit on any given association (one sent * from each endpoint).
*/ staticint sctp_send_asconf(struct sctp_association *asoc, struct sctp_chunk *chunk)
{ int retval = 0;
/* If there is an outstanding ASCONF chunk, queue it for later * transmission.
*/ if (asoc->addip_last_asconf) {
list_add_tail(&chunk->list, &asoc->addip_chunk_list); goto out;
}
/* Hold the chunk until an ASCONF_ACK is received. */
sctp_chunk_hold(chunk);
retval = sctp_primitive_ASCONF(asoc->base.net, asoc, chunk); if (retval)
sctp_chunk_free(chunk); else
asoc->addip_last_asconf = chunk;
out: return retval;
}
/* Add a list of addresses as bind addresses to local endpoint or * association. * * Basically run through each address specified in the addrs/addrcnt * array/length pair, determine if it is IPv6 or IPv4 and call * sctp_do_bind() on it. * * If any of them fails, then the operation will be reversed and the * ones that were added will be removed. * * Only sctp_setsockopt_bindx() is supposed to call this function.
*/ staticint sctp_bindx_add(struct sock *sk, struct sockaddr *addrs, int addrcnt)
{ int cnt; int retval = 0; void *addr_buf; struct sockaddr *sa_addr; struct sctp_af *af;
addr_buf = addrs; for (cnt = 0; cnt < addrcnt; cnt++) { /* The list may contain either IPv4 or IPv6 address; * determine the address length for walking thru the list.
*/
sa_addr = addr_buf;
af = sctp_get_af_specific(sa_addr->sa_family); if (!af) {
retval = -EINVAL; goto err_bindx_add;
}
err_bindx_add: if (retval < 0) { /* Failed. Cleanup the ones that have been added */ if (cnt > 0)
sctp_bindx_rem(sk, addrs, cnt); return retval;
}
}
return retval;
}
/* Send an ASCONF chunk with Add IP address parameters to all the peers of the * associations that are part of the endpoint indicating that a list of local * addresses are added to the endpoint. * * If any of the addresses is already in the bind address list of the * association, we do not send the chunk for that association. But it will not * affect other associations. * * Only sctp_setsockopt_bindx() is supposed to call this function.
*/ staticint sctp_send_asconf_add_ip(struct sock *sk, struct sockaddr *addrs, int addrcnt)
{ struct sctp_sock *sp; struct sctp_endpoint *ep; struct sctp_association *asoc; struct sctp_bind_addr *bp; struct sctp_chunk *chunk; struct sctp_sockaddr_entry *laddr; union sctp_addr *addr; union sctp_addr saveaddr; void *addr_buf; struct sctp_af *af; struct list_head *p; int i; int retval = 0;
list_for_each_entry(asoc, &ep->asocs, asocs) { if (!asoc->peer.asconf_capable) continue;
if (asoc->peer.addip_disabled_mask & SCTP_PARAM_ADD_IP) continue;
if (!sctp_state(asoc, ESTABLISHED)) continue;
/* Check if any address in the packed array of addresses is * in the bind address list of the association. If so, * do not send the asconf chunk to its peer, but continue with * other associations.
*/
addr_buf = addrs; for (i = 0; i < addrcnt; i++) {
addr = addr_buf;
af = sctp_get_af_specific(addr->v4.sin_family); if (!af) {
retval = -EINVAL; goto out;
}
if (sctp_assoc_lookup_laddr(asoc, addr)) break;
addr_buf += af->sockaddr_len;
} if (i < addrcnt) continue;
/* Use the first valid address in bind addr list of * association as Address Parameter of ASCONF CHUNK.
*/
bp = &asoc->base.bind_addr;
p = bp->address_list.next;
laddr = list_entry(p, struct sctp_sockaddr_entry, list);
chunk = sctp_make_asconf_update_ip(asoc, &laddr->a, addrs,
addrcnt, SCTP_PARAM_ADD_IP); if (!chunk) {
retval = -ENOMEM; goto out;
}
/* Add the new addresses to the bind address list with * use_as_src set to 0.
*/
addr_buf = addrs; for (i = 0; i < addrcnt; i++) {
addr = addr_buf;
af = sctp_get_af_specific(addr->v4.sin_family);
memcpy(&saveaddr, addr, af->sockaddr_len);
retval = sctp_add_bind_addr(bp, &saveaddr, sizeof(saveaddr),
SCTP_ADDR_NEW, GFP_ATOMIC);
addr_buf += af->sockaddr_len;
} if (asoc->src_out_of_asoc_ok) { struct sctp_transport *trans;
/* Remove a list of addresses from bind addresses list. Do not remove the * last address. * * Basically run through each address specified in the addrs/addrcnt * array/length pair, determine if it is IPv6 or IPv4 and call * sctp_del_bind() on it. * * If any of them fails, then the operation will be reversed and the * ones that were removed will be added back. * * At least one address has to be left; if only one address is * available, the operation will return -EBUSY. * * Only sctp_setsockopt_bindx() is supposed to call this function.
*/ staticint sctp_bindx_rem(struct sock *sk, struct sockaddr *addrs, int addrcnt)
{ struct sctp_sock *sp = sctp_sk(sk); struct sctp_endpoint *ep = sp->ep; int cnt; struct sctp_bind_addr *bp = &ep->base.bind_addr; int retval = 0; void *addr_buf; union sctp_addr *sa_addr; struct sctp_af *af;
addr_buf = addrs; for (cnt = 0; cnt < addrcnt; cnt++) { /* If the bind address list is empty or if there is only one * bind address, there is nothing more to be removed (we need * at least one address here).
*/ if (list_empty(&bp->address_list) ||
(sctp_list_single_entry(&bp->address_list))) {
retval = -EBUSY; goto err_bindx_rem;
}
sa_addr = addr_buf;
af = sctp_get_af_specific(sa_addr->sa.sa_family); if (!af) {
retval = -EINVAL; goto err_bindx_rem;
}
if (!sa_addr->v4.sin_port)
sa_addr->v4.sin_port = htons(bp->port);
/* FIXME - There is probably a need to check if sk->sk_saddr and * sk->sk_rcv_addr are currently set to one of the addresses to * be removed. This is something which needs to be looked into * when we are fixing the outstanding issues with multi-homing * socket routing and failover schemes. Refer to comments in * sctp_do_bind(). -daisy
*/
retval = sctp_del_bind_addr(bp, sa_addr);
addr_buf += af->sockaddr_len;
err_bindx_rem: if (retval < 0) { /* Failed. Add the ones that has been removed back */ if (cnt > 0)
sctp_bindx_add(sk, addrs, cnt); return retval;
}
}
return retval;
}
/* Send an ASCONF chunk with Delete IP address parameters to all the peers of * the associations that are part of the endpoint indicating that a list of * local addresses are removed from the endpoint. * * If any of the addresses is already in the bind address list of the * association, we do not send the chunk for that association. But it will not * affect other associations. * * Only sctp_setsockopt_bindx() is supposed to call this function.
*/ staticint sctp_send_asconf_del_ip(struct sock *sk, struct sockaddr *addrs, int addrcnt)
{ struct sctp_sock *sp; struct sctp_endpoint *ep; struct sctp_association *asoc; struct sctp_transport *transport; struct sctp_bind_addr *bp; struct sctp_chunk *chunk; union sctp_addr *laddr; void *addr_buf; struct sctp_af *af; struct sctp_sockaddr_entry *saddr; int i; int retval = 0; int stored = 0;
if (asoc->peer.addip_disabled_mask & SCTP_PARAM_DEL_IP) continue;
if (!sctp_state(asoc, ESTABLISHED)) continue;
/* Check if any address in the packed array of addresses is * not present in the bind address list of the association. * If so, do not send the asconf chunk to its peer, but * continue with other associations.
*/
addr_buf = addrs; for (i = 0; i < addrcnt; i++) {
laddr = addr_buf;
af = sctp_get_af_specific(laddr->v4.sin_family); if (!af) {
retval = -EINVAL; goto out;
}
if (!sctp_assoc_lookup_laddr(asoc, laddr)) break;
addr_buf += af->sockaddr_len;
} if (i < addrcnt) continue;
/* Find one address in the association's bind address list * that is not in the packed array of addresses. This is to * make sure that we do not delete all the addresses in the * association.
*/
bp = &asoc->base.bind_addr;
laddr = sctp_find_unmatch_addr(bp, (union sctp_addr *)addrs,
addrcnt, sp); if ((laddr == NULL) && (addrcnt == 1)) { if (asoc->asconf_addr_del_pending) continue;
asoc->asconf_addr_del_pending =
kzalloc(sizeof(union sctp_addr), GFP_ATOMIC); if (asoc->asconf_addr_del_pending == NULL) {
retval = -ENOMEM; goto out;
}
asoc->asconf_addr_del_pending->sa.sa_family =
addrs->sa_family;
asoc->asconf_addr_del_pending->v4.sin_port =
htons(bp->port); if (addrs->sa_family == AF_INET) { struct sockaddr_in *sin;
/* We do not need RCU protection throughout this loop * because this is done under a socket lock from the * setsockopt call.
*/
chunk = sctp_make_asconf_update_ip(asoc, laddr, addrs, addrcnt,
SCTP_PARAM_DEL_IP); if (!chunk) {
retval = -ENOMEM; goto out;
}
skip_mkasconf: /* Reset use_as_src flag for the addresses in the bind address * list that are to be deleted.
*/
addr_buf = addrs; for (i = 0; i < addrcnt; i++) {
laddr = addr_buf;
af = sctp_get_af_specific(laddr->v4.sin_family);
list_for_each_entry(saddr, &bp->address_list, list) { if (sctp_cmp_addr_exact(&saddr->a, laddr))
saddr->state = SCTP_ADDR_DEL;
}
addr_buf += af->sockaddr_len;
}
/* Update the route and saddr entries for all the transports * as some of the addresses in the bind address list are * about to be deleted and cannot be used as source addresses.
*/
list_for_each_entry(transport, &asoc->peer.transport_addr_list,
transports) {
sctp_transport_route(transport, NULL,
sctp_sk(asoc->base.sk));
}
if (stored) /* We don't need to transmit ASCONF */ continue;
retval = sctp_send_asconf(asoc, chunk);
}
out: return retval;
}
/* set addr events to assocs in the endpoint. ep and addr_wq must be locked */ int sctp_asconf_mgmt(struct sctp_sock *sp, struct sctp_sockaddr_entry *addrw)
{ struct sock *sk = sctp_opt2sk(sp); union sctp_addr *addr; struct sctp_af *af;
/* It is safe to write port space in caller. */
addr = &addrw->a;
addr->v4.sin_port = htons(sp->ep->base.bind_addr.port);
af = sctp_get_af_specific(addr->sa.sa_family); if (!af) return -EINVAL; if (sctp_verify_addr(sk, addr, af->sockaddr_len)) return -EINVAL;
/* Helper for tunneling sctp_bindx() requests through sctp_setsockopt() * * API 8.1 * int sctp_bindx(int sd, struct sockaddr *addrs, int addrcnt, * int flags); * * If sd is an IPv4 socket, the addresses passed must be IPv4 addresses. * If the sd is an IPv6 socket, the addresses passed can either be IPv4 * or IPv6 addresses. * * A single address may be specified as INADDR_ANY or IN6ADDR_ANY, see * Section 3.1.2 for this usage. * * addrs is a pointer to an array of one or more socket addresses. Each * address is contained in its appropriate structure (i.e. struct * sockaddr_in or struct sockaddr_in6) the family of the address type * must be used to distinguish the address length (note that this * representation is termed a "packed array" of addresses). The caller * specifies the number of addresses in the array with addrcnt. * * On success, sctp_bindx() returns 0. On failure, sctp_bindx() returns * -1, and sets errno to the appropriate error code. * * For SCTP, the port given in each socket address must be the same, or * sctp_bindx() will fail, setting errno to EINVAL. * * The flags parameter is formed from the bitwise OR of zero or more of * the following currently defined flags: * * SCTP_BINDX_ADD_ADDR * * SCTP_BINDX_REM_ADDR * * SCTP_BINDX_ADD_ADDR directs SCTP to add the given addresses to the * association, and SCTP_BINDX_REM_ADDR directs SCTP to remove the given * addresses from the association. The two flags are mutually exclusive; * if both are given, sctp_bindx() will fail with EINVAL. A caller may * not remove all addresses from an association; sctp_bindx() will * reject such an attempt with EINVAL. * * An application can use sctp_bindx(SCTP_BINDX_ADD_ADDR) to associate * additional addresses with an endpoint after calling bind(). Or use * sctp_bindx(SCTP_BINDX_REM_ADDR) to remove some addresses a listening * socket is associated with so that no new association accepted will be * associated with those addresses. If the endpoint supports dynamic * address a SCTP_BINDX_REM_ADDR or SCTP_BINDX_ADD_ADDR may cause a * endpoint to send the appropriate message to the peer to change the * peers address lists. * * Adding and removing addresses from a connected association is * optional functionality. Implementations that do not support this * functionality should return EOPNOTSUPP. * * Basically do nothing but copying the addresses from user to kernel * land and invoking either sctp_bindx_add() or sctp_bindx_rem() on the sk. * This is used for tunneling the sctp_bindx() request through sctp_setsockopt() * from userspace. * * On exit there is no need to do sockfd_put(), sys_setsockopt() does * it. * * sk The sk of the socket * addrs The pointer to the addresses * addrssize Size of the addrs buffer * op Operation to perform (add or remove, see the flags of * sctp_bindx) * * Returns 0 if ok, <0 errno code on error.
*/ staticint sctp_setsockopt_bindx(struct sock *sk, struct sockaddr *addrs, int addrs_size, int op)
{ int err; int addrcnt = 0; int walk_size = 0; struct sockaddr *sa_addr; void *addr_buf = addrs; struct sctp_af *af;
/* Walk through the addrs buffer and count the number of addresses. */ while (walk_size < addrs_size) { if (walk_size + sizeof(sa_family_t) > addrs_size) return -EINVAL;
sa_addr = addr_buf;
af = sctp_get_af_specific(sa_addr->sa_family);
/* If the address family is not supported or if this address * causes the address buffer to overflow return EINVAL.
*/ if (!af || (walk_size + af->sockaddr_len) > addrs_size) return -EINVAL;
addrcnt++;
addr_buf += af->sockaddr_len;
walk_size += af->sockaddr_len;
}
/* Do the work. */ switch (op) { case SCTP_BINDX_ADD_ADDR: /* Allow security module to validate bindx addresses. */
err = security_sctp_bind_connect(sk, SCTP_SOCKOPT_BINDX_ADD,
addrs, addrs_size); if (err) return err;
err = sctp_bindx_add(sk, addrs, addrcnt); if (err) return err; return sctp_send_asconf_add_ip(sk, addrs, addrcnt); case SCTP_BINDX_REM_ADDR:
err = sctp_bindx_rem(sk, addrs, addrcnt); if (err) return err; return sctp_send_asconf_del_ip(sk, addrs, addrcnt);
default: return -EINVAL;
}
}
staticint sctp_bind_add(struct sock *sk, struct sockaddr *addrs, int addrlen)
{ int err;
if (init->sinit_num_ostreams) {
__u16 outcnt = init->sinit_num_ostreams;
asoc->c.sinit_num_ostreams = outcnt; /* outcnt has been changed, need to re-init stream */
err = sctp_stream_init(&asoc->stream, outcnt, 0, GFP_KERNEL); if (err) goto free;
}
if (init->sinit_max_instreams)
asoc->c.sinit_max_instreams = init->sinit_max_instreams;
if (init->sinit_max_attempts)
asoc->max_init_attempts = init->sinit_max_attempts;
if (init->sinit_max_init_timeo)
asoc->max_init_timeo =
msecs_to_jiffies(init->sinit_max_init_timeo);
staticint sctp_connect_add_peer(struct sctp_association *asoc, union sctp_addr *daddr, int addr_len)
{ struct sctp_endpoint *ep = asoc->ep; struct sctp_association *old; struct sctp_transport *t; int err;
err = sctp_verify_addr(ep->base.sk, daddr, addr_len); if (err) return err;
old = sctp_endpoint_lookup_assoc(ep, daddr, &t); if (old && old != asoc) return old->state >= SCTP_STATE_ESTABLISHED ? -EISCONN
: -EALREADY;
if (sctp_endpoint_is_peeled_off(ep, daddr)) return -EADDRNOTAVAIL;
t = sctp_assoc_add_peer(asoc, daddr, GFP_KERNEL, SCTP_UNKNOWN); if (!t) return -ENOMEM;
return 0;
}
/* __sctp_connect(struct sock* sk, struct sockaddr *kaddrs, int addrs_size) * * Common routine for handling connect() and sctp_connectx(). * Connect will come in with just a single address.
*/ staticint __sctp_connect(struct sock *sk, struct sockaddr *kaddrs, int addrs_size, int flags, sctp_assoc_t *assoc_id)
{ struct sctp_sock *sp = sctp_sk(sk); struct sctp_endpoint *ep = sp->ep; struct sctp_transport *transport; struct sctp_association *asoc; void *addr_buf = kaddrs; union sctp_addr *daddr; struct sctp_af *af; int walk_size, err; long timeo;
/* In case the user of sctp_connectx() wants an association * id back, assign one now.
*/ if (assoc_id) {
err = sctp_assoc_set_id(asoc, GFP_KERNEL); if (err < 0) goto out_free;
}
out_free:
pr_debug("%s: took out_free path with asoc:%p kaddrs:%p err:%d\n",
__func__, asoc, kaddrs, err);
sctp_association_free(asoc); return err;
}
/* Helper for tunneling sctp_connectx() requests through sctp_setsockopt() * * API 8.9 * int sctp_connectx(int sd, struct sockaddr *addrs, int addrcnt, * sctp_assoc_t *asoc); * * If sd is an IPv4 socket, the addresses passed must be IPv4 addresses. * If the sd is an IPv6 socket, the addresses passed can either be IPv4 * or IPv6 addresses. * * A single address may be specified as INADDR_ANY or IN6ADDR_ANY, see * Section 3.1.2 for this usage. * * addrs is a pointer to an array of one or more socket addresses. Each * address is contained in its appropriate structure (i.e. struct * sockaddr_in or struct sockaddr_in6) the family of the address type * must be used to distengish the address length (note that this * representation is termed a "packed array" of addresses). The caller * specifies the number of addresses in the array with addrcnt. * * On success, sctp_connectx() returns 0. It also sets the assoc_id to * the association id of the new association. On failure, sctp_connectx() * returns -1, and sets errno to the appropriate error code. The assoc_id * is not touched by the kernel. * * For SCTP, the port given in each socket address must be the same, or * sctp_connectx() will fail, setting errno to EINVAL. * * An application can use sctp_connectx to initiate an association with * an endpoint that is multi-homed. Much like sctp_bindx() this call * allows a caller to specify multiple addresses at which a peer can be * reached. The way the SCTP stack uses the list of addresses to set up * the association is implementation dependent. This function only * specifies that the stack will try to make use of all the addresses in * the list when needed. * * Note that the list of addresses passed in is only used for setting up * the association. It does not necessarily equal the set of addresses * the peer uses for the resulting association. If the caller wants to * find out the set of peer addresses, it must use sctp_getpaddrs() to * retrieve them after the association has been set up. * * Basically do nothing but copying the addresses from user to kernel * land and invoking either sctp_connectx(). This is used for tunneling * the sctp_connectx() request through sctp_setsockopt() from userspace. * * On exit there is no need to do sockfd_put(), sys_setsockopt() does * it. * * sk The sk of the socket * addrs The pointer to the addresses * addrssize Size of the addrs buffer * * Returns >=0 if ok, <0 errno code on error.
*/ staticint __sctp_setsockopt_connectx(struct sock *sk, struct sockaddr *kaddrs, int addrs_size, sctp_assoc_t *assoc_id)
{ int err = 0, flags = 0;
/* in-kernel sockets don't generally have a file allocated to them * if all they do is call sock_create_kern().
*/ if (sk->sk_socket->file)
flags = sk->sk_socket->file->f_flags;
/* * This is an older interface. It's kept for backward compatibility * to the option that doesn't provide association id.
*/ staticint sctp_setsockopt_connectx_old(struct sock *sk, struct sockaddr *kaddrs, int addrs_size)
{ return __sctp_setsockopt_connectx(sk, kaddrs, addrs_size, NULL);
}
/* * New interface for the API. The since the API is done with a socket * option, to make it simple we feed back the association id is as a return * indication to the call. Error is always negative and association id is * always positive.
*/ staticint sctp_setsockopt_connectx(struct sock *sk, struct sockaddr *kaddrs, int addrs_size)
{
sctp_assoc_t assoc_id = 0; int err = 0;
/* * New (hopefully final) interface for the API. * We use the sctp_getaddrs_old structure so that use-space library * can avoid any unnecessary allocations. The only different part * is that we store the actual length of the address buffer into the * addrs_num structure member. That way we can re-use the existing * code.
*/ #ifdef CONFIG_COMPAT struct compat_sctp_getaddrs_old {
sctp_assoc_t assoc_id;
s32 addr_num;
compat_uptr_t addrs; /* struct sockaddr * */
}; #endif
staticint sctp_getsockopt_connectx3(struct sock *sk, int len, char __user *optval, int __user *optlen)
{ struct sctp_getaddrs_old param;
sctp_assoc_t assoc_id = 0; struct sockaddr *kaddrs; int err = 0;
#ifdef CONFIG_COMPAT if (in_compat_syscall()) { struct compat_sctp_getaddrs_old param32;
if (len < sizeof(param32)) return -EINVAL; if (copy_from_user(¶m32, optval, sizeof(param32))) return -EFAULT;
kaddrs = memdup_user(param.addrs, param.addr_num); if (IS_ERR(kaddrs)) return PTR_ERR(kaddrs);
err = __sctp_setsockopt_connectx(sk, kaddrs, param.addr_num, &assoc_id);
kfree(kaddrs); if (err == 0 || err == -EINPROGRESS) { if (copy_to_user(optval, &assoc_id, sizeof(assoc_id))) return -EFAULT; if (put_user(sizeof(assoc_id), optlen)) return -EFAULT;
}
return err;
}
/* API 3.1.4 close() - UDP Style Syntax * Applications use close() to perform graceful shutdown (as described in * Section 10.1 of [SCTP]) on ALL the associations currently represented * by a UDP-style socket. * * The syntax is * * ret = close(int sd); * * sd - the socket descriptor of the associations to be closed. * * To gracefully shutdown a specific association represented by the * UDP-style socket, an application should use the sendmsg() call, * passing no user data, but including the appropriate flag in the * ancillary data (see Section xxxx). * * If sd in the close() call is a branched-off socket representing only * one association, the shutdown is performed on that association only. * * 4.1.6 close() - TCP Style Syntax * * Applications use close() to gracefully close down an association. * * The syntax is: * * int close(int sd); * * sd - the socket descriptor of the association to be closed. * * After an application calls close() on a socket descriptor, no further * socket operations will succeed on that descriptor. * * API 7.1.4 SO_LINGER * * An application using the TCP-style socket can use this option to * perform the SCTP ABORT primitive. The linger option structure is: * * struct linger { * int l_onoff; // option on/off * int l_linger; // linger time * }; * * To enable the option, set l_onoff to 1. If the l_linger value is set * to 0, calling close() is the same as the ABORT primitive. If the * value is set to a negative value, the setsockopt() call will return * an error. If the value is set to a positive value linger_time, the * close() can be blocked for at most linger_time ms. If the graceful * shutdown phase does not finish during this period, close() will * return but the graceful shutdown phase continues in the system.
*/ staticvoid sctp_close(struct sock *sk, long timeout)
{ struct net *net = sock_net(sk); struct sctp_endpoint *ep; struct sctp_association *asoc; struct list_head *pos, *temp; unsignedint data_was_unread;
/* Clean up any skbs sitting on the receive queue. */
data_was_unread = sctp_queue_purge_ulpevents(&sk->sk_receive_queue);
data_was_unread += sctp_queue_purge_ulpevents(&sctp_sk(sk)->pd_lobby);
/* Walk all associations on an endpoint. */
list_for_each_safe(pos, temp, &ep->asocs) {
asoc = list_entry(pos, struct sctp_association, asocs);
if (sctp_style(sk, TCP)) { /* A closed association can still be in the list if * it belongs to a TCP-style listening socket that is * not yet accepted. If so, free it. If not, send an * ABORT or SHUTDOWN based on the linger options.
*/ if (sctp_state(asoc, CLOSED)) {
sctp_association_free(asoc); continue;
}
}
/* On a TCP-style socket, block for at most linger_time if set. */ if (sctp_style(sk, TCP) && timeout)
sctp_wait_for_close(sk, timeout);
/* This will run the backlog queue. */
release_sock(sk);
/* Supposedly, no process has access to the socket, but * the net layers still may. * Also, sctp_destroy_sock() needs to be called with addr_wq_lock * held and that should be grabbed before socket lock.
*/
spin_lock_bh(&net->sctp.addr_wq_lock);
bh_lock_sock_nested(sk);
/* Hold the sock, since sk_common_release() will put sock_put() * and we have just a little more cleanup.
*/
sock_hold(sk);
sk_common_release(sk);
/* Handle EPIPE error. */ staticint sctp_error(struct sock *sk, int flags, int err)
{ if (err == -EPIPE)
err = sock_error(sk) ? : -EPIPE; if (err == -EPIPE && !(flags & MSG_NOSIGNAL))
send_sig(SIGPIPE, current, 0); return err;
}
/* API 3.1.3 sendmsg() - UDP Style Syntax * * An application uses sendmsg() and recvmsg() calls to transmit data to * and receive data from its peer. * * ssize_t sendmsg(int socket, const struct msghdr *message, * int flags); * * socket - the socket descriptor of the endpoint. * message - pointer to the msghdr structure which contains a single * user message and possibly some ancillary data. * * See Section 5 for complete description of the data * structures. * * flags - flags sent or received with the user message, see Section * 5 for complete description of the flags. * * Note: This function could use a rewrite especially when explicit * connect support comes in.
*/ /* BUG: We do not implement the equivalent of sk_stream_wait_memory(). */
/* Label connection socket for first association 1-to-many * style for client sequence socket()->sendmsg(). This * needs to be done before sctp_assoc_add_peer() as that will * set up the initial packet that needs to account for any * security ip options (CIPSO/CALIPSO) added to the packet.
*/
af = sctp_get_af_specific(daddr->sa.sa_family); if (!af) return -EINVAL;
err = security_sctp_bind_connect(sk, SCTP_SENDMSG_CONNECT,
(struct sockaddr *)daddr,
af->sockaddr_len); if (err < 0) return err;
if (!cmsgs->prinfo)
sinfo->sinfo_flags = asoc->default_flags;
}
if (!cmsgs->srinfo && !cmsgs->prinfo)
sinfo->sinfo_timetolive = asoc->default_timetolive;
if (cmsgs->authinfo) { /* Reuse sinfo_tsn to indicate that authinfo was set and * sinfo_ssn to save the keyid on tx path.
*/
sinfo->sinfo_tsn = 1;
sinfo->sinfo_ssn = cmsgs->authinfo->auth_keynumber;
}
}
/* This is an extended version of skb_pull() that removes the data from the * start of a skb even when data is spread across the list of skb's in the * frag_list. len specifies the total amount of data that needs to be removed. * when 'len' bytes could be removed from the skb, it returns 0. * If 'len' exceeds the total skb length, it returns the no. of bytes that * could not be removed.
*/ staticint sctp_skb_pull(struct sk_buff *skb, int len)
{ struct sk_buff *list; int skb_len = skb_headlen(skb); int rlen;
if (len <= skb_len) {
__skb_pull(skb, len); return 0;
}
len -= skb_len;
__skb_pull(skb, skb_len);
/* API 3.1.3 recvmsg() - UDP Style Syntax * * ssize_t recvmsg(int socket, struct msghdr *message, * int flags); * * socket - the socket descriptor of the endpoint. * message - pointer to the msghdr structure which contains a single * user message and possibly some ancillary data. * * See Section 5 for complete description of the data * structures. * * flags - flags sent or received with the user message, see Section * 5 for complete description of the flags.
*/ staticint sctp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int flags, int *addr_len)
{ struct sctp_ulpevent *event = NULL; struct sctp_sock *sp = sctp_sk(sk); struct sk_buff *skb, *head_skb; int copied; int err = 0; int skb_len;
/* Check if we allow SCTP_NXTINFO. */ if (sp->recvnxtinfo)
sctp_ulpevent_read_nxtinfo(event, msg, sk); /* Check if we allow SCTP_RCVINFO. */ if (sp->recvrcvinfo)
sctp_ulpevent_read_rcvinfo(event, msg); /* Check if we allow SCTP_SNDRCVINFO. */ if (sctp_ulpevent_type_enabled(sp->subscribe, SCTP_DATA_IO_EVENT))
sctp_ulpevent_read_sndrcvinfo(event, msg);
err = copied;
/* If skb's length exceeds the user's buffer, update the skb and * push it back to the receive_queue so that the next call to * recvmsg() will return the remaining data. Don't set MSG_EOR.
*/ if (skb_len > copied) {
msg->msg_flags &= ~MSG_EOR; if (flags & MSG_PEEK) goto out_free;
sctp_skb_pull(skb, copied);
skb_queue_head(&sk->sk_receive_queue, skb);
/* When only partial message is copied to the user, increase * rwnd by that amount. If all the data in the skb is read, * rwnd is updated when the event is freed.
*/ if (!sctp_ulpevent_is_notification(event))
sctp_assoc_rwnd_increase(event->asoc, copied); goto out;
} elseif ((event->msg_flags & MSG_NOTIFICATION) ||
(event->msg_flags & MSG_EOR))
msg->msg_flags |= MSG_EOR; else
msg->msg_flags &= ~MSG_EOR;
out_free: if (flags & MSG_PEEK) { /* Release the skb reference acquired after peeking the skb in * sctp_skb_recv_datagram().
*/
kfree_skb(skb);
} else { /* Free the event which includes releasing the reference to * the owner of the skb, freeing the skb and updating the * rwnd.
*/
sctp_ulpevent_free(event);
}
out:
release_sock(sk); return err;
}
/* 7.1.12 Enable/Disable message fragmentation (SCTP_DISABLE_FRAGMENTS) * * This option is a on/off flag. If enabled no SCTP message * fragmentation will be performed. Instead if a message being sent * exceeds the current PMTU size, the message will NOT be sent and * instead a error will be indicated to the user.
*/ staticint sctp_setsockopt_disable_fragments(struct sock *sk, int *val, unsignedint optlen)
{ if (optlen < sizeof(int)) return -EINVAL;
sctp_sk(sk)->disable_fragments = (*val == 0) ? 0 : 1; return 0;
}
/* At the time when a user app subscribes to SCTP_SENDER_DRY_EVENT, * if there is no data to be sent or retransmit, the stack will * immediately send up this notification.
*/ if (sctp_ulpevent_type_enabled(sp->subscribe, SCTP_SENDER_DRY_EVENT)) { struct sctp_ulpevent *event;
asoc = sctp_id2assoc(sk, 0); if (asoc && sctp_outq_is_empty(&asoc->outqueue)) {
event = sctp_ulpevent_make_sender_dry_event(asoc,
GFP_USER | __GFP_NOWARN); if (!event) return -ENOMEM;
/* 7.1.8 Automatic Close of associations (SCTP_AUTOCLOSE) * * This socket option is applicable to the UDP-style socket only. When * set it will cause associations that are idle for more than the * specified number of seconds to automatically close. An association * being idle is defined an association that has NOT sent or received * user data. The special value of '0' indicates that no automatic * close of any associations should be performed. The option expects an * integer defining the number of seconds of idle time before an * association is closed.
*/ staticint sctp_setsockopt_autoclose(struct sock *sk, u32 *optval, unsignedint optlen)
{ struct sctp_sock *sp = sctp_sk(sk); struct net *net = sock_net(sk);
/* Applicable to UDP-style socket only */ if (sctp_style(sk, TCP)) return -EOPNOTSUPP; if (optlen != sizeof(int)) return -EINVAL;
sp->autoclose = *optval; if (sp->autoclose > net->sctp.max_autoclose)
sp->autoclose = net->sctp.max_autoclose;
return 0;
}
/* 7.1.13 Peer Address Parameters (SCTP_PEER_ADDR_PARAMS) * * Applications can enable or disable heartbeats for any peer address of * an association, modify an address's heartbeat interval, force a * heartbeat to be sent immediately, and adjust the address's maximum * number of retransmissions sent before an address is considered * unreachable. The following structure is used to access and modify an * address's parameters: * * struct sctp_paddrparams { * sctp_assoc_t spp_assoc_id; * struct sockaddr_storage spp_address; * uint32_t spp_hbinterval; * uint16_t spp_pathmaxrxt; * uint32_t spp_pathmtu; * uint32_t spp_sackdelay; * uint32_t spp_flags; * uint32_t spp_ipv6_flowlabel;
--> --------------------
--> maximum size reached
--> --------------------
Messung V0.5
¤ Dauer der Verarbeitung: 0.29 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.