Skip to content
Snippets Groups Projects
socket.c 200 KiB
Newer Older
  • Learn to ignore specific revisions
  • 		pr_warn_ratelimited(DEPRECATED
    
    				    "Use of int in maxseg socket option.\n"
    
    				    "Use struct sctp_assoc_value instead\n",
    				    current->comm, task_pid_nr(current));
    
    		if (copy_from_user(&val, optval, optlen))
    			return -EFAULT;
    		params.assoc_id = 0;
    	} else if (optlen == sizeof(struct sctp_assoc_value)) {
    		if (copy_from_user(&params, optval, optlen))
    			return -EFAULT;
    		val = params.assoc_value;
    	} else
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		return -EINVAL;
    
    	if ((val != 0) && ((val < 8) || (val > SCTP_MAX_CHUNK_LEN)))
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		return -EINVAL;
    
    
    	asoc = sctp_id2assoc(sk, params.assoc_id);
    	if (!asoc && params.assoc_id && sctp_style(sk, UDP))
    		return -EINVAL;
    
    	if (asoc) {
    		if (val == 0) {
    			val = asoc->pathmtu;
    			val -= sp->pf->af->net_header_len;
    			val -= sizeof(struct sctphdr) +
    					sizeof(struct sctp_data_chunk);
    		}
    
    		asoc->user_frag = val;
    		asoc->frag_point = sctp_frag_point(asoc, asoc->pathmtu);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	}
    
    	return 0;
    }
    
    
    /*
     *  7.1.9 Set Peer Primary Address (SCTP_SET_PEER_PRIMARY_ADDR)
     *
     *   Requests that the peer mark the enclosed address as the association
     *   primary. The enclosed address must be one of the association's
     *   locally bound addresses. The following structure is used to make a
     *   set primary request:
     */
    static int sctp_setsockopt_peer_primary_addr(struct sock *sk, char __user *optval,
    
    					     unsigned int optlen)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    
    	struct net *net = sock_net(sk);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	struct sctp_sock	*sp;
    	struct sctp_association	*asoc = NULL;
    	struct sctp_setpeerprim	prim;
    	struct sctp_chunk	*chunk;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	int 			err;
    
    	sp = sctp_sk(sk);
    
    
    	if (!net->sctp.addip_enable)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		return -EPERM;
    
    	if (optlen != sizeof(struct sctp_setpeerprim))
    		return -EINVAL;
    
    	if (copy_from_user(&prim, optval, optlen))
    		return -EFAULT;
    
    	asoc = sctp_id2assoc(sk, prim.sspp_assoc_id);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		return -EINVAL;
    
    	if (!asoc->peer.asconf_capable)
    		return -EPERM;
    
    	if (asoc->peer.addip_disabled_mask & SCTP_PARAM_SET_PRIMARY)
    		return -EPERM;
    
    	if (!sctp_state(asoc, ESTABLISHED))
    		return -ENOTCONN;
    
    
    	af = sctp_get_af_specific(prim.sspp_addr.ss_family);
    	if (!af)
    		return -EINVAL;
    
    	if (!af->addr_valid((union sctp_addr *)&prim.sspp_addr, sp, NULL))
    		return -EADDRNOTAVAIL;
    
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	if (!sctp_assoc_lookup_laddr(asoc, (union sctp_addr *)&prim.sspp_addr))
    		return -EADDRNOTAVAIL;
    
    	/* Create an ASCONF chunk with SET_PRIMARY parameter	*/
    	chunk = sctp_make_asconf_set_prim(asoc,
    					  (union sctp_addr *)&prim.sspp_addr);
    	if (!chunk)
    		return -ENOMEM;
    
    	err = sctp_send_asconf(asoc, chunk);
    
    
    	pr_debug("%s: we set peer primary addr primitively\n", __func__);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	return err;
    }
    
    
    static int sctp_setsockopt_adaptation_layer(struct sock *sk, char __user *optval,
    
    					    unsigned int optlen)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	if (optlen != sizeof(struct sctp_setadaptation))
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		return -EINVAL;
    
    	if (copy_from_user(&adaptation, optval, optlen))
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		return -EFAULT;
    
    
    	sctp_sk(sk)->adaptation_ind = adaptation.ssb_adaptation_ind;
    
    /*
     * 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_ratelimited(DEPRECATED
    
    				    "Use of int in max_burst socket option deprecated.\n"
    
    				    "Use struct sctp_assoc_value instead\n",
    				    current->comm, task_pid_nr(current));
    
    		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)
    
    	struct net *net = sock_net(sk);
    
    	struct sctp_authchunk val;
    
    
    	if (!net->sctp.auth_enable)
    
    	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 net *net = sock_net(sk);
    
    	struct sctp_hmacalgo *hmacs;
    
    	if (!net->sctp.auth_enable)
    
    	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 net *net = sock_net(sk);
    
    	struct sctp_authkey *authkey;
    	struct sctp_association *asoc;
    	int ret;
    
    
    	if (!net->sctp.auth_enable)
    
    	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:
    
    	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 net *net = sock_net(sk);
    
    	struct sctp_authkeyid val;
    	struct sctp_association *asoc;
    
    
    	if (!net->sctp.auth_enable)
    
    	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 net *net = sock_net(sk);
    
    	struct sctp_authkeyid val;
    	struct sctp_association *asoc;
    
    
    	if (!net->sctp.auth_enable)
    
    	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);
    
    }
    
    
    /*
     * 8.1.23 SCTP_AUTO_ASCONF
     *
     * This option will enable or disable the use of the automatic generation of
     * ASCONF chunks to add and delete addresses to an existing association.  Note
     * that this option has two caveats namely: a) it only affects sockets that
     * are bound to all addresses available to the SCTP stack, and b) the system
     * administrator may have an overriding control that turns the ASCONF feature
     * off no matter what setting the socket option may have.
     * This option expects an integer boolean flag, where a non-zero value turns on
     * the option, and a zero value turns off the option.
     * Note. In this implementation, socket operation overrides default parameter
     * being set by sysctl as well as FreeBSD implementation
     */
    static int sctp_setsockopt_auto_asconf(struct sock *sk, char __user *optval,
    					unsigned int optlen)
    {
    	int val;
    	struct sctp_sock *sp = sctp_sk(sk);
    
    	if (optlen < sizeof(int))
    		return -EINVAL;
    	if (get_user(val, (int __user *)optval))
    		return -EFAULT;
    	if (!sctp_is_ep_boundall(sk) && val)
    		return -EINVAL;
    	if ((val && sp->do_auto_asconf) || (!val && !sp->do_auto_asconf))
    		return 0;
    
    	if (val == 0 && sp->do_auto_asconf) {
    		list_del(&sp->auto_asconf_list);
    		sp->do_auto_asconf = 0;
    	} else if (val && !sp->do_auto_asconf) {
    		list_add_tail(&sp->auto_asconf_list,
    
    		    &sock_net(sk)->sctp.auto_asconf_splist);
    
    /*
     * SCTP_PEER_ADDR_THLDS
     *
     * This option allows us to alter the partially failed threshold for one or all
     * transports in an association.  See Section 6.1 of:
     * http://www.ietf.org/id/draft-nishida-tsvwg-sctp-failover-05.txt
     */
    static int sctp_setsockopt_paddr_thresholds(struct sock *sk,
    					    char __user *optval,
    					    unsigned int optlen)
    {
    	struct sctp_paddrthlds val;
    	struct sctp_transport *trans;
    	struct sctp_association *asoc;
    
    	if (optlen < sizeof(struct sctp_paddrthlds))
    		return -EINVAL;
    	if (copy_from_user(&val, (struct sctp_paddrthlds __user *)optval,
    			   sizeof(struct sctp_paddrthlds)))
    		return -EFAULT;
    
    
    	if (sctp_is_any(sk, (const union sctp_addr *)&val.spt_address)) {
    		asoc = sctp_id2assoc(sk, val.spt_assoc_id);
    		if (!asoc)
    			return -ENOENT;
    		list_for_each_entry(trans, &asoc->peer.transport_addr_list,
    				    transports) {
    			if (val.spt_pathmaxrxt)
    				trans->pathmaxrxt = val.spt_pathmaxrxt;
    			trans->pf_retrans = val.spt_pathpfthld;
    		}
    
    		if (val.spt_pathmaxrxt)
    			asoc->pathmaxrxt = val.spt_pathmaxrxt;
    		asoc->pf_retrans = val.spt_pathpfthld;
    	} else {
    		trans = sctp_addr_id2transport(sk, &val.spt_address,
    					       val.spt_assoc_id);
    		if (!trans)
    			return -ENOENT;
    
    		if (val.spt_pathmaxrxt)
    			trans->pathmaxrxt = val.spt_pathmaxrxt;
    		trans->pf_retrans = val.spt_pathpfthld;
    	}
    
    	return 0;
    }
    
    
    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.
     */
    
    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;
    
    
    	pr_debug("%s: sk:%p, optname:%d\n", __func__, sk, optname);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	/* 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;
    	}
    
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	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;
    
    	case SCTP_AUTO_ASCONF:
    		retval = sctp_setsockopt_auto_asconf(sk, optval, optlen);
    		break;
    
    	case SCTP_PEER_ADDR_THLDS:
    		retval = sctp_setsockopt_paddr_thresholds(sk, optval, optlen);
    		break;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	default:
    		retval = -ENOPROTOOPT;
    		break;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	release_sock(sk);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    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.
     */
    
    static int sctp_connect(struct sock *sk, struct sockaddr *addr,
    			int addr_len)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    	int err = 0;
    
    	struct sctp_af *af;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	pr_debug("%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);
    
    	release_sock(sk);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	return err;
    }
    
    /* FIXME: Write comments. */
    
    static int sctp_disconnect(struct sock *sk, int flags)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    	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.
     */
    
    static struct sock *sctp_accept(struct sock *sk, int flags, int *err)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    	struct sctp_sock *sp;
    	struct sctp_endpoint *ep;
    	struct sock *newsk = NULL;
    	struct sctp_association *asoc;
    	long timeo;
    	int error = 0;
    
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	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:
    
    	release_sock(sk);
    
    	*err = error;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	return newsk;
    }
    
    /* The SCTP ioctl handler. */
    
    static int sctp_ioctl(struct sock *sk, int cmd, unsigned long arg)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    
    
    	/*
    	 * 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;
    
    	release_sock(sk);
    
    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.
     */
    
    static int sctp_init_sock(struct sock *sk)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    
    	struct net *net = sock_net(sk);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	struct sctp_sock *sp;
    
    
    	pr_debug("%s: sk:%p\n", __func__, sk);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	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 = net->sctp.max_burst;
    
    	sp->sctp_hmac_alg = net->sctp.sctp_hmac_alg;
    
    
    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   = net->sctp.max_retrans_init;
    	sp->initmsg.sinit_max_init_timeo = net->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 = net->sctp.rto_initial;
    	sp->rtoinfo.srto_max     = net->sctp.rto_max;
    	sp->rtoinfo.srto_min     = net->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 = net->sctp.max_retrans_association;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	sp->assocparams.sasoc_number_peer_destinations = 0;
    	sp->assocparams.sasoc_peer_rwnd = 0;
    	sp->assocparams.sasoc_local_rwnd = 0;
    
    	sp->assocparams.sasoc_cookie_life = net->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  = net->sctp.hb_interval;
    	sp->pathmaxrxt  = net->sctp.max_retrans_path;
    
    	sp->pathmtu     = 0; /* allow default discovery */
    
    	sp->sackdelay   = net->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.
    	 */
    
    	sp->ep = sctp_endpoint_new(sk, GFP_KERNEL);
    	if (!sp->ep)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		return -ENOMEM;
    
    	sp->hmac = NULL;
    
    
    	sk->sk_destruct = sctp_destruct_sock;
    
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	SCTP_DBG_OBJCNT_INC(sock);
    
    	percpu_counter_inc(&sctp_sockets_allocated);
    
    	sock_prot_inuse_add(net, sk->sk_prot, 1);
    	if (net->sctp.default_auto_asconf) {
    
    		list_add_tail(&sp->auto_asconf_list,
    
    		    &net->sctp.auto_asconf_splist);
    
    		sp->do_auto_asconf = 1;
    	} else
    		sp->do_auto_asconf = 0;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	return 0;
    }