📄 socket.c
字号:
/* SCTP kernel reference 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 reference 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. * * The SCTP reference implementation is free software; * you can redistribute it and/or modify it under the terms of * the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * The SCTP reference implementation is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * ************************ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU CC; see the file COPYING. If not, write to * the Free Software Foundation, 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. * * Please send any bug reports or fixes you make to the * email address(es): * lksctp developers <lksctp-developers@lists.sourceforge.net> * * Or submit a bug report through the following website: * http://www.sf.net/projects/lksctp * * 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> * * Any bugs reported given to us we will try to fix... any fixes shared will * be incorporated into the next SCTP release. */#include <linux/config.h>#include <linux/types.h>#include <linux/kernel.h>#include <linux/wait.h>#include <linux/time.h>#include <linux/ip.h>#include <linux/fcntl.h>#include <linux/poll.h>#include <linux/init.h>#include <linux/crypto.h>#include <net/ip.h>#include <net/icmp.h>#include <net/route.h>#include <net/ipv6.h>#include <net/inet_common.h>#include <linux/socket.h> /* for sa_family_t */#include <net/sock.h>#include <net/sctp/sctp.h>#include <net/sctp/sm.h>/* WARNING: Please do not remove the SCTP_STATIC attribute to * any of the functions below as they are used to export functions * used by a project regression testsuite. *//* Forward declarations for internal helper functions. */static int sctp_writeable(struct sock *sk);static void sctp_wfree(struct sk_buff *skb);static int sctp_wait_for_sndbuf(struct sctp_association *, long *timeo_p, size_t msg_len);static int sctp_wait_for_packet(struct sock * sk, int *err, long *timeo_p);static int sctp_wait_for_connect(struct sctp_association *, long *timeo_p);static int sctp_wait_for_accept(struct sock *sk, long timeo);static void sctp_wait_for_close(struct sock *sk, long timeo);static struct sctp_af *sctp_sockaddr_af(struct sctp_sock *opt, union sctp_addr *addr, int len);static int sctp_bindx_add(struct sock *, struct sockaddr *, int);static int sctp_bindx_rem(struct sock *, struct sockaddr *, int);static int sctp_send_asconf_add_ip(struct sock *, struct sockaddr *, int);static int sctp_send_asconf_del_ip(struct sock *, struct sockaddr *, int);static int sctp_send_asconf(struct sctp_association *asoc, struct sctp_chunk *chunk);static int sctp_do_bind(struct sock *, union sctp_addr *, int);static int sctp_autobind(struct sock *sk);static void sctp_sock_migrate(struct sock *, struct sock *, struct sctp_association *, sctp_socket_type_t);static char *sctp_hmac_alg = SCTP_COOKIE_HMAC_ALG;extern kmem_cache_t *sctp_bucket_cachep;/* Get the sndbuf space available at the time on the association. */static inline int sctp_wspace(struct sctp_association *asoc){ struct sock *sk = asoc->base.sk; int amt = 0; if (asoc->ep->sndbuf_policy) { /* make sure that no association uses more than sk_sndbuf */ amt = sk->sk_sndbuf - asoc->sndbuf_used; } else { /* do socket level accounting */ amt = sk->sk_sndbuf - atomic_read(&sk->sk_wmem_alloc); } if (amt < 0) amt = 0; return amt;}/* Increment the used sndbuf space count of the corresponding association by * the size of the outgoing data chunk. * Also, set the skb destructor for sndbuf accounting later. * * Since it is always 1-1 between chunk and skb, and also a new skb is always * allocated for chunk bundling in sctp_packet_transmit(), we can use the * destructor in the data chunk skb for the purpose of the sndbuf space * tracking. */static inline void sctp_set_owner_w(struct sctp_chunk *chunk){ struct sctp_association *asoc = chunk->asoc; struct sock *sk = asoc->base.sk; /* The sndbuf space is tracked per association. */ sctp_association_hold(asoc); skb_set_owner_w(chunk->skb, sk); chunk->skb->destructor = sctp_wfree; /* Save the chunk pointer in skb for sctp_wfree to use later. */ *((struct sctp_chunk **)(chunk->skb->cb)) = chunk; asoc->sndbuf_used += SCTP_DATA_SNDSIZE(chunk) + sizeof(struct sk_buff) + sizeof(struct sctp_chunk); sk->sk_wmem_queued += SCTP_DATA_SNDSIZE(chunk) + sizeof(struct sk_buff) + sizeof(struct sctp_chunk); atomic_add(sizeof(struct sctp_chunk), &sk->sk_wmem_alloc);}/* Verify that this is a valid address. */static inline int sctp_verify_addr(struct sock *sk, union sctp_addr *addr, int len){ struct sctp_af *af; /* Verify basic sockaddr. */ af = sctp_sockaddr_af(sctp_sk(sk), addr, len); if (!af) return -EINVAL; /* Is this a valid SCTP address? */ if (!af->addr_valid(addr, sctp_sk(sk))) 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)) 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 || (id == (sctp_assoc_t)-1)) return NULL; spin_lock_bh(&sctp_assocs_id_lock); asoc = (struct sctp_association *)idr_find(&sctp_assocs_id, (int)id); spin_unlock_bh(&sctp_assocs_id_lock); if (!asoc || (asoc->base.sk != sk) || asoc->base.dead) return NULL; return asoc;}/* Look up the transport from an address and an assoc id. If both address and * id are specified, the associations matching the address and the id should be * the same. */static struct sctp_transport *sctp_addr_id2transport(struct sock *sk, struct sockaddr_storage *addr, sctp_assoc_t id){ struct sctp_association *addr_asoc = NULL, *id_asoc = NULL; struct sctp_transport *transport; union sctp_addr *laddr = (union sctp_addr *)addr; laddr->v4.sin_port = ntohs(laddr->v4.sin_port); addr_asoc = sctp_endpoint_lookup_assoc(sctp_sk(sk)->ep, (union sctp_addr *)addr, &transport); laddr->v4.sin_port = htons(laddr->v4.sin_port); if (!addr_asoc) return NULL; id_asoc = sctp_id2assoc(sk, id); if (id_asoc && (id_asoc != addr_asoc)) return NULL; sctp_get_pf_specific(sk->sk_family)->addr_v4map(sctp_sk(sk), (union sctp_addr *)addr); return transport;}/* API 3.1.2 bind() - UDP Style Syntax * The syntax of bind() is, * * ret = bind(int sd, struct sockaddr *addr, int addrlen); * * sd - the socket descriptor returned by socket(). * addr - the address structure (struct sockaddr_in or struct * sockaddr_in6 [RFC 2553]), * addr_len - the size of the address structure. */SCTP_STATIC int sctp_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len){ int retval = 0; sctp_lock_sock(sk); SCTP_DEBUG_PRINTK("sctp_bind(sk: %p, uaddr: %p, addr_len: %d)\n", sk, uaddr, addr_len); /* Disallow binding twice. */ if (!sctp_sk(sk)->ep->base.bind_addr.port) retval = sctp_do_bind(sk, (union sctp_addr *)uaddr, addr_len); else retval = -EINVAL; sctp_release_sock(sk); return retval;}static long sctp_get_port_local(struct sock *, union sctp_addr *);/* Verify this is a valid sockaddr. */static struct sctp_af *sctp_sockaddr_af(struct sctp_sock *opt, union sctp_addr *addr, int len){ struct sctp_af *af; /* Check minimum size. */ if (len < sizeof (struct sockaddr)) return NULL; /* Does this PF support this AF? */ if (!opt->pf->af_supported(addr->sa.sa_family, 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;}/* Bind a local address either to an endpoint or to an association. */SCTP_STATIC int sctp_do_bind(struct sock *sk, union sctp_addr *addr, int len){ struct sctp_sock *sp = sctp_sk(sk); struct sctp_endpoint *ep = sp->ep; struct sctp_bind_addr *bp = &ep->base.bind_addr; struct sctp_af *af; unsigned short snum; int ret = 0; SCTP_DEBUG_PRINTK("sctp_do_bind(sk: %p, newaddr: %p, len: %d)\n", sk, addr, len); /* Common sockaddr verification. */ af = sctp_sockaddr_af(sp, addr, len); if (!af) return -EINVAL; /* PF specific bind() address verification. */ if (!sp->pf->bind_verify(sp, addr)) return -EADDRNOTAVAIL; snum= ntohs(addr->v4.sin_port); SCTP_DEBUG_PRINTK("sctp_do_bind: port: %d, new port: %d\n", bp->port, snum); /* We must either be unbound, or bind to the same port. */ if (bp->port && (snum != bp->port)) { SCTP_DEBUG_PRINTK("sctp_do_bind:" " New port %d does not match existing port " "%d.\n", snum, bp->port); return -EINVAL; } if (snum && snum < PROT_SOCK && !capable(CAP_NET_BIND_SERVICE)) return -EACCES; /* Make sure we are allowed to bind here. * The function sctp_get_port_local() does duplicate address * detection. */ if ((ret = sctp_get_port_local(sk, addr))) { if (ret == (long) sk) { /* This endpoint has a conflicting address. */ return -EINVAL; } else { return -EADDRINUSE; } } /* Refresh ephemeral port. */ if (!bp->port) bp->port = inet_sk(sk)->num; /* Add the address to the bind address list. */ sctp_local_bh_disable(); sctp_write_lock(&ep->base.addr_lock); /* Use GFP_ATOMIC since BHs are disabled. */ addr->v4.sin_port = ntohs(addr->v4.sin_port); ret = sctp_add_bind_addr(bp, addr, GFP_ATOMIC); addr->v4.sin_port = htons(addr->v4.sin_port); sctp_write_unlock(&ep->base.addr_lock); sctp_local_bh_enable(); /* Copy back into socket for getsockname() use. */ if (!ret) { inet_sk(sk)->sport = htons(inet_sk(sk)->num); af->to_sk_saddr(addr, sk); } return ret;} /* ADDIP Section 4.1.1 Congestion Control of ASCONF Chunks * * R1) One and only one ASCONF Chunk MAY be in transit and unacknowledged * at any one time. If a sender, after sending an ASCONF chunk, decides * it needs to transfer another ASCONF Chunk, it MUST wait until the * ASCONF-ACK Chunk returns from the previous ASCONF Chunk before sending a * subsequent ASCONF. Note this restriction binds each side, so at any * time two ASCONF may be in-transit on any given association (one sent * from each endpoint). */static int sctp_send_asconf(struct sctp_association *asoc, struct sctp_chunk *chunk){ int retval = 0; /* If there is an outstanding ASCONF chunk, queue it for later * transmission. */ if (asoc->addip_last_asconf) { __skb_queue_tail(&asoc->addip_chunks, (struct sk_buff *)chunk); goto out; } /* Hold the chunk until an ASCONF_ACK is received. */ sctp_chunk_hold(chunk); retval = sctp_primitive_ASCONF(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. */int sctp_bindx_add(struct sock *sk, struct sockaddr *addrs, int addrcnt){ int cnt; int retval = 0; void *addr_buf; struct sockaddr *sa_addr; struct sctp_af *af; SCTP_DEBUG_PRINTK("sctp_bindx_add (sk: %p, addrs: %p, addrcnt: %d)\n", sk, addrs, addrcnt);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -