/*
* 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
*/
/*
*/
/*
* Module for all network transactions. SLP messages can be multicast,
* unicast over UDP, or unicast over TCP; this module provides routines
* for all three. TCP transactions are handled by a single dedicated
* thread, while multicast and UDP unicast messages are sent by the
* calling thread.
*
* slp_uc_tcp_send: enqueues a message on the TCP transaction thread's
* queue.
* slp_tcp_wait: blocks until all TCP-enqueued transactions for
* a given SLP handle are complete
* slp_uc_udp_send: unicasts a message using a datagram
* slp_mc_send: multicasts a message
*/
/*
* todo: correct multicast interfaces;
*/
#include <stdio.h>
#include <stdlib.h>
#include <syslog.h>
#include <errno.h>
#include <unistd.h>
#include <time.h>
#include <string.h>
#include <slp-internal.h>
#include <slp_net_utils.h>
/*
* TCP thread particulars
*/
static int tcp_sockfd;
/* Used to pass arguments to the TCP thread, via 'tcp_q' */
struct tcp_rqst {
const char *scopes;
unsigned short xid;
};
/* Used to keep track of broadcast interfaces */
struct bc_ifs {
int num_ifs;
};
/*
* Private utility routines
*/
static SLPError start_tcp_thr();
static void tcp_thread();
struct msghdr *);
struct sockaddr_in *, char *,
int, struct bc_ifs *);
struct bc_ifs *);
const char *, char *, void **, unsigned long long,
unsigned long long, unsigned long long *,
int *, int *, int);
static void tcp_handoff(slp_handle_impl_t *, const char *,
struct sockaddr_in *, unsigned short);
static unsigned long long now_millis();
static int wait_for_response(unsigned long long, int *,
unsigned long long, unsigned long long *,
static void free_pr_node(void *, VISIT, int, void *);
/*
* Unicasts a message using TCP. 'target' is a targets list
* containing DAs corresponding to 'scopes'. 'free_target' directs
* tcp_thread to free the target list when finished; this is useful
* when a target needs to be synthesised by another message thread
* (such as slp_mc_send for tcp_handoffs). If this message is a
* retransmission due to a large reply, 'xid' should be the same as for
* the original message.
*
* This call returns as soon as the message has been enqueued on 'tcp_q'.
* Callers interested in knowing when the transaction has completed
* should call slp_tcp_wait with the same SLP handle.
*/
unsigned short xid) {
/* initialize TCP vars in handle, if necessary */
"out of memory");
return;
}
}
"out of memory");
return;
}
}
(hp->tcp_ref_cnt)++;
/* start TCP thread, if not already running */
if (!tcp_thr_running)
if (start_tcp_thr() != SLP_OK)
return;
/* create and enqueue the request */
return;
}
}
/*
* Wait for TCP to complete, if a transaction corresponding to this
* SLP handle is pending. If none are pending, returns immediately.
*/
while (hp->tcp_ref_cnt > 0)
}
/*
* Unicasts a message using datagrams. 'target' should contain a
* list of DAs corresponding to 'scopes'.
*
* This call does not return until the transaction has completed. It
* may handoff a message to the TCP thread if necessary, but will not
* wait for that transaction to complete. Hence callers should always
* invoke slp_tcp_wait before cleaning up resources.
*/
const char *scopes) {
int sockfd;
/* build the header and iovec */
return;
mtu = slp_get_mtu();
/* walk targets list until we either succeed or run out of targets */
char *state;
const char *timeouts;
int timeout;
/* make the socket, msghdr and reply buf */
"could not create socket: %s",
return;
}
"out of memory");
return;
}
/* timeout loop */
timeout != -1 &&
int pollerr;
continue; /* try again */
}
sent = now_millis();
if (pollerr == 0)
/* timeout */
continue;
if (pollerr < 0)
break;
/* only using one fd, so no need to scan pfd */
/* if reply overflows, hand off to TCP */
break;
}
"recvfrom failed: %s",
} else {
/* success -- but check error code */
switch (errcode) {
case SLP_MSG_PARSE_ERROR:
case SLP_VER_NOT_SUPPORTED:
case SLP_SICK_DA:
case SLP_DA_BUSY_NOW:
case SLP_RQST_NOT_SUPPORTED: {
/* drop it */
"DA %s returned error code %d; dropping reply",
}
}
}
break;
}
if (timeout != -1)
/* success or cancel */
break;
/* else failure */
}
/* failed all attempts or canceled by consumer */
return;
}
/* success or tcp handoff */
if (reply) {
if (slp_get_overflow(reply))
else
}
if (use_tcp)
}
/*
* Multicasts (or broadcasts) a message, using multicast convergance
* to collect results. Large replies will cause the message to be handed
* off to the TCP thread.
*
* This call does not return until the transaction is complete. It does
* not, however, wait until pending TCP transactions are complete, so
* callers should always invoke slp_tcp_wait before cleaning up any
* resources.
*/
const char *timeouts;
/* build the header and iovec */
return;
return;
maxwait = slp_get_mcmaxwait();
/* set the final timeout */
now = now_millis();
/* timeout prep and loop */
noresults = anyresults = 0;
timeout != -1 &&
noresults < 2 &&
/* send msg */
continue; /* try again */
}
sent = now_millis();
/* receive results */
if (!anyresults)
noresults++;
anyresults = 0;
}
/* clean up PR list collator */
if (collator)
/* close all fds in pfd */
/* free broadcast addrs, if used */
}
/*
* Private net helper routines
*/
/*
* Starts the tcp_thread and allocates any necessary resources.
*/
int terr;
(void) mutex_lock(&start_lock);
/* make sure someone else hasn't already intialized the thread */
if (tcp_thr_running) {
(void) mutex_unlock(&start_lock);
return (SLP_OK);
}
/* create the tcp queue */
(void) mutex_unlock(&start_lock);
return (err);
}
/* start the tcp thread */
(void) mutex_unlock(&start_lock);
return (SLP_INTERNAL_SYSTEM_ERROR);
}
(void) mutex_unlock(&start_lock);
return (SLP_OK);
}
/*
* Called by the tcp thread to shut itself down. The queue must be
* empty (and should be, since the tcp thread will only shut itself
* down if nothing has been put in its queue for the timeout period).
*/
static void end_tcp_thr() {
(void) mutex_lock(&start_lock);
(void) mutex_unlock(&start_lock);
}
/*
* The thread of control for the TCP thread. This sits in a loop, waiting
* on 'tcp_q' for new messages. If no message appear after 30 seconds,
* this thread cleans up resources and shuts itself down.
*/
static void tcp_thread() {
for (;;) {
const char *scopes;
unsigned short xid;
/* set idle shutdown timeout */
/* get the next request from the tcp queue */
if (!etimed)
continue;
else
end_tcp_thr();
}
/* Check if this handle has been cancelled */
goto transaction_complete;
/* build the header and iovec */
continue;
}
if (xid)
/* walk targets list until we either succeed or run out of targets */
/* create the socket */
< 0) {
"could not create socket: %s",
break;
}
/* connect to target */
sizeof (*sin)) < 0) {
"could not connect, error = %s",
goto failed;
}
/* send the message and read the reply */
== -1) {
"could not send, error = %s",
goto failed;
}
/* if success, break out of failover loop */
(void) close(tcp_sockfd);
break;
}
/* else if timed out, mark target failed and try next one */
(void) close(tcp_sockfd);
}
if (reply) {
}
} else if (ctarg) {
/* success */
}
/* If all TCP transactions on this handle are complete, send notice */
if (--(hp->tcp_ref_cnt) == 0)
if (free_target)
}
}
/*
* Performs a full read for TCP replies, dynamically allocating a
* buffer large enough to hold the reply.
*/
unsigned int len;
/* find out how long the reply is */
nleft = 5;
p = lenbuf;
while (nleft != 0) {
nread = 0;
else
return (SLP_NETWORK_ERROR);
} else if (nread == 0)
/* shouldn't hit EOF here */
return (SLP_NETWORK_ERROR);
p += nread;
}
/* allocate space for the reply, and copy in what we've already read */
/* This buffer gets freed by a msg-specific unpacking routine later */
return (SLP_MEMORY_ALLOC_FAILED);
}
/* read the rest of the message */
p = *reply + 5;
while (nleft != 0) {
nread = 0;
else {
return (SLP_NETWORK_ERROR);
}
} else if (nread == 0)
/*
* shouldn't hit EOF here, but perhaps we've
* gotten something useful, so return OK.
*/
return (SLP_OK);
p += nread;
}
return (SLP_OK);
}
/*
* Lays in a SLP header for this message into the scatter / gather
* array 'iov'. 'header' is the buffer used to contain the header,
* and must contain enough space. 'scopes' should contain a string
* with the scopes to be used for this message.
*/
const char *scopes) {
int i;
mtu = slp_get_mtu();
/* use the remaining buffer in header for the prlist */
}
off = 0;
return (err);
/* start out with empty prlist */
/* store the scope string len into the space provided by the caller */
off = 0;
return (err);
}
return (SLP_OK);
}
/*
* Populates a struct msghdr suitable for use with sendmsg.
*/
msg->msg_accrightslen = 0;
}
/*
* Sets the address on 'sin', sets the flag in the message header,
* and creates an array of pollfds for all interfaces we need to
* use. If we need to use only broadcast, and net.slp.interfaces
* is set, fills bcifs with an array of subnet broadcast addresses
* to which we should send. Returns err != SLP_OK only on catastrophic
* error.
*/
char *ifs_string;
/* Get the desired multicast interfaces, if set */
if ((ifs_string = (char *)SLPGetProperty(
char *p, *tstate;
/* count the number of IFs given */
}
/* copy the given IFs into an array for easier processing */
"out of memory");
return (SLP_MEMORY_ALLOC_FAILED);
}
i = 0;
/* strtok_r will destructively modify, so make a copy first */
"out of memory");
return (SLP_MEMORY_ALLOC_FAILED);
}
for (
p;
/* skip */
num_givenifs--;
continue;
}
i++;
}
*nfds = num_givenifs;
/* allocate a pollfd array for all interfaces */
"out of memory");
return (SLP_MEMORY_ALLOC_FAILED);
}
/* lay the given interfaces into the pollfd array */
for (i = 0; i < num_givenifs; i++) {
/* create a socket to bind to this interface */
"could not create socket: %s",
return (SLP_INTERNAL_SYSTEM_ERROR);
}
/* fill in the pollfd structure */
if (use_broadcast) {
(void) memcpy(
/* bind fd to interface */
sizeof (*bcsin)) == 0) {
continue;
}
/* else fallthru to default (multicast) */
"could not set broadcast interface: %s",
}
/* else use multicast */
< 0) {
"could not set multicast interface: %s",
continue;
}
}
if (use_broadcast) {
if ((err = make_bc_target(
!= SLP_OK) {
if (err == SLP_MEMORY_ALLOC_FAILED) {
/* the only thing which is really a showstopper */
return (err);
}
/* else no valid interfaces */
}
}
}
if (!have_valid_if) {
if (*fds && !have_valid_if) {
/* couldn't process net.slp.interfaces property */
}
/* bind to default interface */
"out of memory");
return (SLP_MEMORY_ALLOC_FAILED);
}
"could not create socket: %s",
return (SLP_INTERNAL_SYSTEM_ERROR);
}
*nfds = 1;
}
/* set required options on all configured fds */
if (use_broadcast) {
"could not enable broadcast: %s",
}
} else {
"could not set multicast TTL: %s",
}
}
}
if (use_broadcast) {
} else {
}
return (SLP_OK);
}
/*
* Obtains the subnet broadcast address for each interface specified
* in net.slp.interfaces, and fill bcifs->sin with an array of these
* addresses.
*/
int i;
!= SLP_OK) {
return (err);
}
/* set SLP port on each sockaddr_in */
}
return (SLP_OK);
}
/*
* Sends msg on 1st fd in fds for multicast, or on all interfaces
* specified in net.slp.interfaces for broadcast. Returns SLP_OK if
* msg was sent successfully on at least one interface; otherwise
* returns SLP_NETWORK_ERROR if msg was not sent on any interfaces.
*/
if (slp_get_usebroadcast()) {
/* hand off to broadcast-specific send function */
return (SLP_OK);
}
/*
* else no ifs given, or bc_sendmsg failed, so send on
* general broadcast addr (255.255.255.255). This will
* cause the message to be sent on all interfaces. The
* address will have been set in make_mc_target.
*/
}
/*
* Send only on one interface -- let routing take care of
* sending the message everywhere it needs to go. Sending
* on more than one interface can cause nasty routing loops.
* Note that this approach doesn't work with partitioned
* networks.
*/
return (SLP_NETWORK_ERROR);
}
return (SLP_OK);
}
/*
* Send msg to each subnet broadcast address in bcifs->sin. Note
* that we can send on any fd (regardless of which interface to which
* it is bound), since the kernel will take care of routing for us.
* Returns err != SLP_OK only if no message was sent on any interface.
*/
int i;
continue;
}
}
}
/*
* This is where the bulk of the multicast convergance algorithm resides.
* mc_recvmsg() waits for data to be ready on any fd in pfd, iterates
* through pfd and reads data from ready fd's. It also checks timeouts
* and user-cancels.
*
* Parameters:
* pfd IN an array of pollfd structs containing fds to poll
* nfds IN number of elements in pfd
* hp IN SLPHandle from originating call
* scopes IN scopes to use for this message
* header IN the SLP message header for this message
* final_to IN final timeout
* sent IN time when message was sent
* noresults OUT set to 0 if any results are received
* anyresults OUT set to true if any results are received
* timeout IN time for this convergence iteration
*
* Returns only if an error has occured, or if either this retransmit
* timeout or the final timeout has expired, or if hp->cancel becomes true.
*/
unsigned long long final_to,
unsigned long long sent,
unsigned long long *now,
nfds_t i;
int pollerr;
/* wait until we can read something */
if (pollerr == 0)
/* timeout */
goto cleanup;
if (pollerr < 0)
/* error */
goto cleanup;
/* iterate through all fds to find one with data to read */
/* unused fd or unwanted event */
continue;
}
/* alloc reply buffer */
return;
}
/* if reply overflows, hand off to TCP */
continue;
}
/* else something nasty happened */
"recvfrom failed: %s",
continue;
} else {
/* success */
if (slp_get_overflow(reply)) {
}
/*
* Add to the PR list. If this responder has already
* answered, it doesn't count.
*/
*noresults = 0;
*anyresults = 1;
}
/* if we've exceeded maxwait, break out */
*now = now_millis();
goto cleanup;
} /* end successful receive */
} /* end fd iteration */
/* reset poll's timeout */
if (timeout <= 0) {
goto cleanup;
}
} /* end main poll loop */
if (reply) {
}
}
/*
* Closes any open sockets and frees the pollfd array.
*/
nfds_t i;
for (i = 0; i < nfds; i++) {
continue;
}
}
}
/*
* Hands off a message to the TCP thread, fabricating a new target
* from 'sin'. 'xid' will be used to create the XID for the TCP message.
*/
}
/*
* Returns the current time in milliseconds.
*/
static unsigned long long now_millis() {
unsigned long long i;
return (i);
}
/*
* A wrapper around poll which waits until a reply comes in. This will
* wait no longer than 'timeout' before returning. poll can return
* even if no data is on the pipe or timeout has occured, so the
* additional parameters are used to break out of the wait loop if
* we have exceeded the timeout value. 'final_to' is ignored if it is 0.
*
* returns: < 0 on error
* 0 on timeout
* > 0 on success (i.e. ready to read data).
* side effect: 'now' is set to the time when poll found data on the pipe.
*/
static int wait_for_response(
unsigned long long final_to,
int *timeout,
unsigned long long sent,
unsigned long long *now,
/* wait until we can read something */
for (;;) {
*now = now_millis();
/* ready to read */
if (pollerr > 0)
return (pollerr);
/* time out */
if (pollerr == 0)
/* timeout */
return (0);
/* error */
if (pollerr < 0)
/* poll is weird. */
if (
break;
continue;
} else {
"poll error: %s",
return (pollerr);
}
}
return (0);
}
/*
* Adds the cname of the host whose address is in 'sin' to this message's
* previous responder list. The message is contained in 'msg'.
* 'collator' contains the complete previous responder list, so that
* even if the PR list in the message overflows and must be truncated,
* the function can still correctly determine if we have heard from this
* host before.
*
* returns: 1 if this is the first time we've heard from this host
* 0 is this is a duplicate reply
*/
static int add2pr_list(
struct sockaddr_in *sin,
void **collator) {
unsigned short prlen;
/* Attempt to resolve the responder's IP address to its host name */
return (0);
res = slp_tsearch(
(int (*)(const void *, const void *)) strcasecmp);
/* duplicate */
"drop PR ignored by host: %s",
cname);
return (0);
}
/* new responder: add to the msg PR list if there is room */
mtu = slp_get_mtu();
return (1); /* no room */
/* else there is enough room */
*p = 0;
if (prlen) {
namelen++; /* add the ',' */
(void) strcat(p, ",");
}
/* update msg and pr list length */
off = 0;
return (1);
}
/*
* The iterator function used while traversing the previous responder
* tree. Just frees resources.
*/
/*ARGSUSED2*/
}
}