sockcommon_sops.c revision e5083e819e9d0322245ac20a9e8d367b88b4064f
/*
* 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 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "@(#)sockcommon_sops.c 1.1 07/06/14 SMI"
#include <sys/sysmacros.h>
#include <sys/socketvar.h>
#define _SUN_TPI_VERSION 2
#include <sys/sodirect.h>
#include <sys/kmem_impl.h>
#include <sys/socket_proto.h>
extern int xnet_skip_checks;
extern int xnet_check_print;
/*ARGSUSED*/
int
{
return (EOPNOTSUPP);
}
/*ARGSUSED*/
int
{
return (EOPNOTSUPP);
}
/*ARGSUSED*/
int
{
return (EOPNOTSUPP);
}
/*ARGSUSED*/
int
{
return (EOPNOTSUPP);
}
/*ARGSUSED*/
int
{
return (EOPNOTSUPP);
}
/*ARGSUSED*/
int
{
return (EOPNOTSUPP);
}
/*
* Generic Socket Ops
*/
/* ARGSUSED */
int
{
}
int
{
int error;
if (xnet_check_print) {
"caused EINVAL\n");
}
goto done;
}
/*
* a bind to a NULL address is interpreted as unbind. So just
* do the downcall.
*/
goto dobind;
case AF_INET:
goto done;
}
if ((flags & _SOBIND_XPG4_2) &&
/*
* sockets however application failures have
* been observed when it is applied to
* all sockets.
*/
goto done;
}
/*
* Force a zero sa_family to match so_family.
*
* Some programs like inetd(1M) don't set the
* family field. Other programs leave
* sin_family set to garbage - SunOS 4.X does
* not check the family field on a bind.
* We use the family field that
* was passed in to the socket() call.
*/
break;
case AF_INET6: {
#ifdef DEBUG
#endif
goto done;
}
/*
* With IPv6 we require the family to match
* unlike in IPv4.
*/
goto done;
}
#ifdef DEBUG
/*
* Verify that apps don't forget to clear
* sin6_scope_id etc
*/
if (sin6->sin6_scope_id != 0 &&
"bind with uninitialized sin6_scope_id "
"(%d) on socket. Pid = %d\n",
(int)sin6->sin6_scope_id,
}
if (sin6->__sin6_src_id != 0) {
"bind with uninitialized __sin6_src_id "
"(%d) on socket. Pid = %d\n",
(int)sin6->__sin6_src_id,
}
#endif /* DEBUG */
break;
}
default:
/* Just pass the request to the protocol */
goto dobind;
}
/*
* First we check if either NCA or KSSL has been enabled for
* the requested address, and if so, we fall back to TPI.
* If neither of those two services are enabled, then we just
* pass the request to the protocol.
*
* Note that KSSL can only be enabled on a socket if NCA is NOT
* enabled for that socket, hence the else-statement below.
*/
/*
* NL7C is not supported in non-global zones,
* we enforce this restriction here.
*/
/* NCA should be used, so fall back to TPI */
if (error)
return (error);
else
}
/* Check if KSSL has been configured for this address */
struct T_bind_req bind_req;
/*
* TODO: Check with KSSL team if we could add a function call
* that only queries whether KSSL is enabled for the given
* address.
*/
if (type != KSSL_NO_PROXY) {
/*
* KSSL has been configured for this address, so
* we must fall back to TPI.
*/
if (error)
return (error);
else
}
}
done:
return (error);
}
int
{
int error = 0;
cr);
return (error);
}
int
{
int error = 0;
/*
* If there is a pending error, return error
* This can happen if a non blocking operation caused an error.
*/
if (error != 0)
goto done;
}
if (error == EINPROGRESS)
done:
return (error);
}
/*ARGSUSED*/
int
{
int error = 0;
EOPNOTSUPP : EINVAL);
}
&nso)) == 0) {
/* finish the accept */
if (error != 0) {
} else {
}
}
return (error);
}
int
{
error = 0;
/*
* Old way of passing fd's is not supported
*/
return (EOPNOTSUPP);
}
return (EMSGSIZE);
}
/*
* For atomic sends we will only do one iteration.
*/
do {
break;
}
if (error != 0)
break;
}
/*
* Send down OOB messages even if the send path is being
* flow controlled (assuming the protocol supports OOB data).
*/
error = EOPNOTSUPP;
break;
}
} else if (so->so_snd_qfull) {
/*
* Need to wait until the protocol is ready to receive
* more data for transmission.
*/
break;
}
/*
* Time to send data to the protocol. We either copy the
* data into mblks or pass the uio directly to the protocol.
* We decide what to do based on the available down calls.
*/
if (error != 0)
break;
} else {
/* save the resid in case of failure */
break;
}
if (error != 0) {
/*
* The send failed. We do not have to free the
* mblks, because that is the protocol's
* responsibility. However, uio_resid must
* remain accurate, so adjust that here.
*/
break;
}
}
return (error);
}
int
{
int error;
error = 0;
return (EOPNOTSUPP);
}
return (EMSGSIZE);
}
break;
}
if (error != 0)
break;
}
if (so->so_snd_qfull) {
/*
* Need to wait until the protocol is ready to receive
* more data for transmission.
*/
break;
}
/*
* We only allow so_maxpsz of data to be sent down to
* the protocol at time.
*/
break;
}
}
if (error != 0) {
/*
* The send failed. The protocol will free the mblks
* that were sent down. Let the caller deal with the
* rest.
*/
break;
}
}
return (error);
}
int
{
int error;
/*
* SunOS 4.X has no check for datagram sockets.
* 5.X checks that it is connected (ENOTCONN)
*/
if (!xnet_skip_checks) {
if (xnet_check_print) {
"caused ENOTCONN\n");
}
}
goto done;
}
/*
* Protocol agreed to shutdown. We need to flush the
* receive buffer if the receive side is being shutdown.
*/
/* wait for active reader to finish */
(void) so_lock_read(so, 0);
}
done:
return (error);
}
int
{
int error;
return (error);
}
int
{
int error;
if (accept) {
if (xnet_check_print) {
}
} else {
}
return (error);
}
int
{
int error = 0;
flags);
if (error < 0) {
cr);
if (error == ENOPROTOOPT) {
if (level == SOL_SOCKET) {
/*
* If a protocol does not support a particular
* socket option, set can fail (not allowed)
* but get can not fail. This is the previous
* sockfs bahvior.
*/
switch (option_name) {
case SO_LINGER:
if (*optlenp < (t_uscalar_t)
sizeof (struct linger)) {
break;
}
error = 0;
break;
case SO_RCVTIMEO:
case SO_SNDTIMEO:
if (*optlenp < (t_uscalar_t)
sizeof (struct timeval)) {
break;
}
error = 0;
break;
case SO_SND_BUFINFO:
if (*optlenp < (t_uscalar_t)
sizeof (struct so_snd_bufinfo)) {
break;
}
error = 0;
sizeof (struct so_snd_bufinfo));
*optlenp =
sizeof (struct so_snd_bufinfo);
break;
case SO_DEBUG:
case SO_REUSEADDR:
case SO_KEEPALIVE:
case SO_DONTROUTE:
case SO_BROADCAST:
case SO_USELOOPBACK:
case SO_OOBINLINE:
case SO_DGRAM_ERRIND:
case SO_SNDBUF:
case SO_RCVBUF:
error = 0;
break;
default:
break;
}
}
}
}
return (error);
}
int
{
int error = 0;
if (xnet_check_print)
return (EINVAL);
}
if (level == SOL_SOCKET) {
switch (option_name) {
case SO_RCVTIMEO:
case SO_SNDTIMEO: {
if (get_udatamodel() == DATAMODEL_NONE ||
get_udatamodel() == DATAMODEL_NATIVE) {
goto done;
}
sizeof (struct timeval));
} else {
goto done;
}
}
if (option_name == SO_RCVTIMEO)
else
return (0);
}
case SO_RCVBUF:
/*
* XXX XPG 4.2 applications retrieve SO_RCVBUF from
* sockfs since the transport might adjust the value
* and not return exactly what was set by the
* application.
*/
break;
}
}
done:
return (error);
}
int
{
int error = 0;
/*
* If there is a pending error, return error
* This can happen if a non blocking operation caused an error.
*/
if (error != 0)
goto done;
}
/*
* calling strioc can result in the socket falling back to TPI,
* if that is supported.
*/
}
done:
return (error);
}
int
{
*reventsp = 0;
return (0);
}
/*
* As long as there is buffer to send data, and the socket is
* in a state where it can send data (i.e., connected for
* connection oriented protocols), then turn on POLLOUT events
*/
state & SS_ISCONNECTED)) {
}
/*
* Turn on POLLIN whenever there is data on the receive queue,
* or the socket is in a state where no more data will be received.
* Also, if the socket is accepting connections, flip the bit if
* there is something on the queue.
*
* We do an initial check for events without holding locks. However,
* if there are no event available, then we redo the check for POLLIN
* events under the lock.
*/
/* Pending connections */
if (so->so_acceptq_len > 0)
/* Data */
/* so_downcalls is null for sctp */
/* do not recheck events */
events &= ~SO_PROTO_POLLEV;
} else {
if (SO_HAVE_DATA(so))
/* Urgent data */
if ((state & SS_OOBPEND) != 0)
}
/* Check for read events again, but this time under lock */
return (0);
} else {
}
}
}
return (0);
}
/*
* Generic Upcalls
*/
void
{
}
/*
* Wake ones who're waiting for conn to become established.
*/
}
int
{
return (0);
}
void
{
switch (action) {
case SOCK_OPCTL_SHUT_SEND:
break;
case SOCK_OPCTL_SHUT_RECV: {
break;
}
case SOCK_OPCTL_ENAB_ACCEPT:
break;
default:
ASSERT(0);
break;
}
}
void
{
if (qfull) {
} else {
}
}
{
int error;
return (NULL);
&error);
return (NULL);
}
*sock_upcallsp = &so_upcalls;
return ((sock_upper_handle_t)nso);
}
void
{
}
}
}
#ifdef DEBUG
#endif
}
/* ARGSUSED */
{
int space_left;
*errorp = 0;
if (msg_size > 0) {
/* the notify functions will drop the lock */
else
return (0);
}
/*
* recv space check
*/
if (space_left <= 0) {
space_left = -1;
}
goto done_unlock;
}
return (0);
}
if (force_pushp != NULL)
/* The read pointer is not aligned correctly for TPI */
"sockfs: Unaligned TPI message received. rptr = %p\n",
}
*errorp = EOPNOTSUPP;
return (-1);
}
return (0);
}
} else {
}
}
} else {
do {
}
}
if (space_left <= 0) {
space_left = -1;
}
/*
* so_notify_data will release the lock
*/
if (force_pushp != NULL)
*force_pushp = B_TRUE;
goto done;
} else if (so->so_rcv_timer_tid == 0) {
/* Make sure the recv push timer is running */
}
done:
return (space_left);
}
/*
* Set the offset of where the oob data is relative to the bytes in
* queued. Also generate SIGURG
*/
void
{
/*
* New urgent data on the way so forget about any old
* urgent data.
*/
/*
* Record that urgent data is pending.
*/
}
/*
* set the offset where the urgent byte is
*/
if (so->so_oobmark == 0)
else
}
/*
* Queue the OOB byte
*/
static void
{
if (!IS_SO_OOB_INLINE(so)) {
} else {
}
}
int
{
int error;
/*
* At this point there will be no more upcalls from the protocol
*/
return (error);
}
void
{
}
void
{
}
/*
* so_recvmsg - read data from the socket
*
* There are two ways of obtaining data; either we ask the protocol to
* copy directly into the supplied buffer, or we copy data from the
* sonode's receive queue. The decision which one to use depends on
* whether the protocol has a sd_recv_uio down call.
*/
int
{
int flags = 0;
int error = 0;
int ret;
union T_primitives *tpr;
void *control;
return (ENOTCONN);
}
error = EOPNOTSUPP;
} else {
}
return (error);
}
/*
* If the protocol has the recv down call, then pass the request
* down.
*/
return (error);
}
/*
* Reading data from the socket buffer
*/
/*
* Set msg_controllen and msg_namelen to zero here to make it
* simpler in the cases that no control or name is returned.
*/
msg->msg_controllen = 0;
msg->msg_namelen = 0;
/* Set SOREADLOCKED */
if (error) {
return (error);
}
if (error != 0) {
goto out;
}
/*
* For datagrams the MOREDATA flag is used to set MSG_TRUNC.
* For non-datagrams MOREDATA is used to set MSG_EOR.
*/
/* Set MSG_EOR based on MOREDATA */
}
}
/*
* If some data was received (i.e. not EOF) and the
*/
goto retry;
}
goto out_locked;
}
/* strsock_proto has already verified length and alignment */
case T_DATA_IND: {
/*
* Set msg_flags to MSG_EOR based on
* MORE_flag and MOREDATA.
*/
else
}
/*
* If some data was received (i.e. not EOF) and the
*/
goto retry;
}
goto out_locked;
}
case T_UNITDATA_IND: {
void *addr;
void *abuf;
void *opt;
if (namelen != 0) {
/* Caller wants source address */
addrlen, 1);
goto out;
}
}
if (optlen != 0) {
/*
* Extract any source address option.
* Determine how large cmsg buffer is needed.
*/
goto out;
}
!(flags & MSG_XPG4_2));
if (controllen != 0)
else if (ncontrollen != 0)
} else {
controllen = 0;
}
if (namelen != 0) {
/*
* Return address to caller.
* Caller handles truncation if length
* exceeds msg_namelen.
* NOTE: AF_UNIX NUL termination is ensured by
* the sender's copyin_name().
*/
}
if (controllen != 0) {
/*
* Return control msg to caller.
* Caller handles truncation if length
* exceeds msg_controllen.
*/
if (error) {
if (msg->msg_namelen != 0)
msg->msg_namelen);
goto out;
}
}
goto out;
}
case T_OPTDATA_IND: {
struct T_optdata_req *tdr;
void *opt;
if (optlen != 0) {
/*
* Determine how large cmsg buffer is needed.
*/
goto out;
}
!(flags & MSG_XPG4_2));
if (controllen != 0)
else if (ncontrollen != 0)
} else {
controllen = 0;
}
if (controllen != 0) {
/*
* Return control msg to caller.
* Caller handles truncation if length
* exceeds msg_controllen.
*/
if (error) {
goto out;
}
}
/*
* Set msg_flags to MSG_EOR based on
* DATA_flag and MOREDATA.
*/
else
}
/*
* If some data was received (i.e. not EOF) and the
* Not possible to wait if control info was received.
*/
controllen == 0 &&
goto retry;
}
goto out_locked;
}
default:
ASSERT(0);
}
out:
/* The sod_lockp pointers to the sonode so_lock */
return (error);
}
so_init, /* sop_init */
so_accept, /* sop_accept */
so_bind, /* sop_bind */
so_listen, /* sop_listen */
so_connect, /* sop_connect */
so_recvmsg, /* sop_recvmsg */
so_sendmsg, /* sop_sendmsg */
so_sendmblk, /* sop_sendmblk */
so_getpeername, /* sop_getpeername */
so_getsockname, /* sop_getsockname */
so_shutdown, /* sop_shutdown */
so_getsockopt, /* sop_getsockopt */
so_setsockopt, /* sop_setsockopt */
so_ioctl, /* sop_ioctl */
so_poll, /* sop_poll */
so_close, /* sop_close */
};
};