socket.c revision fa9e4066f08beec538e775443c5be79dd423fcab
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (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 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
* socket.c, Code implementing a simple socket interface.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include "socket_impl.h"
#include <sys/isa_defs.h>
#include <sys/sysmacros.h>
#include <sys/bootconf.h>
#include "socket_inet.h"
#include "ipv4.h"
#include "ipv4_impl.h"
#include "udp_inet.h"
#include "tcp_inet.h"
#include "mac.h"
#include "mac_impl.h"
/* Default send and receive socket buffer size */
/* Default max socket buffer size */
const struct sockaddr *, int);
static int bind_check(int, const struct sockaddr *);
static int quickbind(int);
/* Check the validity of a fd and return the socket index of that fd. */
int
{
int i;
i = FD_TO_SOCKET(fd);
if (i < 0 || i >= MAXSOCKET) {
return (-1);
}
return (-1);
}
return (i);
}
/*
* Create an endpoint for network communication. Returns a descriptor.
*
* Notes:
* Only PF_INET communication domains are supported. Within
* this domain, only SOCK_RAW, SOCK_DGRAM and SOCK_STREAM types are
* supported.
*/
int
{
static int sock_initialized;
int i;
errno = 0;
if (!sock_initialized) {
for (i = 0; i < MAXSOCKET; i++)
}
return (-1);
}
/* Find available socket */
for (i = 0; i < MAXSOCKET; i++) {
break;
}
if (i >= MAXSOCKET) {
return (-1);
}
/* Some socket initialization... */
/*
* Note that we ignore the protocol field for SOCK_DGRAM and
* SOCK_STREAM. When we support different protocols in future,
* this needs to be changed.
*/
switch (type) {
case SOCK_RAW:
break;
case SOCK_DGRAM:
udp_socket_init(&sockets[i]);
break;
case SOCK_STREAM:
tcp_socket_init(&sockets[i]);
break;
default:
errno = EPROTOTYPE;
break;
}
if (errno != 0)
return (-1);
/* IPv4 generic initialization. */
ipv4_socket_init(&sockets[i]);
/* MAC generic initialization. */
mac_socket_init(&sockets[i]);
return (i + SOCKETTYPE);
}
int
{
int i;
errno = 0;
return (-1);
if (*namelen < sizeof (struct sockaddr_in)) {
return (-1);
}
/* Structure assignment... */
*namelen = sizeof (struct sockaddr_in);
return (0);
}
/*
* The socket options we support are:
* SO_RCVTIMEO - Value is in msecs, and is of uint32_t.
* SO_DONTROUTE - Value is an int, and is a boolean (nonzero if set).
* SO_REUSEADDR - Value is an int boolean.
* SO_RCVBUF - Value is an int.
* SO_SNDBUF - Value is an int.
*/
int
{
int i;
errno = 0;
return (-1);
switch (level) {
case SOL_SOCKET: {
switch (option) {
case SO_RCVTIMEO:
} else {
*optlen = 0;
}
break;
case SO_DONTROUTE:
if (*optlen == sizeof (int)) {
*(int *)optval =
} else {
*optlen = 0;
}
break;
case SO_REUSEADDR:
if (*optlen == sizeof (int)) {
*(int *)optval =
} else {
*optlen = 0;
}
break;
case SO_RCVBUF:
if (*optlen == sizeof (int)) {
} else {
*optlen = 0;
}
break;
case SO_SNDBUF:
if (*optlen == sizeof (int)) {
} else {
*optlen = 0;
}
break;
case SO_LINGER:
/* struct copy */
} else {
*optlen = 0;
}
default:
errno = ENOPROTOOPT;
break;
}
break;
} /* case SOL_SOCKET */
case IPPROTO_TCP:
case IPPROTO_IP: {
switch (option) {
default:
*optlen = 0;
errno = ENOPROTOOPT;
break;
}
break;
} /* case IPPROTO_IP or IPPROTO_TCP */
default:
errno = ENOPROTOOPT;
break;
} /* switch (level) */
if (errno != 0)
return (-1);
else
return (0);
}
/*
* Generate a network-order source port from the privileged range if
* range of 512-1023 privileged ports as ports we can use. This mirrors
* historical rpc client practice for privileged port selection.
*/
{
in_port_t p;
if (reserved) {
if (++rsvdport >= IPPORT_RESERVED)
else
p = rsvdport;
} else
p = ++dynamic;
return (htons(p));
}
/*
* The socket options we support are:
* SO_RECVTIMEO - Value is uint32_t msecs.
* SO_DONTROUTE - Value is int boolean (nonzero == TRUE, zero == FALSE).
* SO_REUSEADDR - value is int boolean.
* SO_RCVBUF - Value is int.
* SO_SNDBUF - Value is int.
*/
int
{
int i;
errno = 0;
return (-1);
switch (level) {
case SOL_SOCKET: {
switch (option) {
case SO_RCVTIMEO:
else {
}
break;
case SO_DONTROUTE:
if (optlen == sizeof (int)) {
if (*(int *)optval)
else
} else {
}
break;
case SO_REUSEADDR:
if (optlen == sizeof (int)) {
if (*(int *)optval)
else
} else {
}
break;
case SO_RCVBUF:
if (optlen == sizeof (int)) {
} else {
}
break;
case SO_SNDBUF:
if (optlen == sizeof (int)) {
} else {
}
break;
case SO_LINGER:
/* struct copy */
} else {
}
break;
default:
errno = ENOPROTOOPT;
break;
}
break;
} /* case SOL_SOCKET */
case IPPROTO_TCP:
case IPPROTO_IP: {
switch (option) {
default:
errno = ENOPROTOOPT;
break;
}
break;
} /* case IPPROTO_IP or IPPROTO_TCP */
default:
errno = ENOPROTOOPT;
break;
} /* switch (level) */
if (errno != 0)
return (-1);
else
return (0);
}
/*
* Shut down part of a full-duplex connection.
*
* Only supported for TCP sockets
*/
int
{
int sock_id;
int i;
errno = 0;
return (-1);
/* shutdown only supported for TCP sockets */
errno = EOPNOTSUPP;
return (-1);
}
return (-1);
}
switch (how) {
case 0:
break;
case 1:
break;
case 2:
break;
default:
return (-1);
}
(SS_CANTRCVMORE | SS_CANTSENDMORE)) {
case (SS_CANTRCVMORE | SS_CANTSENDMORE):
/* Call lower level protocol close routine. */
for (i = TRANSPORT_LVL; i >= MEDIA_LVL; i--) {
}
}
break;
case SS_CANTRCVMORE:
break;
case SS_CANTSENDMORE:
/* Call lower level protocol close routine. */
if (tcp_shutdown(sock_id) < 0)
return (-1);
break;
default:
return (-1);
}
return (0);
}
/*
* "close" a socket.
*/
int
socket_close(int s)
{
int sock_id, i;
errno = 0;
return (-1);
/* Call lower level protocol close routine. */
for (i = TRANSPORT_LVL; i >= MEDIA_LVL; i--) {
/*
* Note that the close() routine of other
* layers can return an error. But right
* now, the only mechanism to report that
* back is for the close() routine to set
* the errno and socket_close() will return
* an error. But the close operation will
* not be stopped.
*/
}
}
/*
* Clear the input queue. This has to be done
* after the lower level protocol close routines have been
* called as they may want to do something about the queue.
*/
return (0);
}
/*
* Read up to `nbyte' of data from socket `s' into `buf'; if non-zero,
* then give up after `read_timeout' seconds. Returns the number of
* bytes read, or -1 on failure.
*/
int
{
ssize_t n;
/*
* keep calling non-blocking recvfrom until something received
* or an error occurs
*/
start = prom_gettime();
for (;;) {
return (-1);
}
} else {
return (n);
}
}
}
/*
* Write up to `nbyte' bytes of data from `buf' to the address pointed to
* `addr' using socket `s'. Returns the number of bytes writte on success,
* or -1 on failure.
*/
int
{
sizeof (*addr)));
}
static int
{
int k;
/* Do not check for duplicate bind() if SO_REUSEADDR option is set. */
for (k = 0; k < MAXSOCKET; k++) {
errno = EADDRINUSE;
return (-1);
}
}
}
}
return (0);
}
/* Assign a name to an unnamed socket. */
int
{
int i;
errno = 0;
return (-1);
/* unbind */
sizeof (struct sockaddr_in));
}
return (0);
}
return (-1);
}
return (-1);
}
namelen) == 0) {
/* attempt to bind to same address ok... */
return (0);
}
return (-1);
}
if (errno != 0) {
return (-1);
}
/* Check for duplicate bind(). */
if (bind_check(i, name) < 0)
return (-1);
if (tcp_bind(i) < 0) {
return (-1);
}
}
return (0);
}
static int
{
int i;
struct sockaddr_in addr;
/*
* XXX This needs more work. Right now, if ipv4_setipaddr()
* have not been called, this will be wrong. But we need
* something better. Need to be revisited.
*/
for (i = SMALLEST_ANON_PORT; i <= LARGEST_ANON_PORT; i++) {
break;
}
/* Need to clear errno as it is probably set by bind_check(). */
errno = 0;
if (i <= LARGEST_ANON_PORT) {
sizeof (struct sockaddr_in));
#ifdef DEBUG
printf("quick bind done addr %s port %d\n",
#endif
return (0);
} else {
return (-1);
}
}
int
{
int sock_id;
errno = 0;
return (-1);
errno = EOPNOTSUPP;
return (-1);
}
return (-1);
}
}
int
{
int sock_id;
int new_sd;
errno = 0;
return (-1);
errno = EOPNOTSUPP;
return (-1);
}
return (-1);
}
return (-1);
return (new_sd);
}
int
{
int sock_id;
int so_type;
errno = 0;
return (-1);
return (-1);
}
/* Don't allow connect for raw socket. */
if (so_type == INETBOOT_RAW) {
return (-1);
}
return (-1);
}
return (-1);
}
/* If the socket is not bound, we need to do a quick bind. */
/* For TCP socket, just call tcp_bind(). */
if (so_type == INETBOOT_STREAM) {
return (-1);
} else {
return (-1);
}
}
}
/* Should do some sanity check for addr .... */
sizeof (struct sockaddr_in));
/* Call TCP connect routine. */
if (tcp_connect(sock_id) == 0)
else {
return (-1);
}
} else {
}
return (0);
}
/* Just a wrapper around recvfrom(). */
{
}
/*
* Receive messages from a connectionless socket. Legal flags are 0 and
* MSG_DONTWAIT. MSG_WAITALL is not currently supported.
*
* Returns length of message for success, -1 if error occurred.
*/
{
int sock_id, i;
char *tmp_buf;
errno = 0;
return (-1);
}
return (-1);
}
return (-1);
}
/* Yup - MSG_WAITALL not implemented */
if ((flags & ~MSG_DONTWAIT) != 0) {
return (-1);
}
/* Go out and check the wire */
errno =
}
return (-1);
}
}
}
}
/* Remove unknown inetgrams from the head of inq. Can this happen? */
if ((so_type == INETBOOT_DGRAM ||
so_type == INETBOOT_STREAM) &&
#ifdef DEBUG
printf("recvfrom: unexpected level %d frame found\n",
#endif /* DEBUG */
continue;
} else {
break;
}
}
/*
* Checking for error should be done everytime a lower layer
* input routing is called. For example, if TCP gets a RST,
* this should be reported asap.
*/
return (-1);
} else {
return (0);
}
}
if ((flags & MSG_DONTWAIT) == 0)
goto retry; /* wait forever */
/* no data */
errno = EWOULDBLOCK;
return (-1);
}
switch (so_type) {
case INETBOOT_STREAM:
/* Need to copy from the socket's remote address. */
sizeof (struct sockaddr_in)));
break;
case INETBOOT_RAW:
case INETBOOT_DGRAM:
default:
break;
}
}
switch (so_type) {
case INETBOOT_STREAM:
/*
* If the message has igm_id == TCP_CALLB_MAGIC_ID, we need
* to drain the data held by tcp and try again.
*/
goto retry;
}
/* TCP should put only user data in the inetgram. */
break;
} else {
/*
* If we have any embedded magic messages just
* drop them.
*/
break;
B_TRUE);
}
break;
}
}
break;
case INETBOOT_DGRAM:
else
break;
case INETBOOT_RAW:
default:
else
break;
}
#ifdef DEBUG
#endif /* DEBUG */
return (bytes);
}
/* Just a wrapper around sendto(). */
{
}
/*
* Transmit a message through a socket.
*
* Supported flags: MSG_DONTROUTE or 0.
*/
{
int sock_id;
errno = 0;
return (-1);
}
return (-1);
}
if ((flags & ~MSG_DONTROUTE) != 0) {
return (-1);
}
return (-1);
}
return (-1);
}
switch (so_type) {
case INETBOOT_RAW:
case INETBOOT_DGRAM:
return (-1);
}
break;
case INETBOOT_STREAM:
return (-1);
}
return (-1);
}
break;
default:
/* Should not happen... */
errno = EPROTOTYPE;
return (-1);
}
return (bytes);
}
static ssize_t
{
int l, offset;
#ifdef DEBUG
{
printf("sendto(%d): msg of length: %d sent to port %d and host: %s\n",
}
#endif /* DEBUG */
/* calculate offset for data */
} else
} else {
sizeof (struct sockaddr_in));
}
/* Get a legal source port if the socket isn't bound. */
}
/* Round up to 16bit value for checksum purposes */
~(sizeof (uint16_t) - 1));
} else
return (-1);
}
for (l = TRANSPORT_LVL; l >= MEDIA_LVL; l--) {
if (errno == 0)
return (-1);
}
}
}
return (len);
}
/* ARGSUSED */
static ssize_t
{
int cnt;
/*
* Call directly TCP's send routine. We do this because TCP
* needs to decide whether to send out the data.
*
* Note also that currently, TCP ignores all flags passed in for
* TCP socket.
*/
return (-1);
} else {
return (cnt);
}
}
/*
* Returns ptr to the last inetgram in the list, or null if list is null
*/
struct inetgram *
{
return (wp);
}
return (NULL);
}
/*
* Adds an inetgram or list of inetgrams to the end of the list.
*/
void
{
return;
else {
}
}
/*
* Nuke a whole list of grams.
*/
void
{
}
/*
* Remove the referenced inetgram. List is altered accordingly. Destroy the
* referenced inetgram if freeit is B_TRUE.
*/
void
{
return;
/* detach wp from the list */
else
if (freeit) {
sizeof (struct inetgram));
}
break;
}
}
}
"bootp", NCT_BOOTP_DHCP,
"dhcp", NCT_BOOTP_DHCP,
"rarp", NCT_RARP_BOOTPARAMS,
"manual", NCT_MANUAL
};
/*
* Figure out from the bootpath what kind of network configuration strategy
* we should use. Returns the network config strategy.
*/
int
get_netconfig_strategy(void)
{
int i;
#if !defined(__i386)
/* sparc */
char lbootpath[OBP_MAXPATHLEN];
char net_options[NCT_BUFSIZE];
int proplen;
/* If the PROM DHCP cache exists, we're done */
if (prom_cached_reply(B_TRUE))
return (NCT_BOOTP_DHCP);
/*
* Newer (version 4) PROMs will put the name in the
* "net-config-strategy" property.
*/
sizeof (net_options)) {
} else {
/*
* We're reduced to sacanning bootpath for the prototol to use.
* Since there was no "net-config-strategy" property, this is
* initializations from bootpath[].
*/
*sp = '\0';
/* find the last '/' (in the device path) */
else
op++;
/* then look for the ':' separating it from the protocol */
op++;
if (*op == ':') {
*nop = '\0';
} else
net_options[0] = '\0';
}
#else
/* i86 */
char net_options[MAXNAMELEN];
/*
* Look at net-config-strategy boot property to determine what protocol
* will be used.
*/
sizeof (net_options), 0);
#endif /* __i386 */
for (i = 0; i < nct_entries; i++)
return (NCT_DEFAULT);
}
/* Modified STREAM routines for ease of porting core TCP code. */
/*ARGSUSED*/
mblk_t *
{
unsigned char *base;
return (NULL);
return (NULL);
return (mp);
}
void
{
#ifdef DEBUG
#endif
#ifdef DEBUG
#endif
}
void
{
while (mp) {
}
}
mblk_t *
{
unsigned char *ndp;
return (NULL);
return (nbp);
}
/* To simplify things, dupb() is implemented as copyb(). */
mblk_t *
{
}
/*
* get number of data bytes in message
*/
{
}
return (count);
}