Skip to content
Snippets Groups Projects
socket.c 139 KiB
Newer Older
  • Learn to ignore specific revisions
  • Linus Torvalds's avatar
    Linus Torvalds committed
    2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452
    	if (0xffffffff == params.spp_hbinterval) {
    		error = sctp_primitive_REQUESTHEARTBEAT (trans->asoc, trans);
    		if (error)
    			return error;
    	} else {
    	/* The value of the heartbeat interval, in milliseconds. A value of 0,
    	 * when modifying the parameter, specifies that the heartbeat on this
    	 * address should be disabled.
    	 */
    		if (params.spp_hbinterval) {
    			trans->hb_allowed = 1;
    			trans->hb_interval = 
    				msecs_to_jiffies(params.spp_hbinterval);
    		} else
    			trans->hb_allowed = 0;
    	}
    
    	/* spp_pathmaxrxt contains the maximum number of retransmissions
    	 * before this address shall be considered unreachable.
    	 */
    	if (params.spp_pathmaxrxt)
    		trans->max_retrans = params.spp_pathmaxrxt;
    
    	return 0;
    }
    
    /* 7.1.3 Initialization Parameters (SCTP_INITMSG)
     *
     * Applications can specify protocol parameters for the default association
     * initialization.  The option name argument to setsockopt() and getsockopt()
     * is SCTP_INITMSG.
     *
     * Setting initialization parameters is effective only on an unconnected
     * socket (for UDP-style sockets only future associations are effected
     * by the change).  With TCP-style sockets, this option is inherited by
     * sockets derived from a listener socket.
     */
    static int sctp_setsockopt_initmsg(struct sock *sk, char __user *optval, int optlen)
    {
    	struct sctp_initmsg sinit;
    	struct sctp_sock *sp = sctp_sk(sk);
    
    	if (optlen != sizeof(struct sctp_initmsg))
    		return -EINVAL;
    	if (copy_from_user(&sinit, optval, optlen))
    		return -EFAULT;
    
    	if (sinit.sinit_num_ostreams)
    		sp->initmsg.sinit_num_ostreams = sinit.sinit_num_ostreams;	
    	if (sinit.sinit_max_instreams)
    		sp->initmsg.sinit_max_instreams = sinit.sinit_max_instreams;	
    	if (sinit.sinit_max_attempts)
    		sp->initmsg.sinit_max_attempts = sinit.sinit_max_attempts;	
    	if (sinit.sinit_max_init_timeo)
    		sp->initmsg.sinit_max_init_timeo = sinit.sinit_max_init_timeo;	
    
    	return 0;
    }
    
    /*
     * 7.1.14 Set default send parameters (SCTP_DEFAULT_SEND_PARAM)
     *
     *   Applications that wish to use the sendto() system call may wish to
     *   specify a default set of parameters that would normally be supplied
     *   through the inclusion of ancillary data.  This socket option allows
     *   such an application to set the default sctp_sndrcvinfo structure.
     *   The application that wishes to use this socket option simply passes
     *   in to this call the sctp_sndrcvinfo structure defined in Section
     *   5.2.2) The input parameters accepted by this call include
     *   sinfo_stream, sinfo_flags, sinfo_ppid, sinfo_context,
     *   sinfo_timetolive.  The user must provide the sinfo_assoc_id field in
     *   to this call if the caller is using the UDP model.
     */
    static int sctp_setsockopt_default_send_param(struct sock *sk,
    						char __user *optval, int optlen)
    {
    	struct sctp_sndrcvinfo info;
    	struct sctp_association *asoc;
    	struct sctp_sock *sp = sctp_sk(sk);
    
    	if (optlen != sizeof(struct sctp_sndrcvinfo))
    		return -EINVAL;
    	if (copy_from_user(&info, optval, optlen))
    		return -EFAULT;
    
    	asoc = sctp_id2assoc(sk, info.sinfo_assoc_id);
    	if (!asoc && info.sinfo_assoc_id && sctp_style(sk, UDP))
    		return -EINVAL;
    
    	if (asoc) {
    		asoc->default_stream = info.sinfo_stream;
    		asoc->default_flags = info.sinfo_flags;
    		asoc->default_ppid = info.sinfo_ppid;
    		asoc->default_context = info.sinfo_context;
    		asoc->default_timetolive = info.sinfo_timetolive;
    	} else {
    		sp->default_stream = info.sinfo_stream;
    		sp->default_flags = info.sinfo_flags;
    		sp->default_ppid = info.sinfo_ppid;
    		sp->default_context = info.sinfo_context;
    		sp->default_timetolive = info.sinfo_timetolive;
    	}
    
    	return 0;
    }
    
    /* 7.1.10 Set Primary Address (SCTP_PRIMARY_ADDR)
     *
     * Requests that the local SCTP stack use the enclosed peer address as
     * the association primary.  The enclosed address must be one of the
     * association peer's addresses.
     */
    static int sctp_setsockopt_primary_addr(struct sock *sk, char __user *optval,
    					int optlen)
    {
    	struct sctp_prim prim;
    	struct sctp_transport *trans;
    
    	if (optlen != sizeof(struct sctp_prim))
    		return -EINVAL;
    
    	if (copy_from_user(&prim, optval, sizeof(struct sctp_prim)))
    		return -EFAULT;
    
    	trans = sctp_addr_id2transport(sk, &prim.ssp_addr, prim.ssp_assoc_id);
    	if (!trans)
    		return -EINVAL;
    
    	sctp_assoc_set_primary(trans->asoc, trans);
    
    	return 0;
    }
    
    /*
     * 7.1.5 SCTP_NODELAY
     *
     * Turn on/off any Nagle-like algorithm.  This means that packets are
     * generally sent as soon as possible and no unnecessary delays are
     * introduced, at the cost of more packets in the network.  Expects an
     *  integer boolean flag.
     */
    static int sctp_setsockopt_nodelay(struct sock *sk, char __user *optval,
    					int optlen)
    {
    	int val;
    
    	if (optlen < sizeof(int))
    		return -EINVAL;
    	if (get_user(val, (int __user *)optval))
    		return -EFAULT;
    
    	sctp_sk(sk)->nodelay = (val == 0) ? 0 : 1;
    	return 0;
    }
    
    /*
     *
     * 7.1.1 SCTP_RTOINFO
     *
     * The protocol parameters used to initialize and bound retransmission
     * timeout (RTO) are tunable. sctp_rtoinfo structure is used to access
     * and modify these parameters.
     * All parameters are time values, in milliseconds.  A value of 0, when
     * modifying the parameters, indicates that the current value should not
     * be changed.
     *
     */
    static int sctp_setsockopt_rtoinfo(struct sock *sk, char __user *optval, int optlen) {
    	struct sctp_rtoinfo rtoinfo;
    	struct sctp_association *asoc;
    
    	if (optlen != sizeof (struct sctp_rtoinfo))
    		return -EINVAL;
    
    	if (copy_from_user(&rtoinfo, optval, optlen))
    		return -EFAULT;
    
    	asoc = sctp_id2assoc(sk, rtoinfo.srto_assoc_id);
    
    	/* Set the values to the specific association */
    	if (!asoc && rtoinfo.srto_assoc_id && sctp_style(sk, UDP))
    		return -EINVAL;
    
    	if (asoc) {
    		if (rtoinfo.srto_initial != 0)
    			asoc->rto_initial = 
    				msecs_to_jiffies(rtoinfo.srto_initial);
    		if (rtoinfo.srto_max != 0)
    			asoc->rto_max = msecs_to_jiffies(rtoinfo.srto_max);
    		if (rtoinfo.srto_min != 0)
    			asoc->rto_min = msecs_to_jiffies(rtoinfo.srto_min);
    	} else {
    		/* If there is no association or the association-id = 0
    		 * set the values to the endpoint.
    		 */
    		struct sctp_sock *sp = sctp_sk(sk);
    
    		if (rtoinfo.srto_initial != 0)
    			sp->rtoinfo.srto_initial = rtoinfo.srto_initial;
    		if (rtoinfo.srto_max != 0)
    			sp->rtoinfo.srto_max = rtoinfo.srto_max;
    		if (rtoinfo.srto_min != 0)
    			sp->rtoinfo.srto_min = rtoinfo.srto_min;
    	}
    
    	return 0;
    }
    
    /*
     *
     * 7.1.2 SCTP_ASSOCINFO
     *
     * This option is used to tune the the maximum retransmission attempts
     * of the association.
     * Returns an error if the new association retransmission value is
     * greater than the sum of the retransmission value  of the peer.
     * See [SCTP] for more information.
     *
     */
    static int sctp_setsockopt_associnfo(struct sock *sk, char __user *optval, int optlen)
    {
    
    	struct sctp_assocparams assocparams;
    	struct sctp_association *asoc;
    
    	if (optlen != sizeof(struct sctp_assocparams))
    		return -EINVAL;
    	if (copy_from_user(&assocparams, optval, optlen))
    		return -EFAULT;
    
    	asoc = sctp_id2assoc(sk, assocparams.sasoc_assoc_id);
    
    	if (!asoc && assocparams.sasoc_assoc_id && sctp_style(sk, UDP))
    		return -EINVAL;
    
    	/* Set the values to the specific association */
    	if (asoc) {
    		if (assocparams.sasoc_asocmaxrxt != 0)
    			asoc->max_retrans = assocparams.sasoc_asocmaxrxt;
    		if (assocparams.sasoc_cookie_life != 0) {
    			asoc->cookie_life.tv_sec =
    					assocparams.sasoc_cookie_life / 1000;
    			asoc->cookie_life.tv_usec =
    					(assocparams.sasoc_cookie_life % 1000)
    					* 1000;
    		}
    	} else {
    		/* Set the values to the endpoint */
    		struct sctp_sock *sp = sctp_sk(sk);
    
    		if (assocparams.sasoc_asocmaxrxt != 0)
    			sp->assocparams.sasoc_asocmaxrxt =
    						assocparams.sasoc_asocmaxrxt;
    		if (assocparams.sasoc_cookie_life != 0)
    			sp->assocparams.sasoc_cookie_life =
    						assocparams.sasoc_cookie_life;
    	}
    	return 0;
    }
    
    /*
     * 7.1.16 Set/clear IPv4 mapped addresses (SCTP_I_WANT_MAPPED_V4_ADDR)
     *
     * This socket option is a boolean flag which turns on or off mapped V4
     * addresses.  If this option is turned on and the socket is type
     * PF_INET6, then IPv4 addresses will be mapped to V6 representation.
     * If this option is turned off, then no mapping will be done of V4
     * addresses and a user will receive both PF_INET6 and PF_INET type
     * addresses on the socket.
     */
    static int sctp_setsockopt_mappedv4(struct sock *sk, char __user *optval, 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 (val)
    		sp->v4mapped = 1;
    	else
    		sp->v4mapped = 0;
    
    	return 0;
    }
    
    /*
     * 7.1.17 Set the maximum fragrmentation size (SCTP_MAXSEG)
     *
     * This socket option specifies the maximum size to put in any outgoing
     * SCTP chunk.  If a message is larger than this size it will be
     * fragmented by SCTP into the specified size.  Note that the underlying
     * SCTP implementation may fragment into smaller sized chunks when the
     * PMTU of the underlying association is smaller than the value set by
     * the user.
     */
    static int sctp_setsockopt_maxseg(struct sock *sk, char __user *optval, int optlen)
    {
    	struct sctp_association *asoc;
    	struct list_head *pos;
    	struct sctp_sock *sp = sctp_sk(sk);
    	int val;
    
    	if (optlen < sizeof(int))
    		return -EINVAL;
    	if (get_user(val, (int __user *)optval))
    		return -EFAULT;
    	if ((val < 8) || (val > SCTP_MAX_CHUNK_LEN))
    		return -EINVAL;
    	sp->user_frag = val;
    
    	if (val) {
    		/* Update the frag_point of the existing associations. */
    		list_for_each(pos, &(sp->ep->asocs)) {
    			asoc = list_entry(pos, struct sctp_association, asocs);
    			asoc->frag_point = sctp_frag_point(sp, asoc->pmtu); 
    		}
    	}
    
    	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,
    					     int optlen)
    {
    	struct sctp_sock	*sp;
    	struct sctp_endpoint	*ep;
    	struct sctp_association	*asoc = NULL;
    	struct sctp_setpeerprim	prim;
    	struct sctp_chunk	*chunk;
    	int 			err;
    
    	sp = sctp_sk(sk);
    	ep = sp->ep;
    
    	if (!sctp_addip_enable)
    		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);
    	if (!asoc) 
    		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;
    
    	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);
    
    	SCTP_DEBUG_PRINTK("We set peer primary addr primitively.\n");
    
    	return err;
    }
    
    static int sctp_setsockopt_adaption_layer(struct sock *sk, char __user *optval,
    					  int optlen)
    {
    	__u32 val;
    
    	if (optlen < sizeof(__u32))
    		return -EINVAL;
    	if (copy_from_user(&val, optval, sizeof(__u32)))
    		return -EFAULT;
    
    	sctp_sk(sk)->adaption_ind = val;
    
    	return 0;
    }
    
    /* 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, int optlen)
    {
    	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:
    		/* 'optlen' is the size of the addresses buffer. */
    		retval = sctp_setsockopt_connectx(sk, (struct sockaddr __user *)optval,
    					       optlen);
    		break;
    
    
    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_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_ADAPTION_LAYER:
    		retval = sctp_setsockopt_adaption_layer(sk, optval, optlen);
    		break;
    
    	default:
    		retval = -ENOPROTOOPT;
    		break;
    	};
    
    	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",
    			  __FUNCTION__, 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);
    
    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, sk->sk_socket->file->f_flags & O_NONBLOCK);
    
    	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;
    	return newsk;
    }
    
    /* The SCTP ioctl handler. */
    SCTP_STATIC int sctp_ioctl(struct sock *sk, int cmd, unsigned long arg)
    {
    	return -ENOIOCTLCMD;
    }
    
    /* 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;
    
    	/* 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 = jiffies_to_msecs(sctp_rto_max);
    
    	/* Initialize default RTO related parameters.  These parameters can
    	 * be modified for with the SCTP_RTOINFO socket option.
    	 */
    	sp->rtoinfo.srto_initial = jiffies_to_msecs(sctp_rto_initial);
    	sp->rtoinfo.srto_max     = jiffies_to_msecs(sctp_rto_max);
    	sp->rtoinfo.srto_min     = jiffies_to_msecs(sctp_rto_min);
    
    	/* 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 = 
    		jiffies_to_msecs(sctp_valid_cookie_life);
    
    	/* Initialize default event subscriptions. By default, all the
    	 * options are off. 
    	 */
    	memset(&sp->subscribe, 0, sizeof(struct sctp_event_subscribe));
    
    	/* Default Peer Address Parameters.  These defaults can
    	 * be modified via SCTP_PEER_ADDR_PARAMS
    	 */
    	sp->paddrparam.spp_hbinterval = jiffies_to_msecs(sctp_hb_interval);
    	sp->paddrparam.spp_pathmaxrxt = sctp_max_retrans_path;
    
    	/* If enabled no SCTP message fragmentation will be performed.
    	 * Configure through SCTP_DISABLE_FRAGMENTS socket option.
    	 */
    	sp->disable_fragments = 0;
    
    	/* Turn on/off any Nagle-like algorithm.  */
    	sp->nodelay           = 1;
    
    	/* 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;
    
    	sp->adaption_ind = 0;
    
    	sp->pf = sctp_get_pf_specific(sk->sk_family);
    
    	/* Control variables for partial data delivery. */
    	sp->pd_mode           = 0;
    	skb_queue_head_init(&sp->pd_lobby);
    
    	/* 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);
    	return 0;
    }
    
    /* Cleanup any SCTP per socket resources.  */
    SCTP_STATIC int sctp_destroy_sock(struct sock *sk)
    {
    	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);
    
    	return 0;
    }
    
    /* 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;
    
    	if (len != sizeof(status)) {
    		retval = -EINVAL;
    		goto out;
    	}
    
    	if (copy_from_user(&status, optval, sizeof(status))) {
    		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), sizeof(union sctp_addr));
    	/* 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->pmtu;
    
    
    	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);
    }
    
    
    /* 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;
    
    	if (len != sizeof(pinfo)) {
    		retval = -EINVAL;
    		goto out;
    	}
    
    	if (copy_from_user(&pinfo, optval, sizeof(pinfo))) {
    		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->pmtu;
    
    
    	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);
    }
    
    /* 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))
    		return -EINVAL;
    	if (copy_to_user(optval, &sctp_sk(sk)->subscribe, len))
    		return -EFAULT;
    	return 0;
    }
    
    /* 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.
     */
    static int sctp_getsockopt_autoclose(struct sock *sk, int len, char __user *optval, int __user *optlen)
    {
    	/* Applicable to UDP-style socket only */
    	if (sctp_style(sk, TCP))
    		return -EOPNOTSUPP;
    	if (len != sizeof(int))
    		return -EINVAL;
    	if (copy_to_user(optval, &sctp_sk(sk)->autoclose, len))
    		return -EFAULT;
    	return 0;
    }
    
    /* Helper routine to branch off an association to a new socket.  */
    SCTP_STATIC int sctp_do_peeloff(struct sctp_association *asoc,
    				struct socket **sockp)
    {
    	struct sock *sk = asoc->base.sk;
    	struct socket *sock;
    	int err = 0;
    
    	/* An association cannot be branched off from an already peeled-off
    	 * socket, nor is this supported for tcp style sockets.
    	 */
    	if (!sctp_style(sk, UDP))