cvcd.c revision 2eaee53e5b3d4cd48a35cd651c0a8ae149d772c5
/*
* 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.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* This code implements the Starcat Virtual Console host daemon (see cvcd(1M)).
* It accepts one TCP connection at a time on a well-known port. Once a
* connection is accepted, the console redirection driver (cvcdredir(7D)) is
* opened, and console I/O is routed back and forth between the two file
* descriptors (network and redirection driver). Per-socket IPsec is used to
* secure the connection if it is enabled with the "-a", "-u" and or "-e"
* command line options.
*/
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <stropts.h>
#include <signal.h>
#include <syslog.h>
#include <locale.h>
#include <limits.h>
#include <sys/priocntl.h>
#include <sys/tspriocntl.h>
#include <sys/rtpriocntl.h>
#include <netdb.h>
#include <tiuser.h>
#include <sys/sc_cvcio.h>
/*
* Header files for per-socket IPsec
*/
/*
* The IPsec socket option struct, from ipsec(7P):
*
* typedef struct ipsec_req {
* uint_t ipsr_ah_req; AH request
* uint_t ipsr_esp_req; ESP request
* uint_t ipsr_self_encap_req; Self-Encap request
* uint8_t ipsr_auth_alg; Auth algs for AH
* uint8_t ipsr_esp_alg; Encr algs for ESP
* uint8_t ipsr_esp_auth_alg; Auth algs for ESP
* } ipsec_req_t;
*
* The -a option sets the ipsr_auth_alg field. Allowable arguments
* are "none", "md5", or "sha1". The -e option sets the ipsr_esp_alg
* field. Allowable arguments are "none", "des", or "3des". "none"
* is the default for both options. The -u option sets ipsr_esp_auth_alg.
* Allowable arguments are the same as -a.
*
* The arguments ("md5", "des", etc.) are named so that they match
* kmd(1m)'s accepted arguments which are listed on the SC in
*/
#define SELF_ENCAP_REQ 0x0
/*
* A type to hold the command line argument string used to select a
* particular authentication header (AH) or encapsulating security
* payload (ESP) algorithm and the ID used for that algorithm when
* filling the ipsec_req_t structure which is passed to
* setsockopt(3SOCKET).
*/
typedef struct cvcd_alg {
char *arg_name;
} cvcd_alg_t;
/*
* Misc. defines.
*/
#define NETWORK_PFD 0
#define REDIR_PFD 1
#define LISTEN_PFD 2
#define NUM_PFDS 3
/*
* Function prototypes
*/
static void cvcd_set_priority(void);
static void cvcd_do_network_console(void);
static void cvcd_usage(void);
/*
* Globals
*/
static char progname[MAXPATHLEN];
static int debug = 0;
/*
* Array of acceptable -a, -u and -e arguments.
*/
static cvcd_alg_t auth_algs_array[] = {
{ NULL, 0x0 }
}, esp_algs_array[] = {
{ NULL, 0x0 }
};
int
{
int err;
int opt;
int tport = 0;
char *hostname;
int fd;
int i;
char prefix[256];
#ifdef DEBUG
#else
#endif
switch (opt) {
case 'a' :
case 'u' :
if (opt == 'a')
else
break;
break;
#ifdef DEBUG
break;
break;
#endif /* DEBUG */
default : cvcd_usage();
exit(1);
}
}
perror("HOSTNAME not defined");
exit(1);
}
/*
* hostname may still be NULL, depends on when cvcd was started
* in the boot sequence. If it is NULL, try one more time
*/
/*
* we reuse the utsname.nodename buffer here! hostname
* already points to it.
*/
"failed to acquire hostname");
} else {
}
}
}
/*
* If all attempts to get the hostname have failed, put something
* meaningful in the buffer.
*/
}
/*
* Must be root.
*/
exit(1);
}
/*
* Daemonize...
*/
if (debug == 0) {
closefrom(0);
(void) chdir("/");
(void) umask(0);
if (fork() != 0) {
exit(0);
}
(void) setpgrp();
}
/*
* Initialize the array of pollfds used to track the listening socket,
* the connection to the console redirection driver, and the network
* connection.
*/
for (i = 0; i < NUM_PFDS; i++) {
}
/* SPR 94004 */
/*
* SPR 83644: cvc and kadb are not compatible under heavy loads.
* Fix: will give cvcd highest TS priority at execution time.
*/
/*
* If not already determined by a command-line flag, figure out which
* port we're supposed to be listening on.
*/
if (tport == 0) {
exit(1);
}
}
if (debug == 1) {
}
/*
* Attempt to initialize the socket we'll use to listen for incoming
* connections. No need to check the return value, as the call will
* exit if it fails.
*/
/*
* Now that we're all set up, we loop forever waiting for connections
* (one at a time) and then driving network console activity over them.
*/
for (;;) {
/*
* Start by waiting for an incoming connection.
*/
do {
if (err == -1) {
exit(1);
}
if ((err > 0) &&
exit(1);
}
}
} while (fd == -1);
/*
* We have a connection. Set the new socket nonblocking, and
* initialize the appropriate pollfd. In theory, the new socket
* is _already_ non-blocking because accept() is supposed to
* hand us a socket with the same properties as the socket we're
* listening on, but it won't hurt to make sure.
*/
opt = 1;
if (err == -1) {
continue;
}
/*
* Since we're ready to do network console stuff, go ahead and
* open the Network Console redirection driver, which will
* switch traffic from the IOSRAM path to the network path if
* the network path has been selected in cvc.
*/
if (fd == -1) {
exit(1);
}
/*
* We have a network connection and we have the redirection
* driver open, so drive the network console until something
* changes.
*/
/*
* cvcd_do_network_console doesn't return until there's a
* problem, so we need to close the network connection and the
* redirection driver and start the whole loop over again.
*/
}
/* NOTREACHED */
return (1);
}
/*
* cvcd_get_alg
*
* Returns the ID of the first algorithm found in
* the 'algs' array with a name matching 'arg'. If
* there is no matching algorithm, the function does
* not return. The 'algs' array must be terminated
* by an entry containing a NULL 'arg_name' field.
*/
static uint8_t
{
== 0) {
}
}
cvcd_usage();
exit(1);
/* NOTREACHED */
}
/*
* cvcd_set_priority
*
* DESCRIBE
* SPR 83644: cvc and kadb are not compatible under heavy loads.
* Fix: will give cvcd highest TS priority at execution time.
*/
static void
cvcd_set_priority(void)
{
short tsmaxpri;
/* Get scheduler properties for this PID */
return;
}
/* Get class ID and maximum priority for TS process class */
return;
}
/* Print priority info in debug mode */
if (debug) {
"PID: %d, current priority: %d, Max priority: %d.",
}
}
/* Change proc's priority to maxtspri */
}
/* Print new priority info in debug mode */
if (debug) {
-1L) {
} else {
}
}
}
/*
* cvcd_init_host_socket
*
* Given a TCP port number, create and initialize a socket appropriate for
* accepting incoming connections to that port.
*/
static int
{
int err;
int fd;
int optval;
/*
* Start by creating the socket, which needs to support IPv6.
*/
if (fd == -1) {
exit(1);
}
/*
* Set the SO_REUSEADDR option, and make the socket non-blocking.
*/
optval = 1;
if (err == -1) {
exit(1);
}
if (err == -1) {
exit(1);
}
/*
* Enable per-socket IPsec if the user specified an AH or ESP
* algorithm to use.
*/
esp_auth_alg != SADB_AALG_NONE) {
/* Hardcoded values */
/* User defined */
if (ah_auth_alg != SADB_AALG_NONE)
if (esp_encr_alg != SADB_EALG_NONE ||
esp_auth_alg != SADB_AALG_NONE) {
}
if (err == -1) {
exit(1);
}
}
/*
* Bind the socket to our local address and port.
*/
if (err == -1) {
exit(1);
}
/*
* Indicate that we want to accept connections on this socket. Since we
* only allow one connection at a time anyway, specify a maximum backlog
* of 1.
*/
if (err == -1) {
exit(1);
}
return (fd);
}
/*
* cvcd_do_network_console
*
* With established connections to the network and the redirection driver,
* shuttle data between the two until something goes wrong.
*/
static void
cvcd_do_network_console(void)
{
int i;
int err;
int count;
short revents;
int input_len = 0;
int output_len = 0;
int input_off = 0;
int output_off = 0;
char output_buf[MAXPKTSZ];
for (;;) {
/*
* Wait for activity on any of the open file descriptors, which
* includes the ability to write data if we have any to write.
* If poll() fails, break out of the network console processing
* loop.
*/
if (output_len != 0) {
}
if (input_len != 0) {
}
if (err == -1) {
break;
}
/*
* If any errors or hangups were detected, or one of our file
* descriptors is bad, bail out of the network console
* processing loop.
*/
for (i = 0; i < NUM_PFDS; i++) {
"poll: status on %s fd:%s%s%s",
((i == LISTEN_PFD) ? "listen" :
goto fail; /* 'break' wouldn't work here */
}
}
/*
* Start by rejecting any connection attempts, since we only
* allow one network connection at a time.
*/
int fd;
if (fd > 0) {
}
}
/*
* If we have data waiting to be written in one direction or the
* other, go ahead and try to send the data on its way. We're
* going to attempt the writes regardless of whether the poll
* indicated that the destinations are ready, because we want to
* find out if either descriptor has a problem (e.g. broken
* network link).
* If an "unexpected" error is detected, give up and break out
* of the network console processing loop.
*/
if (output_len != 0) {
break;
} else if (count > 0) {
output_len -= count;
if (output_len == 0) {
output_off = 0;
} else {
output_off += count;
}
}
}
if (input_len != 0) {
break;
} else if (count > 0) {
if (input_len == 0) {
input_off = 0;
} else {
}
}
}
/*
* Finally, take a look at each data source and, if there isn't
* any residual data from that source still waiting to be
* processed, see if more data can be read. We don't want to
* read more data from a source if we haven't finished
* processing the last data we read from it because doing so
* would maximize the amount of data lost if the network console
* failed or was closed.
* If an "unexpected" error is detected, give up and break out
* of the network console processing loop.
* The call to read() appears to be in the habit of returning 0
* when you've read all of the data from a stream that has been
* hung up, and poll apparently feels that that condition
* justifies setting POLLIN, so we're going to treat 0 as an
* error return from read().
*/
if (count <= 0) {
/*
* Reading 0 simply means there is no data
* available, since this is a terminal.
*/
break;
}
} else {
output_len = count;
output_off = 0;
}
}
if (count <= 0) {
/*
* Reading 0 here implies a hangup, since this
* is a non-blocking socket that poll() reported
* as having data available. This will
* typically occur when the console user drops
* to OBP or intentially switches to IOSRAM
* mode.
*/
if (count == 0) {
"read(network): hangup detected");
break;
break;
}
} else {
input_off = 0;
}
}
} /* End forever loop */
/*
* If we get here, something bad happened during an attempt to access
* either the redirection driver or the network connection. There
* doesn't appear to be any way to avoid the possibility of losing
* the loss if it happens.
* XXX - We could do more, but is it worth the effort? Logging the
* lost data would be pretty easy... actually preserving it
* in the console flow would be a lot harder. We're more robust
* than the previous generation at this point, at least, so
* perhaps that's enough for now?
*/
fail:
if (input_len != 0) {
}
if (output_len != 0) {
}
}
static void
{
#if defined(DEBUG)
(void) printf("%s [-d] [-p port] "
"[-a none|md5|sha1] [-e none|des|3des] [-u none|md5|sha1]\n",
progname);
#else
(void) printf("%s [-a none|md5|sha1] [-e none|des|3des] "
"[-u none|md5|sha1]\n", progname);
#endif /* DEBUG */
}
/*
* cvcd_err ()
*
* Description:
* Log messages via syslog daemon.
*
* Input:
* code - logging code
* format - messages to log
*
* Output:
* void
*
*/
static void
{
if (debug == 0) {
} else {
}
}