tcp_socket.c revision 721fffe35d40e548a5a58dc53a2ec9c6762172d9
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* This file contains all TCP kernel socket related functions. */
#include <sys/squeue_impl.h>
#include <sys/tpicommon.h>
#include <sys/socketvar.h>
#include <inet/proto_set.h>
#include <inet/tcp_impl.h>
sock_upcalls_t *, int, cred_t *);
static int tcp_getsockopt(sock_lower_handle_t, int, int, void *,
static int tcp_setsockopt(sock_lower_handle_t, int, int, const void *,
static void tcp_clr_flowctrl(sock_lower_handle_t);
cred_t *);
NULL,
NULL,
NULL,
};
/* ARGSUSED */
static void
{
struct sock_proto_props sopp;
extern struct module_info tcp_rinfo;
/* All Solaris components should pass a cred for this operation. */
}
static int
{
/*
* It is OK to manipulate these fields outside the eager's squeue
* because they will not start being used until tcp_accept_finish
* has been called.
*/
}
static int
{
int error;
/* All Solaris components should pass a cred for this operation. */
if (error != 0) {
/* failed to enter */
return (ENOSR);
}
/* binding to a NULL address really means unbind */
else
} else {
}
if (error < 0) {
else
}
return (error);
}
/*
* SOP_LISTEN() calls into tcp_listen().
*/
/* ARGSUSED */
static int
{
int error;
/* All Solaris components should pass a cred for this operation. */
if (error != 0) {
/* failed to enter */
return (ENOBUFS);
}
if (error == 0) {
} else if (error < 0) {
else
}
return (error);
}
static int
{
int error;
/* All Solaris components should pass a cred for this operation. */
if (error != 0) {
return (error);
}
if (error != 0) {
/* failed to enter */
return (ENOSR);
}
/*
* TCP supports quick connect, so no need to do an implicit bind
*/
if (error == 0) {
} else if (error < 0) {
case TCPS_SYN_SENT:
break;
case TCPS_ESTABLISHED:
break;
case TCPS_LISTEN:
error = EOPNOTSUPP;
break;
default:
break;
}
} else {
}
}
struct sock_proto_props sopp;
}
done:
}
/* ARGSUSED3 */
int
{
/* All Solaris components should pass a cred for this operation. */
return (ENOTCONN);
}
/* ARGSUSED3 */
int
{
/* All Solaris components should pass a cred for this operation. */
}
/* returns UNIX error, the optlen is a value-result arg */
static int
{
int error;
void *optvalp_buf;
int len;
if (error != 0) {
if (error < 0) {
}
return (error);
}
return (ENOMEM);
}
if (len == -1) {
return (EINVAL);
}
/*
* update optlen and copy option value
*/
return (0);
}
static int
{
int error;
/*
* Entering the squeue synchronously can result in a context switch,
* which can cause a rather sever performance degradation. So we try to
* handle whatever options we can without entering the squeue.
*/
if (level == IPPROTO_TCP) {
switch (option_name) {
case TCP_NODELAY:
return (EINVAL);
return (0);
default:
break;
}
}
return (ENOMEM);
}
if (error != 0) {
if (error < 0) {
}
return (error);
}
return (error);
}
/* ARGSUSED */
static int
{
/* All Solaris components should pass a cred for this operation. */
if (msg->msg_controllen != 0) {
return (EOPNOTSUPP);
}
case M_DATA:
if (tcpstate < TCPS_ESTABLISHED) {
/*
* We return ENOTCONN if the endpoint is trying to
* connect or has never been connected, and EPIPE if it
* has been disconnected. The connection id helps us
* distinguish between the last two cases.
*/
} else if (tcpstate > TCPS_CLOSE_WAIT) {
return (EPIPE);
}
/*
* Squeue Flow Control
*/
}
/*
* The application may pass in an address in the msghdr, but
* we ignore the address on connection-oriented sockets.
* Just like BSD this code does not generate an error for
* TCP (a CONNREQUIRED socket) when sending to an address
* delivered on the connection as if no address had been
* supplied.
*/
} else {
}
return (0);
default:
ASSERT(0);
}
return (0);
}
/* ARGSUSED */
static int
{
/* All Solaris components should pass a cred for this operation. */
/*
*/
return (ENOTCONN);
/* shutdown the send side */
SOCK_OPCTL_SHUT_SEND, 0);
}
/* shutdown the recv side */
SOCK_OPCTL_SHUT_RECV, 0);
return (0);
}
static void
{
int error;
/*
* If tcp->tcp_rsrv_mp == NULL, it means that tcp_clr_flowctrl()
* is currently running.
*/
return;
}
} else {
/*
* Send back a window update immediately if TCP is above
* ESTABLISHED state and the increase of the rcv window
* that the other side knows is at least 1 MSS after flow
* control is lifted.
*/
}
}
}
/* ARGSUSED */
static int
{
int error;
/* All Solaris components should pass a cred for this operation. */
/*
* If we don't have a helper stream then create one.
* ip_create_helper_stream takes care of locking the conn_t,
* so this check for NULL is just a performance optimization.
*/
/*
* Create a helper stream for non-STREAMS socket.
*/
if (error != 0) {
ip0dbg(("tcp_ioctl: create of IP helper stream "
"failed %d\n", error));
return (error);
}
}
switch (cmd) {
case ND_SET:
case ND_GET:
case _SIOCSOCKFALLBACK:
case TCP_IOC_ABORT_CONN:
case TI_GETPEERNAME:
case TI_GETMYNAME:
ip1dbg(("tcp_ioctl: cmd 0x%x on non streams socket",
cmd));
break;
default:
/*
* If the conn is not closing, pass on to IP using
* helper stream. Bump the ioctlref to prevent tcp_close
* if it ends up queued or aborted/interrupted.
*/
break;
}
break;
}
return (error);
}
/* ARGSUSED */
static int
{
/* All Solaris components should pass a cred for this operation. */
/*
* Drop IP's reference on the conn. This is the last reference
* on the connp if the state was less than established. If the
* connection has gone into timewait state, then we will have
* one ref for the TCP and one more ref (total of two) for the
* classifier connected hash list (a timewait connections stays
* in connected hash till closed).
*
* We can't assert the references because there might be other
* transient reference places because of some walkers or queued
* packets in squeue for the timewait state.
*/
return (0);
}
/* ARGSUSED */
{
return (NULL);
}
return (NULL);
}
/*
* Put the ref for TCP. Ref for IP was already put
* by ipcl_conn_create. Also Make the conn_t globally
* visible to walkers
*/
*errorp = 0;
return ((sock_lower_handle_t)connp);
}
int
{
int error;
NULL);
/* Pre-allocate the T_ordrel_ind mblk. */
/*
* Enter the squeue so that no new packets can come in
*/
if (error != 0) {
/* failed to enter, free all the pre-allocated messages. */
/*
* We cannot process the eager, so at least send out a
* RST so the peer can reconnect.
*/
}
return (ENOMEM);
}
/*
* Both endpoints must be of the same type (either STREAMS or
* non-STREAMS) for fusion to be enabled. So if we are fused,
* we have to unfuse.
*/
/*
* No longer a direct socket
*/
/* The eager will deal with opts when accept() is called */
} else {
}
/*
* There should be atleast two ref's (IP + TCP)
*/
return (0);
}