/*
* 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 (c) 2014, Joyent, Inc. All rights reserved.
*/
#include <sys/sysmacros.h>
#include <sys/socketvar.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
}
}
if (so->so_filter_active == 0 ||
}
done:
return (error);
}
int
{
int error = 0;
if ((so)->so_filter_active == 0 ||
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 (so->so_filter_active == 0 ||
if (error == EINPROGRESS)
}
done:
return (error);
}
/*ARGSUSED*/
int
{
int error = 0;
EOPNOTSUPP : EINVAL);
}
&nso)) == 0) {
/* finish the accept */
if ((so->so_filter_active > 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_SND_FLOWCTRLD(so)) {
/*
* 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 (so->so_filter_active > 0 &&
if (error != 0)
break;
continue;
}
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;
return (EOPNOTSUPP);
error = 0;
return (EMSGSIZE);
}
break;
}
if (error != 0)
break;
}
/* Socket filters are not flow controlled */
/*
* 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 (so->so_filter_active > 0 &&
if (error != 0)
break;
continue;
}
if (error != 0) {
/*
* The send failed. The protocol will free the mblks
* that were sent down. Let the caller deal with the
* rest.
*/
break;
}
}
/* Let the filter know whether the protocol is flow controlled */
return (error);
}
#pragma inline(so_sendmblk_impl)
int
{
int error;
return (EOPNOTSUPP);
}
B_FALSE);
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;
}
if (so->so_filter_active == 0 ||
/*
* 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;
if (so->so_filter_active == 0 ||
return (error);
}
int
{
int error;
if (accept) {
if (xnet_check_print) {
}
} else if (so->so_filter_active == 0 ||
}
return (error);
}
int
{
int error = 0;
if (level == SOL_FILTER)
if ((so->so_filter_active == 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 (level == SOL_FILTER)
if (xnet_check_print)
return (EINVAL);
}
if (so->so_filter_active > 0 &&
goto done;
if (level == SOL_SOCKET) {
switch (option_name) {
case SO_RCVTIMEO:
case SO_SNDTIMEO: {
/*
* We pass down these two options to protocol in order
* to support some third part protocols which need to
* know them. For those protocols which don't care
* these two options, simply return 0.
*/
if (get_udatamodel() == DATAMODEL_NONE ||
get_udatamodel() == DATAMODEL_NATIVE) {
goto done;
}
sizeof (struct timeval));
} else {
goto done;
}
}
if (option_name == SO_RCVTIMEO)
else
break;
}
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.
*/
if ((so->so_filter_active == 0 ||
}
done:
return (error);
}
int
{
*reventsp = 0;
/*
*/
return (0);
}
/*
* If the socket is in a state where it can send data
* turn on POLLWRBAND and POLLOUT events.
*/
/*
* out of band data is allowed even if the connection
* is flow controlled
*/
if (!SO_SND_FLOWCTRLD(so)) {
/*
* As long as there is buffer to send data
* turn on POLLOUT events
*/
}
}
/*
* 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 we're looking for POLLRDHUP, indicate it if we have sent the
* last rx signal for the socket.
*/
/* 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) {
}
/*
* If the socket has become disconnected, we set POLLHUP.
* Note that if we are in this state, we will have set POLLIN
* (SO_HAVE_DATA() is true on a disconnected socket), but not
* POLLOUT (SS_ISCONNECTED is false). This is in keeping with
* the semantics of POLLHUP, which is defined to be mutually
* exclusive with respect to POLLOUT but not POLLIN. We are
* therefore setting POLLHUP primarily for the benefit of
* those not polling on POLLIN, as they have no other way of
* knowing that the socket has been disconnected.
*/
}
/* Check for read events again, but this time under lock */
if (SO_HAVE_DATA(so) ||
}
return (0);
} else {
}
}
}
return (0);
}
/*
* Generic Upcalls
*/
void
{
}
/*
* Wake ones who're waiting for conn to become established.
*/
}
int
{
/*
* If we aren't currently connected, then this isn't a disconnect but
* rather a failure to connect.
*/
return (0);
}
void
{
switch (action) {
case SOCK_OPCTL_SHUT_SEND:
break;
case SOCK_OPCTL_SHUT_RECV: {
break;
}
case SOCK_OPCTL_ENAB_ACCEPT:
/*
* The protocol can stop generating newconn upcalls when
* the backlog is full, so to make sure the listener does
* not end up with a queue full of deferred connections
* we reduce the backlog by one. Thus the listener will
* start closing deferred connections before the backlog
* is full.
*/
if (so->so_filter_active > 0)
break;
default:
ASSERT(0);
break;
}
}
void
{
if (qfull) {
} else {
/* so_notify_writable drops so_lock */
}
}
{
int error;
return (NULL);
}
&error);
return (NULL);
}
/*
* The new socket (nso), proto_handle and sock_upcallsp are all
* valid at this point. But as soon as nso is placed in the accept
* queue that can no longer be assumed (since an accept() thread may
* pull it off the queue and close the socket).
*/
*sock_upcallsp = &so_upcalls;
/* drop proto ref */
return (NULL);
} else {
so->so_acceptq_len++;
} else {
}
return ((sock_upper_handle_t)nso);
}
}
void
{
}
}
}
if (so->so_filter_active > 0) {
}
}
}
#ifdef DEBUG
#endif
}
/* ARGSUSED */
{
int space_left;
*errorp = 0;
/* the notify functions will drop the lock */
else
return (0);
}
goto space_check;
}
/* The read pointer is not aligned correctly for TPI */
"sockfs: Unaligned TPI message received. rptr = %p\n",
goto space_check;
}
if (so->so_filter_active > 0) {
continue;
if (msg_size == 0) {
goto space_check;
}
}
}
goto space_check;
}
if (force_pushp != NULL)
*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);
if (space_left <= 0) {
space_left = -1;
}
goto done_unlock;
}
#pragma inline(so_queue_msg_impl)
{
so->so_filter_bottom));
}
/*
* 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;
/*
* No new data will be enqueued once the CLOSING flag is set.
*/
if (so->so_filter_active > 0)
/*
* We grab and release the accept lock to ensure that any
* thread about to insert a socket in so_newconn completes
* before we flush the queue. Any thread calling so_newconn
* after we drop the lock will observe the SS_CLOSING flag,
* which will stop it from inserting the socket in the queue.
*/
}
switch (error) {
default:
/* Protocol made a synchronous close; remove proto ref */
break;
case EINPROGRESS:
/*
* Protocol is in the process of closing, it will make a
* 'closed' upcall to remove the reference.
*/
error = 0;
break;
}
return (error);
}
/*
* Upcall made by the protocol when it's doing an asynchronous close. It
* will drop the protocol's reference on the socket.
*/
void
{
}
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;
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
*/
flags |= MSG_NOMARK;
goto retry;
}
goto out_locked;
}
/* so_queue_msg 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
*/
flags |= MSG_NOMARK;
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: {
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 &&
flags |= MSG_NOMARK;
goto retry;
}
goto out_locked;
}
default:
ASSERT(0);
}
out:
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 */
};
};