Skip to content
Snippets Groups Projects
socket.c 189 KiB
Newer Older
  • Learn to ignore specific revisions
  • Linus Torvalds's avatar
    Linus Torvalds committed
    	return 0;
    }
    
    
    /*
     * 7.1.29.  Set or Get the default context (SCTP_CONTEXT)
     *
     * The context field in the sctp_sndrcvinfo structure is normally only
     * used when a failed message is retrieved holding the value that was
     * sent down on the actual send call.  This option allows the setting of
     * a default context on an association basis that will be received on
     * reading messages from the peer.  This is especially helpful in the
     * one-2-many model for an application to keep some reference to an
     * internal state machine that is processing messages on the
     * association.  Note that the setting of this value only effects
     * received messages from the peer and does not effect the value that is
     * saved with outbound messages.
     */
    static int sctp_setsockopt_context(struct sock *sk, char __user *optval,
    
    				   unsigned int optlen)
    
    {
    	struct sctp_assoc_value params;
    	struct sctp_sock *sp;
    	struct sctp_association *asoc;
    
    	if (optlen != sizeof(struct sctp_assoc_value))
    		return -EINVAL;
    	if (copy_from_user(&params, optval, optlen))
    		return -EFAULT;
    
    	sp = sctp_sk(sk);
    
    	if (params.assoc_id != 0) {
    		asoc = sctp_id2assoc(sk, params.assoc_id);
    		if (!asoc)
    			return -EINVAL;
    		asoc->default_rcv_context = params.assoc_value;
    	} else {
    		sp->default_rcv_context = params.assoc_value;
    	}
    
    	return 0;
    }
    
    
    /*
     * 7.1.24.  Get or set fragmented interleave (SCTP_FRAGMENT_INTERLEAVE)
     *
     * This options will at a minimum specify if the implementation is doing
     * fragmented interleave.  Fragmented interleave, for a one to many
     * socket, is when subsequent calls to receive a message may return
     * parts of messages from different associations.  Some implementations
     * may allow you to turn this value on or off.  If so, when turned off,
     * no fragment interleave will occur (which will cause a head of line
     * blocking amongst multiple associations sharing the same one to many
     * socket).  When this option is turned on, then each receive call may
     * come from a different association (thus the user must receive data
     * with the extended calls (e.g. sctp_recvmsg) to keep track of which
     * association each receive belongs to.
     *
     * This option takes a boolean value.  A non-zero value indicates that
     * fragmented interleave is on.  A value of zero indicates that
     * fragmented interleave is off.
     *
     * Note that it is important that an implementation that allows this
     * option to be turned on, have it off by default.  Otherwise an unaware
     * application using the one to many model may become confused and act
     * incorrectly.
     */
    static int sctp_setsockopt_fragment_interleave(struct sock *sk,
    					       char __user *optval,
    
    					       unsigned int optlen)
    
    {
    	int val;
    
    	if (optlen != sizeof(int))
    		return -EINVAL;
    	if (get_user(val, (int __user *)optval))
    		return -EFAULT;
    
    	sctp_sk(sk)->frag_interleave = (val == 0) ? 0 : 1;
    
    	return 0;
    }
    
    
     * 8.1.21.  Set or Get the SCTP Partial Delivery Point
    
     *       (SCTP_PARTIAL_DELIVERY_POINT)
    
     * This option will set or get the SCTP partial delivery point.  This
     * point is the size of a message where the partial delivery API will be
     * invoked to help free up rwnd space for the peer.  Setting this to a
    
     * lower value will cause partial deliveries to happen more often.  The
    
     * calls argument is an integer that sets or gets the partial delivery
    
     * point.  Note also that the call will fail if the user attempts to set
     * this value larger than the socket receive buffer size.
     *
     * Note that any single message having a length smaller than or equal to
     * the SCTP partial delivery point will be delivered in one single read
     * call as long as the user provided buffer is large enough to hold the
     * message.
    
     */
    static int sctp_setsockopt_partial_delivery_point(struct sock *sk,
    						  char __user *optval,
    
    						  unsigned int optlen)
    
    {
    	u32 val;
    
    	if (optlen != sizeof(u32))
    		return -EINVAL;
    	if (get_user(val, (int __user *)optval))
    		return -EFAULT;
    
    
    	/* Note: We double the receive buffer from what the user sets
    	 * it to be, also initial rwnd is based on rcvbuf/2.
    	 */
    	if (val > (sk->sk_rcvbuf >> 1))
    		return -EINVAL;
    
    
    	sctp_sk(sk)->pd_point = val;
    
    	return 0; /* is this the right error code? */
    }
    
    
    /*
     * 7.1.28.  Set or Get the maximum burst (SCTP_MAX_BURST)
     *
     * This option will allow a user to change the maximum burst of packets
     * that can be emitted by this association.  Note that the default value
     * is 4, and some implementations may restrict this setting so that it
     * can only be lowered.
     *
     * NOTE: This text doesn't seem right.  Do this on a socket basis with
     * future associations inheriting the socket value.
     */
    static int sctp_setsockopt_maxburst(struct sock *sk,
    				    char __user *optval,
    
    				    unsigned int optlen)
    
    	struct sctp_assoc_value params;
    	struct sctp_sock *sp;
    	struct sctp_association *asoc;
    
    		pr_warn("Use of int in max_burst socket option deprecated\n");
    		pr_warn("Use struct sctp_assoc_value instead\n");
    
    		if (copy_from_user(&val, optval, optlen))
    			return -EFAULT;
    	} else if (optlen == sizeof(struct sctp_assoc_value)) {
    		if (copy_from_user(&params, optval, optlen))
    			return -EFAULT;
    		val = params.assoc_value;
    		assoc_id = params.assoc_id;
    	} else
    
    	sp = sctp_sk(sk);
    
    	if (assoc_id != 0) {
    		asoc = sctp_id2assoc(sk, assoc_id);
    		if (!asoc)
    			return -EINVAL;
    		asoc->max_burst = val;
    	} else
    		sp->max_burst = val;
    
    /*
     * 7.1.18.  Add a chunk that must be authenticated (SCTP_AUTH_CHUNK)
     *
     * This set option adds a chunk type that the user is requesting to be
     * received only in an authenticated way.  Changes to the list of chunks
     * will only effect future associations on the socket.
     */
    static int sctp_setsockopt_auth_chunk(struct sock *sk,
    
    				      char __user *optval,
    				      unsigned int optlen)
    
    	if (!sctp_auth_enable)
    		return -EACCES;
    
    
    	if (optlen != sizeof(struct sctp_authchunk))
    		return -EINVAL;
    	if (copy_from_user(&val, optval, optlen))
    		return -EFAULT;
    
    	switch (val.sauth_chunk) {
    		case SCTP_CID_INIT:
    		case SCTP_CID_INIT_ACK:
    		case SCTP_CID_SHUTDOWN_COMPLETE:
    		case SCTP_CID_AUTH:
    			return -EINVAL;
    	}
    
    	/* add this chunk id to the endpoint */
    	return sctp_auth_ep_add_chunkid(sctp_sk(sk)->ep, val.sauth_chunk);
    }
    
    /*
     * 7.1.19.  Get or set the list of supported HMAC Identifiers (SCTP_HMAC_IDENT)
     *
     * This option gets or sets the list of HMAC algorithms that the local
     * endpoint requires the peer to use.
     */
    static int sctp_setsockopt_hmac_ident(struct sock *sk,
    
    				      char __user *optval,
    				      unsigned int optlen)
    
    {
    	struct sctp_hmacalgo *hmacs;
    
    	if (!sctp_auth_enable)
    		return -EACCES;
    
    
    	if (optlen < sizeof(struct sctp_hmacalgo))
    		return -EINVAL;
    
    
    	hmacs= memdup_user(optval, optlen);
    	if (IS_ERR(hmacs))
    		return PTR_ERR(hmacs);
    
    	idents = hmacs->shmac_num_idents;
    	if (idents == 0 || idents > SCTP_AUTH_NUM_HMACS ||
    	    (idents * sizeof(u16)) > (optlen - sizeof(struct sctp_hmacalgo))) {
    
    		err = -EINVAL;
    		goto out;
    	}
    
    	err = sctp_auth_ep_set_hmacs(sctp_sk(sk)->ep, hmacs);
    out:
    	kfree(hmacs);
    	return err;
    }
    
    /*
     * 7.1.20.  Set a shared key (SCTP_AUTH_KEY)
     *
     * This option will set a shared secret key which is used to build an
     * association shared key.
     */
    static int sctp_setsockopt_auth_key(struct sock *sk,
    				    char __user *optval,
    
    				    unsigned int optlen)
    
    {
    	struct sctp_authkey *authkey;
    	struct sctp_association *asoc;
    	int ret;
    
    
    	if (!sctp_auth_enable)
    		return -EACCES;
    
    
    	if (optlen <= sizeof(struct sctp_authkey))
    		return -EINVAL;
    
    
    	authkey= memdup_user(optval, optlen);
    	if (IS_ERR(authkey))
    		return PTR_ERR(authkey);
    
    	if (authkey->sca_keylength > optlen - sizeof(struct sctp_authkey)) {
    
    	asoc = sctp_id2assoc(sk, authkey->sca_assoc_id);
    	if (!asoc && authkey->sca_assoc_id && sctp_style(sk, UDP)) {
    		ret = -EINVAL;
    		goto out;
    	}
    
    	ret = sctp_auth_set_key(sctp_sk(sk)->ep, asoc, authkey);
    out:
    	kfree(authkey);
    	return ret;
    }
    
    /*
     * 7.1.21.  Get or set the active shared key (SCTP_AUTH_ACTIVE_KEY)
     *
     * This option will get or set the active shared key to be used to build
     * the association shared key.
     */
    static int sctp_setsockopt_active_key(struct sock *sk,
    
    				      char __user *optval,
    				      unsigned int optlen)
    
    {
    	struct sctp_authkeyid val;
    	struct sctp_association *asoc;
    
    
    	if (!sctp_auth_enable)
    		return -EACCES;
    
    
    	if (optlen != sizeof(struct sctp_authkeyid))
    		return -EINVAL;
    	if (copy_from_user(&val, optval, optlen))
    		return -EFAULT;
    
    	asoc = sctp_id2assoc(sk, val.scact_assoc_id);
    	if (!asoc && val.scact_assoc_id && sctp_style(sk, UDP))
    		return -EINVAL;
    
    	return sctp_auth_set_active_key(sctp_sk(sk)->ep, asoc,
    					val.scact_keynumber);
    }
    
    /*
     * 7.1.22.  Delete a shared key (SCTP_AUTH_DELETE_KEY)
     *
     * This set option will delete a shared secret key from use.
     */
    static int sctp_setsockopt_del_key(struct sock *sk,
    
    				   char __user *optval,
    				   unsigned int optlen)
    
    {
    	struct sctp_authkeyid val;
    	struct sctp_association *asoc;
    
    
    	if (!sctp_auth_enable)
    		return -EACCES;
    
    
    	if (optlen != sizeof(struct sctp_authkeyid))
    		return -EINVAL;
    	if (copy_from_user(&val, optval, optlen))
    		return -EFAULT;
    
    	asoc = sctp_id2assoc(sk, val.scact_assoc_id);
    	if (!asoc && val.scact_assoc_id && sctp_style(sk, UDP))
    		return -EINVAL;
    
    	return sctp_auth_del_key_id(sctp_sk(sk)->ep, asoc,
    				    val.scact_keynumber);
    
    }
    
    
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    /* API 6.2 setsockopt(), getsockopt()
     *
     * Applications use setsockopt() and getsockopt() to set or retrieve
     * socket options.  Socket options are used to change the default
     * behavior of sockets calls.  They are described in Section 7.
     *
     * The syntax is:
     *
     *   ret = getsockopt(int sd, int level, int optname, void __user *optval,
     *                    int __user *optlen);
     *   ret = setsockopt(int sd, int level, int optname, const void __user *optval,
     *                    int optlen);
     *
     *   sd      - the socket descript.
     *   level   - set to IPPROTO_SCTP for all SCTP options.
     *   optname - the option name.
     *   optval  - the buffer to store the value of the option.
     *   optlen  - the size of the buffer.
     */
    SCTP_STATIC int sctp_setsockopt(struct sock *sk, int level, int optname,
    
    				char __user *optval, unsigned int optlen)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    	int retval = 0;
    
    	SCTP_DEBUG_PRINTK("sctp_setsockopt(sk: %p... optname: %d)\n",
    			  sk, optname);
    
    	/* I can hardly begin to describe how wrong this is.  This is
    	 * so broken as to be worse than useless.  The API draft
    	 * REALLY is NOT helpful here...  I am not convinced that the
    	 * semantics of setsockopt() with a level OTHER THAN SOL_SCTP
    	 * are at all well-founded.
    	 */
    	if (level != SOL_SCTP) {
    		struct sctp_af *af = sctp_sk(sk)->pf->af;
    		retval = af->setsockopt(sk, level, optname, optval, optlen);
    		goto out_nounlock;
    	}
    
    	sctp_lock_sock(sk);
    
    	switch (optname) {
    	case SCTP_SOCKOPT_BINDX_ADD:
    		/* 'optlen' is the size of the addresses buffer. */
    		retval = sctp_setsockopt_bindx(sk, (struct sockaddr __user *)optval,
    					       optlen, SCTP_BINDX_ADD_ADDR);
    		break;
    
    	case SCTP_SOCKOPT_BINDX_REM:
    		/* 'optlen' is the size of the addresses buffer. */
    		retval = sctp_setsockopt_bindx(sk, (struct sockaddr __user *)optval,
    					       optlen, SCTP_BINDX_REM_ADDR);
    		break;
    
    
    	case SCTP_SOCKOPT_CONNECTX_OLD:
    		/* 'optlen' is the size of the addresses buffer. */
    		retval = sctp_setsockopt_connectx_old(sk,
    					    (struct sockaddr __user *)optval,
    					    optlen);
    		break;
    
    
    	case SCTP_SOCKOPT_CONNECTX:
    		/* 'optlen' is the size of the addresses buffer. */
    
    		retval = sctp_setsockopt_connectx(sk,
    					    (struct sockaddr __user *)optval,
    					    optlen);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	case SCTP_DISABLE_FRAGMENTS:
    		retval = sctp_setsockopt_disable_fragments(sk, optval, optlen);
    		break;
    
    	case SCTP_EVENTS:
    		retval = sctp_setsockopt_events(sk, optval, optlen);
    		break;
    
    	case SCTP_AUTOCLOSE:
    		retval = sctp_setsockopt_autoclose(sk, optval, optlen);
    		break;
    
    	case SCTP_PEER_ADDR_PARAMS:
    		retval = sctp_setsockopt_peer_addr_params(sk, optval, optlen);
    		break;
    
    
    	case SCTP_DELAYED_SACK:
    
    		retval = sctp_setsockopt_delayed_ack(sk, optval, optlen);
    
    	case SCTP_PARTIAL_DELIVERY_POINT:
    		retval = sctp_setsockopt_partial_delivery_point(sk, optval, optlen);
    		break;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	case SCTP_INITMSG:
    		retval = sctp_setsockopt_initmsg(sk, optval, optlen);
    		break;
    	case SCTP_DEFAULT_SEND_PARAM:
    		retval = sctp_setsockopt_default_send_param(sk, optval,
    							    optlen);
    		break;
    	case SCTP_PRIMARY_ADDR:
    		retval = sctp_setsockopt_primary_addr(sk, optval, optlen);
    		break;
    	case SCTP_SET_PEER_PRIMARY_ADDR:
    		retval = sctp_setsockopt_peer_primary_addr(sk, optval, optlen);
    		break;
    	case SCTP_NODELAY:
    		retval = sctp_setsockopt_nodelay(sk, optval, optlen);
    		break;
    	case SCTP_RTOINFO:
    		retval = sctp_setsockopt_rtoinfo(sk, optval, optlen);
    		break;
    	case SCTP_ASSOCINFO:
    		retval = sctp_setsockopt_associnfo(sk, optval, optlen);
    		break;
    	case SCTP_I_WANT_MAPPED_V4_ADDR:
    		retval = sctp_setsockopt_mappedv4(sk, optval, optlen);
    		break;
    	case SCTP_MAXSEG:
    		retval = sctp_setsockopt_maxseg(sk, optval, optlen);
    		break;
    
    	case SCTP_ADAPTATION_LAYER:
    		retval = sctp_setsockopt_adaptation_layer(sk, optval, optlen);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		break;
    
    	case SCTP_CONTEXT:
    		retval = sctp_setsockopt_context(sk, optval, optlen);
    		break;
    
    	case SCTP_FRAGMENT_INTERLEAVE:
    		retval = sctp_setsockopt_fragment_interleave(sk, optval, optlen);
    		break;
    
    	case SCTP_MAX_BURST:
    		retval = sctp_setsockopt_maxburst(sk, optval, optlen);
    		break;
    
    	case SCTP_AUTH_CHUNK:
    		retval = sctp_setsockopt_auth_chunk(sk, optval, optlen);
    		break;
    	case SCTP_HMAC_IDENT:
    		retval = sctp_setsockopt_hmac_ident(sk, optval, optlen);
    		break;
    	case SCTP_AUTH_KEY:
    		retval = sctp_setsockopt_auth_key(sk, optval, optlen);
    		break;
    	case SCTP_AUTH_ACTIVE_KEY:
    		retval = sctp_setsockopt_active_key(sk, optval, optlen);
    		break;
    	case SCTP_AUTH_DELETE_KEY:
    		retval = sctp_setsockopt_del_key(sk, optval, optlen);
    		break;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	default:
    		retval = -ENOPROTOOPT;
    		break;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	sctp_release_sock(sk);
    
    out_nounlock:
    	return retval;
    }
    
    /* API 3.1.6 connect() - UDP Style Syntax
     *
     * An application may use the connect() call in the UDP model to initiate an
     * association without sending data.
     *
     * The syntax is:
     *
     * ret = connect(int sd, const struct sockaddr *nam, socklen_t len);
     *
     * sd: the socket descriptor to have a new association added to.
     *
     * nam: the address structure (either struct sockaddr_in or struct
     *    sockaddr_in6 defined in RFC2553 [7]).
     *
     * len: the size of the address.
     */
    
    SCTP_STATIC int sctp_connect(struct sock *sk, struct sockaddr *addr,
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    			     int addr_len)
    {
    	int err = 0;
    
    	struct sctp_af *af;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	sctp_lock_sock(sk);
    
    
    	SCTP_DEBUG_PRINTK("%s - sk: %p, sockaddr: %p, addr_len: %d\n",
    
    			  __func__, sk, addr, addr_len);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	/* Validate addr_len before calling common connect/connectx routine. */
    	af = sctp_get_af_specific(addr->sa_family);
    	if (!af || addr_len < af->sockaddr_len) {
    		err = -EINVAL;
    	} else {
    		/* Pass correct addr len to common routine (so it knows there
    		 * is only one address being passed.
    		 */
    
    		err = __sctp_connect(sk, addr, af->sockaddr_len, NULL);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	}
    
    	sctp_release_sock(sk);
    	return err;
    }
    
    /* FIXME: Write comments. */
    SCTP_STATIC int sctp_disconnect(struct sock *sk, int flags)
    {
    	return -EOPNOTSUPP; /* STUB */
    }
    
    /* 4.1.4 accept() - TCP Style Syntax
     *
     * Applications use accept() call to remove an established SCTP
     * association from the accept queue of the endpoint.  A new socket
     * descriptor will be returned from accept() to represent the newly
     * formed association.
     */
    SCTP_STATIC struct sock *sctp_accept(struct sock *sk, int flags, int *err)
    {
    	struct sctp_sock *sp;
    	struct sctp_endpoint *ep;
    	struct sock *newsk = NULL;
    	struct sctp_association *asoc;
    	long timeo;
    	int error = 0;
    
    	sctp_lock_sock(sk);
    
    	sp = sctp_sk(sk);
    	ep = sp->ep;
    
    	if (!sctp_style(sk, TCP)) {
    		error = -EOPNOTSUPP;
    		goto out;
    	}
    
    	if (!sctp_sstate(sk, LISTENING)) {
    		error = -EINVAL;
    		goto out;
    	}
    
    
    	timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	error = sctp_wait_for_accept(sk, timeo);
    	if (error)
    		goto out;
    
    	/* We treat the list of associations on the endpoint as the accept
    	 * queue and pick the first association on the list.
    	 */
    	asoc = list_entry(ep->asocs.next, struct sctp_association, asocs);
    
    	newsk = sp->pf->create_accept_sk(sk, asoc);
    	if (!newsk) {
    		error = -ENOMEM;
    		goto out;
    	}
    
    	/* Populate the fields of the newsk from the oldsk and migrate the
    	 * asoc to the newsk.
    	 */
    	sctp_sock_migrate(sk, newsk, asoc, SCTP_SOCKET_TCP);
    
    out:
    	sctp_release_sock(sk);
    
    	*err = error;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	return newsk;
    }
    
    /* The SCTP ioctl handler. */
    SCTP_STATIC int sctp_ioctl(struct sock *sk, int cmd, unsigned long arg)
    {
    
    	int rc = -ENOTCONN;
    
    	sctp_lock_sock(sk);
    
    	/*
    	 * SEQPACKET-style sockets in LISTENING state are valid, for
    	 * SCTP, so only discard TCP-style sockets in LISTENING state.
    	 */
    	if (sctp_style(sk, TCP) && sctp_sstate(sk, LISTENING))
    		goto out;
    
    	switch (cmd) {
    	case SIOCINQ: {
    		struct sk_buff *skb;
    		unsigned int amount = 0;
    
    		skb = skb_peek(&sk->sk_receive_queue);
    		if (skb != NULL) {
    			/*
    			 * We will only return the amount of this packet since
    			 * that is all that will be read.
    			 */
    			amount = skb->len;
    		}
    		rc = put_user(amount, (int __user *)arg);
    		break;
    
    	default:
    		rc = -ENOIOCTLCMD;
    		break;
    	}
    out:
    	sctp_release_sock(sk);
    	return rc;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    /* This is the function which gets called during socket creation to
     * initialized the SCTP-specific portion of the sock.
     * The sock structure should already be zero-filled memory.
     */
    SCTP_STATIC int sctp_init_sock(struct sock *sk)
    {
    	struct sctp_endpoint *ep;
    	struct sctp_sock *sp;
    
    	SCTP_DEBUG_PRINTK("sctp_init_sock(sk: %p)\n", sk);
    
    	sp = sctp_sk(sk);
    
    	/* Initialize the SCTP per socket area.  */
    	switch (sk->sk_type) {
    	case SOCK_SEQPACKET:
    		sp->type = SCTP_SOCKET_UDP;
    		break;
    	case SOCK_STREAM:
    		sp->type = SCTP_SOCKET_TCP;
    		break;
    	default:
    		return -ESOCKTNOSUPPORT;
    	}
    
    	/* Initialize default send parameters. These parameters can be
    	 * modified with the SCTP_DEFAULT_SEND_PARAM socket option.
    	 */
    	sp->default_stream = 0;
    	sp->default_ppid = 0;
    	sp->default_flags = 0;
    	sp->default_context = 0;
    	sp->default_timetolive = 0;
    
    
    	sp->default_rcv_context = 0;
    
    	sp->max_burst = sctp_max_burst;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	/* Initialize default setup parameters. These parameters
    	 * can be modified with the SCTP_INITMSG socket option or
    	 * overridden by the SCTP_INIT CMSG.
    	 */
    	sp->initmsg.sinit_num_ostreams   = sctp_max_outstreams;
    	sp->initmsg.sinit_max_instreams  = sctp_max_instreams;
    	sp->initmsg.sinit_max_attempts   = sctp_max_retrans_init;
    
    	sp->initmsg.sinit_max_init_timeo = sctp_rto_max;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	/* Initialize default RTO related parameters.  These parameters can
    	 * be modified for with the SCTP_RTOINFO socket option.
    	 */
    
    	sp->rtoinfo.srto_initial = sctp_rto_initial;
    	sp->rtoinfo.srto_max     = sctp_rto_max;
    	sp->rtoinfo.srto_min     = sctp_rto_min;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	/* Initialize default association related parameters. These parameters
    	 * can be modified with the SCTP_ASSOCINFO socket option.
    	 */
    	sp->assocparams.sasoc_asocmaxrxt = sctp_max_retrans_association;
    	sp->assocparams.sasoc_number_peer_destinations = 0;
    	sp->assocparams.sasoc_peer_rwnd = 0;
    	sp->assocparams.sasoc_local_rwnd = 0;
    
    	sp->assocparams.sasoc_cookie_life = sctp_valid_cookie_life;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	/* Initialize default event subscriptions. By default, all the
    
    	 * options are off.
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	 */
    	memset(&sp->subscribe, 0, sizeof(struct sctp_event_subscribe));
    
    	/* Default Peer Address Parameters.  These defaults can
    	 * be modified via SCTP_PEER_ADDR_PARAMS
    	 */
    
    	sp->hbinterval  = sctp_hb_interval;
    
    	sp->pathmaxrxt  = sctp_max_retrans_path;
    	sp->pathmtu     = 0; // allow default discovery
    
    	sp->sackdelay   = sctp_sack_timeout;
    
    			  SPP_PMTUD_ENABLE |
    			  SPP_SACKDELAY_ENABLE;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	/* If enabled no SCTP message fragmentation will be performed.
    	 * Configure through SCTP_DISABLE_FRAGMENTS socket option.
    	 */
    	sp->disable_fragments = 0;
    
    
    	/* Enable Nagle algorithm by default.  */
    	sp->nodelay           = 0;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	/* Enable by default. */
    	sp->v4mapped          = 1;
    
    	/* Auto-close idle associations after the configured
    	 * number of seconds.  A value of 0 disables this
    	 * feature.  Configure through the SCTP_AUTOCLOSE socket option,
    	 * for UDP-style sockets only.
    	 */
    	sp->autoclose         = 0;
    
    	/* User specified fragmentation limit. */
    	sp->user_frag         = 0;
    
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	sp->pf = sctp_get_pf_specific(sk->sk_family);
    
    	/* Control variables for partial data delivery. */
    
    	atomic_set(&sp->pd_mode, 0);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	skb_queue_head_init(&sp->pd_lobby);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	/* Create a per socket endpoint structure.  Even if we
    	 * change the data structure relationships, this may still
    	 * be useful for storing pre-connect address information.
    	 */
    	ep = sctp_endpoint_new(sk, GFP_KERNEL);
    	if (!ep)
    		return -ENOMEM;
    
    	sp->ep = ep;
    	sp->hmac = NULL;
    
    	SCTP_DBG_OBJCNT_INC(sock);
    
    	percpu_counter_inc(&sctp_sockets_allocated);
    
    	sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	return 0;
    }
    
    /* Cleanup any SCTP per socket resources.  */
    
    SCTP_STATIC void sctp_destroy_sock(struct sock *sk)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    	struct sctp_endpoint *ep;
    
    	SCTP_DEBUG_PRINTK("sctp_destroy_sock(sk: %p)\n", sk);
    
    	/* Release our hold on the endpoint. */
    	ep = sctp_sk(sk)->ep;
    	sctp_endpoint_free(ep);
    
    	percpu_counter_dec(&sctp_sockets_allocated);
    
    	sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    /* API 4.1.7 shutdown() - TCP Style Syntax
     *     int shutdown(int socket, int how);
     *
     *     sd      - the socket descriptor of the association to be closed.
     *     how     - Specifies the type of shutdown.  The  values  are
     *               as follows:
     *               SHUT_RD
     *                     Disables further receive operations. No SCTP
     *                     protocol action is taken.
     *               SHUT_WR
     *                     Disables further send operations, and initiates
     *                     the SCTP shutdown sequence.
     *               SHUT_RDWR
     *                     Disables further send  and  receive  operations
     *                     and initiates the SCTP shutdown sequence.
     */
    SCTP_STATIC void sctp_shutdown(struct sock *sk, int how)
    {
    	struct sctp_endpoint *ep;
    	struct sctp_association *asoc;
    
    	if (!sctp_style(sk, TCP))
    		return;
    
    	if (how & SEND_SHUTDOWN) {
    		ep = sctp_sk(sk)->ep;
    		if (!list_empty(&ep->asocs)) {
    			asoc = list_entry(ep->asocs.next,
    					  struct sctp_association, asocs);
    			sctp_primitive_SHUTDOWN(asoc, NULL);
    		}
    	}
    }
    
    /* 7.2.1 Association Status (SCTP_STATUS)
    
     * Applications can retrieve current status information about an
     * association, including association state, peer receiver window size,
     * number of unacked data chunks, and number of data chunks pending
     * receipt.  This information is read-only.
     */
    static int sctp_getsockopt_sctp_status(struct sock *sk, int len,
    				       char __user *optval,
    				       int __user *optlen)
    {
    	struct sctp_status status;
    	struct sctp_association *asoc = NULL;
    	struct sctp_transport *transport;
    	sctp_assoc_t associd;
    	int retval = 0;
    
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		retval = -EINVAL;
    		goto out;
    	}
    
    
    	len = sizeof(status);
    	if (copy_from_user(&status, optval, len)) {
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		retval = -EFAULT;
    		goto out;
    	}
    
    	associd = status.sstat_assoc_id;
    	asoc = sctp_id2assoc(sk, associd);
    	if (!asoc) {
    		retval = -EINVAL;
    		goto out;
    	}
    
    	transport = asoc->peer.primary_path;
    
    	status.sstat_assoc_id = sctp_assoc2id(asoc);
    	status.sstat_state = asoc->state;
    	status.sstat_rwnd =  asoc->peer.rwnd;
    	status.sstat_unackdata = asoc->unack_data;
    
    	status.sstat_penddata = sctp_tsnmap_pending(&asoc->peer.tsn_map);
    	status.sstat_instrms = asoc->c.sinit_max_instreams;
    	status.sstat_outstrms = asoc->c.sinit_num_ostreams;
    	status.sstat_fragmentation_point = asoc->frag_point;
    	status.sstat_primary.spinfo_assoc_id = sctp_assoc2id(transport->asoc);
    
    	memcpy(&status.sstat_primary.spinfo_address, &transport->ipaddr,
    			transport->af_specific->sockaddr_len);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	/* Map ipv4 address into v4-mapped-on-v6 address.  */
    	sctp_get_pf_specific(sk->sk_family)->addr_v4map(sctp_sk(sk),
    		(union sctp_addr *)&status.sstat_primary.spinfo_address);
    
    	status.sstat_primary.spinfo_state = transport->state;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	status.sstat_primary.spinfo_cwnd = transport->cwnd;
    	status.sstat_primary.spinfo_srtt = transport->srtt;
    	status.sstat_primary.spinfo_rto = jiffies_to_msecs(transport->rto);
    
    	status.sstat_primary.spinfo_mtu = transport->pathmtu;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	if (status.sstat_primary.spinfo_state == SCTP_UNKNOWN)
    		status.sstat_primary.spinfo_state = SCTP_ACTIVE;
    
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	if (put_user(len, optlen)) {
    		retval = -EFAULT;
    		goto out;
    	}
    
    	SCTP_DEBUG_PRINTK("sctp_getsockopt_sctp_status(%d): %d %d %d\n",
    			  len, status.sstat_state, status.sstat_rwnd,
    			  status.sstat_assoc_id);
    
    	if (copy_to_user(optval, &status, len)) {
    		retval = -EFAULT;
    		goto out;
    	}
    
    out:
    
    	return retval;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    
    /* 7.2.2 Peer Address Information (SCTP_GET_PEER_ADDR_INFO)
     *
     * Applications can retrieve information about a specific peer address
     * of an association, including its reachability state, congestion
     * window, and retransmission timer values.  This information is
     * read-only.
     */
    static int sctp_getsockopt_peer_addr_info(struct sock *sk, int len,
    					  char __user *optval,
    					  int __user *optlen)
    {
    	struct sctp_paddrinfo pinfo;
    	struct sctp_transport *transport;
    	int retval = 0;
    
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		retval = -EINVAL;
    		goto out;
    	}
    
    
    	len = sizeof(pinfo);
    	if (copy_from_user(&pinfo, optval, len)) {
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		retval = -EFAULT;
    		goto out;
    	}
    
    	transport = sctp_addr_id2transport(sk, &pinfo.spinfo_address,
    					   pinfo.spinfo_assoc_id);
    	if (!transport)
    		return -EINVAL;
    
    	pinfo.spinfo_assoc_id = sctp_assoc2id(transport->asoc);
    
    	pinfo.spinfo_state = transport->state;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	pinfo.spinfo_cwnd = transport->cwnd;
    	pinfo.spinfo_srtt = transport->srtt;
    	pinfo.spinfo_rto = jiffies_to_msecs(transport->rto);
    
    	pinfo.spinfo_mtu = transport->pathmtu;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	if (pinfo.spinfo_state == SCTP_UNKNOWN)
    		pinfo.spinfo_state = SCTP_ACTIVE;
    
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	if (put_user(len, optlen)) {
    		retval = -EFAULT;
    		goto out;
    	}
    
    	if (copy_to_user(optval, &pinfo, len)) {
    		retval = -EFAULT;
    		goto out;
    	}
    
    out:
    
    	return retval;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    /* 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.
     */
    static int sctp_getsockopt_disable_fragments(struct sock *sk, int len,
    					char __user *optval, int __user *optlen)
    {
    	int val;
    
    	if (len < sizeof(int))
    		return -EINVAL;
    
    	len = sizeof(int);
    	val = (sctp_sk(sk)->disable_fragments == 1);
    	if (put_user(len, optlen))
    		return -EFAULT;
    	if (copy_to_user(optval, &val, len))
    		return -EFAULT;
    	return 0;
    }
    
    /* 7.1.15 Set notification and ancillary events (SCTP_EVENTS)
     *
     * This socket option is used to specify various notifications and
     * ancillary data the user wishes to receive.
     */
    static int sctp_getsockopt_events(struct sock *sk, int len, char __user *optval,
    				  int __user *optlen)
    {
    
    	if (len < sizeof(struct sctp_event_subscribe))
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		return -EINVAL;
    
    	len = sizeof(struct sctp_event_subscribe);
    	if (put_user(len, optlen))
    		return -EFAULT;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	if (copy_to_user(optval, &sctp_sk(sk)->subscribe, len))
    		return -EFAULT;
    	return 0;
    }
    
    /* 7.1.8 Automatic Close of associations (SCTP_AUTOCLOSE)