xref: /onnv-gate/usr/src/cmd/fm/modules/common/ip-transport/ip.c (revision 13093:48f2dbca79a2)
11193Smws /*
21193Smws  * CDDL HEADER START
31193Smws  *
41193Smws  * The contents of this file are subject to the terms of the
51570Scindi  * Common Development and Distribution License (the "License").
61570Scindi  * You may not use this file except in compliance with the License.
71193Smws  *
81193Smws  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91193Smws  * or http://www.opensolaris.org/os/licensing.
101193Smws  * See the License for the specific language governing permissions
111193Smws  * and limitations under the License.
121193Smws  *
131193Smws  * When distributing Covered Code, include this CDDL HEADER in each
141193Smws  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151193Smws  * If applicable, add the following below this CDDL HEADER, with the
161193Smws  * fields enclosed by brackets "[]" replaced with your own identifying
171193Smws  * information: Portions Copyright [yyyy] [name of copyright owner]
181193Smws  *
191193Smws  * CDDL HEADER END
201193Smws  */
211193Smws 
221193Smws /*
2312142SJames.Kremer@Sun.COM  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
241193Smws  */
251193Smws 
261193Smws #include <sys/types.h>
271193Smws #include <sys/socket.h>
281193Smws #include <sys/sysmacros.h>
291193Smws #include <sys/fm/protocol.h>
301193Smws 
311193Smws #include <netinet/in.h>
321193Smws #include <arpa/inet.h>
331193Smws 
341193Smws #include <strings.h>
351193Smws #include <unistd.h>
361193Smws #include <pthread.h>
371193Smws #include <fcntl.h>
381193Smws #include <errno.h>
391193Smws #include <netdb.h>
401193Smws #include <poll.h>
4112142SJames.Kremer@Sun.COM #include <stdarg.h>
421193Smws 
431193Smws #include <fm/fmd_api.h>
441193Smws 
451193Smws #define	IP_MAGIC	"\177FMA" /* magic string identifying a packet header */
461193Smws #define	IP_MAGLEN	4	/* length of magic string */
4712142SJames.Kremer@Sun.COM #define	IP_DEBUG_OFF	0	/* No informational debugging printed */
4812142SJames.Kremer@Sun.COM #define	IP_DEBUG_FINE	1	/* Basic debug information printed (default) */
4912142SJames.Kremer@Sun.COM #define	IP_DEBUG_FINER	2	/* More debug information printed. */
5012142SJames.Kremer@Sun.COM #define	IP_DEBUG_FINEST	3	/* All debug information printed */
511193Smws 
521193Smws typedef struct ip_hdr {
531193Smws 	char iph_magic[IP_MAGLEN]; /* magic string */
541193Smws 	uint32_t iph_size;	/* packed size */
551193Smws } ip_hdr_t;
561193Smws 
571193Smws typedef struct ip_buf {
581193Smws 	void *ipb_buf;		/* data buffer */
591193Smws 	size_t ipb_size;	/* size of buffer */
601193Smws } ip_buf_t;
611193Smws 
6212581SJames.Kremer@Sun.COM typedef struct ip_cinfo {	    /* Connection specific information */
6312581SJames.Kremer@Sun.COM 	struct addrinfo *ipc_addr;  /* Connection address(es) */
6412581SJames.Kremer@Sun.COM 	char *ipc_name;		    /* The name of the server or interface */
6512581SJames.Kremer@Sun.COM 	int ipc_retry;		    /* The number of connection retries */
6612581SJames.Kremer@Sun.COM 	boolean_t ipc_accept;	    /* Will connection accept clients */
6712581SJames.Kremer@Sun.COM 	id_t ipc_timer;		    /* FMD timer id for connection */
6812581SJames.Kremer@Sun.COM 	struct ip_cinfo *ipc_next;  /* Next conneciton in list */
6912142SJames.Kremer@Sun.COM } ip_cinfo_t;
7012142SJames.Kremer@Sun.COM 
711193Smws typedef struct ip_xprt {
721193Smws 	fmd_xprt_t *ipx_xprt;	/* transport handle */
731193Smws 	int ipx_flags;		/* transport flags */
741193Smws 	int ipx_fd;		/* socket file descriptor */
751193Smws 	int ipx_done;		/* flag indicating connection closed */
761193Smws 	pthread_t ipx_tid;	/* recv-side auxiliary thread */
771193Smws 	ip_buf_t ipx_sndbuf;	/* buffer for sending events */
781193Smws 	ip_buf_t ipx_rcvbuf;	/* buffer for receiving events */
7912142SJames.Kremer@Sun.COM 	ip_cinfo_t *ipx_cinfo;	/* info for reconnect */
8012581SJames.Kremer@Sun.COM 	id_t ipx_spnd_timer;	/* connection suspend timer */
8112142SJames.Kremer@Sun.COM 	char *ipx_addr;		/* address:port of remote connection */
8212142SJames.Kremer@Sun.COM 	struct ip_xprt *ipx_next;	/* next ip_xprt in global list */
831193Smws } ip_xprt_t;
841193Smws 
8512581SJames.Kremer@Sun.COM #define	IPX_ID(a) ((a)->ipx_addr == NULL ? "(Not connected)" : (a)->ipx_addr)
8612142SJames.Kremer@Sun.COM 
871193Smws typedef struct ip_stat {
881193Smws 	fmd_stat_t ips_accfail;	/* failed accepts */
891193Smws 	fmd_stat_t ips_badmagic; /* invalid packet headers */
901193Smws 	fmd_stat_t ips_packfail; /* failed packs */
911193Smws 	fmd_stat_t ips_unpackfail; /* failed unpacks */
921193Smws } ip_stat_t;
931193Smws 
9412142SJames.Kremer@Sun.COM static void ip_xprt_create(fmd_xprt_t *, int, int, ip_cinfo_t *, char *);
951193Smws static void ip_xprt_destroy(ip_xprt_t *);
961193Smws 
971193Smws static ip_stat_t ip_stat = {
981193Smws 	{ "accfail", FMD_TYPE_UINT64, "failed accepts" },
991193Smws 	{ "badmagic", FMD_TYPE_UINT64, "invalid packet headers" },
1001193Smws 	{ "packfail", FMD_TYPE_UINT64, "failed packs" },
1011193Smws 	{ "unpackfail", FMD_TYPE_UINT64, "failed unpacks" },
1021193Smws };
1031193Smws 
1041193Smws static fmd_hdl_t *ip_hdl;	/* module handle */
1051193Smws static pthread_mutex_t ip_lock;	/* lock for ip_xps list */
1061193Smws static ip_xprt_t *ip_xps;	/* list of active transports */
10712581SJames.Kremer@Sun.COM static pthread_mutex_t ip_conns_lock;	/* lock for ip_conns list */
10812581SJames.Kremer@Sun.COM static ip_cinfo_t *ip_conns;	/* list of all configured connection info */
1091193Smws static nvlist_t *ip_auth;	/* authority to use for transport(s) */
1101193Smws static size_t ip_size;		/* default buffer size */
1111193Smws static volatile int ip_quit;	/* signal to quit */
1121193Smws static int ip_qlen;		/* queue length for listen(3SOCKET) */
1131193Smws static int ip_mtbf;		/* mtbf for simulating packet drop */
1149120SStephen.Hanson@Sun.COM static int ip_external;		/* set transport to be "external" */
1159120SStephen.Hanson@Sun.COM static int ip_no_remote_repair;	/* disallow remote repair */
1169120SStephen.Hanson@Sun.COM static int ip_hconly;		/* only cache faults that are hc-scheme */
1179120SStephen.Hanson@Sun.COM static int ip_rdonly;		/* force transport to be rdonly */
1189120SStephen.Hanson@Sun.COM static int ip_hc_present_only;	/* only cache faults if hc-scheme and present */
1199120SStephen.Hanson@Sun.COM static char *ip_domain_name;	/* set domain name for received list.suspects */
1201193Smws static hrtime_t ip_burp;	/* make mtbf slower by adding this much delay */
1211193Smws static int ip_translate;	/* call fmd_xprt_translate() before sending */
1221193Smws static char *ip_port;		/* port to connect to (or bind to if server) */
12312142SJames.Kremer@Sun.COM static int ip_retry;		/* retry count for ip_xprt_setup() -1=forever */
1241193Smws static hrtime_t ip_sleep;	/* sleep delay for ip_xprt_setup() */
12512142SJames.Kremer@Sun.COM static int ip_debug_level;	/* level for printing debug messages */
12612142SJames.Kremer@Sun.COM 
12712142SJames.Kremer@Sun.COM /*
12812142SJames.Kremer@Sun.COM  * Prints a debug message to the fmd debug framework if the debug level is set
12912142SJames.Kremer@Sun.COM  * to at least the given level.
13012142SJames.Kremer@Sun.COM  */
13112142SJames.Kremer@Sun.COM static void
ip_debug(int level,char * fmt,...)13212142SJames.Kremer@Sun.COM ip_debug(int level, char *fmt, ...)
13312142SJames.Kremer@Sun.COM {
13412142SJames.Kremer@Sun.COM 	if (ip_debug_level >= level) {
13512142SJames.Kremer@Sun.COM 		va_list args;
13612142SJames.Kremer@Sun.COM 		va_start(args, fmt);
13712142SJames.Kremer@Sun.COM 		fmd_hdl_vdebug(ip_hdl, fmt, args);
13812142SJames.Kremer@Sun.COM 		va_end(args);
13912142SJames.Kremer@Sun.COM 	}
14012142SJames.Kremer@Sun.COM }
1411193Smws 
1421193Smws /*
1431193Smws  * Allocate space in ipx_sndbuf for a header and a packed XDR encoding of
1441193Smws  * the specified nvlist, and then send the buffer to our remote peer.
1451193Smws  */
1461193Smws static int
ip_fmdo_send(fmd_hdl_t * hdl,fmd_xprt_t * xp,fmd_event_t * ep,nvlist_t * nvl)14712307SJames.Kremer@Sun.COM ip_fmdo_send(fmd_hdl_t *hdl, fmd_xprt_t *xp, fmd_event_t *ep, nvlist_t *nvl)
1481193Smws {
14912307SJames.Kremer@Sun.COM 	ip_xprt_t *ipx;
1501193Smws 	size_t size, nvsize;
1511193Smws 	char *buf, *nvbuf;
1521193Smws 	ip_hdr_t *iph;
1531193Smws 	ssize_t r, n;
1541193Smws 	int err;
1551193Smws 
15612307SJames.Kremer@Sun.COM 	if (xp == NULL) {
15712307SJames.Kremer@Sun.COM 		ip_debug(IP_DEBUG_FINE, "ip_fmdo_send failed: xp=NULL\n");
15812307SJames.Kremer@Sun.COM 		return (FMD_SEND_FAILED);
15912307SJames.Kremer@Sun.COM 	}
16012307SJames.Kremer@Sun.COM 	ipx = fmd_xprt_getspecific(hdl, xp);
16112307SJames.Kremer@Sun.COM 
1621193Smws 	/*
1631193Smws 	 * For testing purposes, if ip_mtbf is non-zero, use this to pseudo-
1641193Smws 	 * randomly simulate the need for retries.  If ip_burp is also set,
1651193Smws 	 * then we also suspend the transport for a bit and wake it up again.
1661193Smws 	 */
1671193Smws 	if (ip_mtbf != 0 && gethrtime() % ip_mtbf == 0) {
1681193Smws 		if (ip_burp != 0) {
16912142SJames.Kremer@Sun.COM 			ip_debug(IP_DEBUG_FINE, "burping ipx %s", IPX_ID(ipx));
1701193Smws 			ipx->ipx_flags |= FMD_XPRT_SUSPENDED;
17112581SJames.Kremer@Sun.COM 			ipx->ipx_spnd_timer = fmd_timer_install(
17212581SJames.Kremer@Sun.COM 			    ip_hdl, ipx, NULL, ip_burp);
1731193Smws 			fmd_xprt_suspend(ip_hdl, xp);
1741193Smws 		}
1751193Smws 		return (FMD_SEND_RETRY);
1761193Smws 	}
1771193Smws 
1781193Smws 	if (ip_translate && (nvl = fmd_xprt_translate(hdl, xp, ep)) == NULL) {
1791193Smws 		fmd_hdl_error(hdl, "failed to translate event %p", (void *)ep);
1801193Smws 		return (FMD_SEND_FAILED);
1811193Smws 	}
1821193Smws 
1831193Smws 	(void) nvlist_size(nvl, &nvsize, NV_ENCODE_XDR);
1841193Smws 	size = r = sizeof (ip_hdr_t) + nvsize;
1851193Smws 
1861193Smws 	if (ipx->ipx_sndbuf.ipb_size < size) {
1871193Smws 		fmd_hdl_free(hdl, ipx->ipx_sndbuf.ipb_buf,
1881193Smws 		    ipx->ipx_sndbuf.ipb_size);
1891193Smws 		ipx->ipx_sndbuf.ipb_size = P2ROUNDUP(size, 16);
1901193Smws 		ipx->ipx_sndbuf.ipb_buf = fmd_hdl_alloc(hdl,
1911193Smws 		    ipx->ipx_sndbuf.ipb_size, FMD_SLEEP);
1921193Smws 	}
1931193Smws 
1941193Smws 	buf = ipx->ipx_sndbuf.ipb_buf;
1951193Smws 	iph = (ip_hdr_t *)(uintptr_t)buf;
1961193Smws 	nvbuf = buf + sizeof (ip_hdr_t);
1971193Smws 
1981193Smws 	bcopy(IP_MAGIC, iph->iph_magic, IP_MAGLEN);
1991193Smws 	iph->iph_size = htonl(nvsize);
2001193Smws 	err = nvlist_pack(nvl, &nvbuf, &nvsize, NV_ENCODE_XDR, 0);
2011193Smws 
2021193Smws 	if (ip_translate)
2031193Smws 		nvlist_free(nvl);
2041193Smws 
2051193Smws 	if (err != 0) {
2061193Smws 		fmd_hdl_error(ip_hdl, "failed to pack event for "
2071193Smws 		    "transport %p: %s\n", (void *)ipx->ipx_xprt, strerror(err));
2081193Smws 		ip_stat.ips_packfail.fmds_value.ui64++;
2091193Smws 		return (FMD_SEND_FAILED);
2101193Smws 	}
2111193Smws 
2121193Smws 	while (!ip_quit && r != 0) {
2131193Smws 		if ((n = send(ipx->ipx_fd, buf, r, 0)) < 0) {
2141193Smws 			if (errno != EINTR && errno != EWOULDBLOCK) {
21512142SJames.Kremer@Sun.COM 				ip_debug(IP_DEBUG_FINE,
21612142SJames.Kremer@Sun.COM 				    "failed to send to %s", IPX_ID(ipx));
2171193Smws 				return (FMD_SEND_FAILED);
2181193Smws 			}
2191193Smws 			continue;
2201193Smws 		}
2211193Smws 		buf += n;
2221193Smws 		r -= n;
2231193Smws 	}
2241193Smws 
22512142SJames.Kremer@Sun.COM 	ip_debug(IP_DEBUG_FINEST, "Sent event %d bytes to %s",
22612142SJames.Kremer@Sun.COM 	    size, IPX_ID(ipx));
2271193Smws 	return (FMD_SEND_SUCCESS);
2281193Smws }
2291193Smws 
2301193Smws /*
23112307SJames.Kremer@Sun.COM  * Sends events over transports that are configured read only.  When the module
23212307SJames.Kremer@Sun.COM  * is in read only mode it will receive all events and only send events that
23312307SJames.Kremer@Sun.COM  * have a subscription set.
23412307SJames.Kremer@Sun.COM  *
23512307SJames.Kremer@Sun.COM  * The configuration file will have to set prop ip_rdonly true and also
23612307SJames.Kremer@Sun.COM  * subscribe for events that are desired to be sent over the transport in order
23712307SJames.Kremer@Sun.COM  * for this function to be used.
23812307SJames.Kremer@Sun.COM  */
23912307SJames.Kremer@Sun.COM /* ARGSUSED */
24012307SJames.Kremer@Sun.COM static void
ip_fmdo_recv(fmd_hdl_t * hdl,fmd_event_t * ep,nvlist_t * nvl,const char * class)24112307SJames.Kremer@Sun.COM ip_fmdo_recv(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl, const char *class)
24212307SJames.Kremer@Sun.COM {
24312307SJames.Kremer@Sun.COM 	int err;
24412307SJames.Kremer@Sun.COM 	ip_xprt_t *ipx;
24512307SJames.Kremer@Sun.COM 
24612581SJames.Kremer@Sun.COM 	if (ip_rdonly && !ip_quit) {
24712307SJames.Kremer@Sun.COM 		(void) pthread_mutex_lock(&ip_lock);
24812307SJames.Kremer@Sun.COM 
24912307SJames.Kremer@Sun.COM 		for (ipx = ip_xps; ipx != NULL; ipx = ipx->ipx_next) {
25012307SJames.Kremer@Sun.COM 			err = ip_fmdo_send(hdl, ipx->ipx_xprt, ep, nvl);
25112307SJames.Kremer@Sun.COM 			while (FMD_SEND_RETRY == err) {
25212307SJames.Kremer@Sun.COM 				err = ip_fmdo_send(hdl, ipx->ipx_xprt, ep, nvl);
25312307SJames.Kremer@Sun.COM 			}
25412307SJames.Kremer@Sun.COM 		}
25512307SJames.Kremer@Sun.COM 		(void) pthread_mutex_unlock(&ip_lock);
25612307SJames.Kremer@Sun.COM 	}
25712307SJames.Kremer@Sun.COM }
25812307SJames.Kremer@Sun.COM 
25912307SJames.Kremer@Sun.COM /*
2601193Smws  * Receive a chunk of data of the specified size from our remote peer.  The
2611193Smws  * data is received into ipx_rcvbuf, and then a pointer to the buffer is
2621193Smws  * returned.  NOTE: The data is only valid until the next call to ip_xprt_recv.
2631193Smws  * If the connection breaks or ip_quit is set during receive, NULL is returned.
2641193Smws  */
2651193Smws static void *
ip_xprt_recv(ip_xprt_t * ipx,size_t size)2661193Smws ip_xprt_recv(ip_xprt_t *ipx, size_t size)
2671193Smws {
2681193Smws 	char *buf = ipx->ipx_rcvbuf.ipb_buf;
2691193Smws 	ssize_t n, r = size;
2701193Smws 
2711193Smws 	if (ipx->ipx_rcvbuf.ipb_size < size) {
2721193Smws 		fmd_hdl_free(ip_hdl, ipx->ipx_rcvbuf.ipb_buf,
2731193Smws 		    ipx->ipx_rcvbuf.ipb_size);
2741193Smws 		ipx->ipx_rcvbuf.ipb_size = P2ROUNDUP(size, 16);
2751193Smws 		ipx->ipx_rcvbuf.ipb_buf = buf = fmd_hdl_alloc(ip_hdl,
2761193Smws 		    ipx->ipx_rcvbuf.ipb_size, FMD_SLEEP);
2771193Smws 	}
2781193Smws 
2791193Smws 	while (!ip_quit && r != 0) {
2801193Smws 		if ((n = recv(ipx->ipx_fd, buf, r, MSG_WAITALL)) == 0) {
2811193Smws 			ipx->ipx_done++;
2821193Smws 			return (NULL);
2831193Smws 		}
2841193Smws 
2851193Smws 		if (n < 0) {
2861193Smws 			if (errno != EINTR && errno != EWOULDBLOCK) {
28712142SJames.Kremer@Sun.COM 				ip_debug(IP_DEBUG_FINE,
28812142SJames.Kremer@Sun.COM 				    "failed to recv on ipx %s", IPX_ID(ipx));
2891193Smws 			}
2901193Smws 			continue;
2911193Smws 		}
29212142SJames.Kremer@Sun.COM 		/* Reset retry counter after a successful connection */
29312142SJames.Kremer@Sun.COM 		if (ipx->ipx_cinfo) {
29412581SJames.Kremer@Sun.COM 			ipx->ipx_cinfo->ipc_retry = ip_retry;
29512142SJames.Kremer@Sun.COM 		}
2961193Smws 
2971193Smws 		buf += n;
2981193Smws 		r -= n;
2991193Smws 	}
3001193Smws 
3011193Smws 	return (r ? NULL: ipx->ipx_rcvbuf.ipb_buf);
3021193Smws }
3031193Smws 
30412142SJames.Kremer@Sun.COM /*
30512142SJames.Kremer@Sun.COM  * Sets the address/port of the remote connection in the connection info struct
30612142SJames.Kremer@Sun.COM  * This is called after a TCP session has been set up with a known remote
30712142SJames.Kremer@Sun.COM  * address (sap)
30812142SJames.Kremer@Sun.COM  */
30912142SJames.Kremer@Sun.COM static void
ip_xprt_set_addr(ip_xprt_t * ipx,const struct sockaddr * sap)31012142SJames.Kremer@Sun.COM ip_xprt_set_addr(ip_xprt_t *ipx, const struct sockaddr *sap)
3111193Smws {
3121193Smws 	const struct sockaddr_in6 *sin6 = (const void *)sap;
3131193Smws 	const struct sockaddr_in *sin = (const void *)sap;
3141193Smws 
3151193Smws 	char buf[INET6_ADDRSTRLEN + 16];
3161193Smws 	struct in_addr v4addr;
3171193Smws 	in_port_t port;
31812142SJames.Kremer@Sun.COM 	int n;
3191193Smws 
32012307SJames.Kremer@Sun.COM 	ip_debug(IP_DEBUG_FINER, "Enter ip_xprt_set_addr");
3211193Smws 
3221193Smws 	if (sap->sa_family == AF_INET6 &&
3231193Smws 	    IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
3241193Smws 		IN6_V4MAPPED_TO_INADDR(&sin6->sin6_addr, &v4addr);
3251193Smws 		(void) inet_ntop(AF_INET, &v4addr, buf, sizeof (buf));
3261193Smws 		port = ntohs(sin6->sin6_port);
3271193Smws 	} else if (sap->sa_family == AF_INET6) {
3281193Smws 		(void) inet_ntop(AF_INET6, &sin6->sin6_addr, buf, sizeof (buf));
3291193Smws 		port = ntohs(sin6->sin6_port);
3301193Smws 	} else {
3311193Smws 		(void) inet_ntop(AF_INET, &sin->sin_addr, buf, sizeof (buf));
3321193Smws 		port = ntohs(sin->sin_port);
3331193Smws 	}
3341193Smws 
3351193Smws 	n = strlen(buf);
3361193Smws 	(void) snprintf(buf + n, sizeof (buf) - n, ":%u", port);
33712142SJames.Kremer@Sun.COM 
33812142SJames.Kremer@Sun.COM 	if (ipx->ipx_addr)
33912142SJames.Kremer@Sun.COM 		fmd_hdl_strfree(ip_hdl, ipx->ipx_addr);
34012142SJames.Kremer@Sun.COM 	ipx->ipx_addr = fmd_hdl_strdup(ip_hdl, buf, FMD_SLEEP);
34112142SJames.Kremer@Sun.COM 	ip_debug(IP_DEBUG_FINE, "connection addr is %s on %p",
34212142SJames.Kremer@Sun.COM 	    ipx->ipx_addr, (void *)ipx);
34312142SJames.Kremer@Sun.COM }
34412142SJames.Kremer@Sun.COM 
34512142SJames.Kremer@Sun.COM static nvlist_t *
ip_xprt_auth(ip_xprt_t * ipx)34612142SJames.Kremer@Sun.COM ip_xprt_auth(ip_xprt_t *ipx)
34712142SJames.Kremer@Sun.COM {
34812142SJames.Kremer@Sun.COM 	nvlist_t *nvl;
34912142SJames.Kremer@Sun.COM 	int err;
35012142SJames.Kremer@Sun.COM 
35112142SJames.Kremer@Sun.COM 	ip_debug(IP_DEBUG_FINER, "Enter ip_xprt_auth");
35212142SJames.Kremer@Sun.COM 
35312142SJames.Kremer@Sun.COM 	if (ip_auth != NULL)
35412142SJames.Kremer@Sun.COM 		err = nvlist_dup(ip_auth, &nvl, 0);
35512142SJames.Kremer@Sun.COM 	else
35612142SJames.Kremer@Sun.COM 		err = nvlist_alloc(&nvl, 0, 0);
35712142SJames.Kremer@Sun.COM 
35812142SJames.Kremer@Sun.COM 	if (err != 0) {
35912142SJames.Kremer@Sun.COM 		fmd_hdl_abort(ip_hdl, "failed to create nvlist for "
36012142SJames.Kremer@Sun.COM 		    "authority: %s\n", strerror(err));
36112142SJames.Kremer@Sun.COM 	}
36212142SJames.Kremer@Sun.COM 
36312142SJames.Kremer@Sun.COM 	if (ip_auth != NULL)
36412142SJames.Kremer@Sun.COM 		return (nvl);
36512142SJames.Kremer@Sun.COM 
36612142SJames.Kremer@Sun.COM 	ip_debug(IP_DEBUG_FINE, "ip_authority %s=%s\n",
36712142SJames.Kremer@Sun.COM 	    FM_FMRI_AUTH_SERVER, ipx->ipx_addr);
3681193Smws 
3691193Smws 	(void) nvlist_add_uint8(nvl, FM_VERSION, FM_FMRI_AUTH_VERSION);
37012142SJames.Kremer@Sun.COM 	(void) nvlist_add_string(nvl, FM_FMRI_AUTH_SERVER, ipx->ipx_addr);
3711193Smws 
3721193Smws 	return (nvl);
3731193Smws }
3741193Smws 
3751193Smws static void
ip_xprt_accept(ip_xprt_t * ipx)3761193Smws ip_xprt_accept(ip_xprt_t *ipx)
3771193Smws {
3781570Scindi 	struct sockaddr_storage sa;
3791193Smws 	socklen_t salen = sizeof (sa);
3801193Smws 	fmd_xprt_t *xp;
3811193Smws 	int fd;
3821193Smws 
38312142SJames.Kremer@Sun.COM 	ip_debug(IP_DEBUG_FINER, "Enter ip_xprt_accept");
38412142SJames.Kremer@Sun.COM 
3851570Scindi 	if ((fd = accept(ipx->ipx_fd, (struct sockaddr *)&sa, &salen)) == -1) {
3861193Smws 		fmd_hdl_error(ip_hdl, "failed to accept connection");
3871193Smws 		ip_stat.ips_accfail.fmds_value.ui64++;
3881193Smws 		return;
3891193Smws 	}
39012142SJames.Kremer@Sun.COM 	ip_debug(IP_DEBUG_FINE, "Accepted socket on fd %d", fd);
3911193Smws 
39212142SJames.Kremer@Sun.COM 	ip_xprt_set_addr(ipx, (struct sockaddr *)&sa);
3931570Scindi 	xp = fmd_xprt_open(ip_hdl, ipx->ipx_flags,
39412142SJames.Kremer@Sun.COM 	    ip_xprt_auth(ipx), NULL);
39512581SJames.Kremer@Sun.COM 	ip_xprt_create(xp, fd, ipx->ipx_flags, ipx->ipx_cinfo, ipx->ipx_addr);
3961193Smws }
3971193Smws 
3981193Smws static void
ip_xprt_recv_event(ip_xprt_t * ipx)3991193Smws ip_xprt_recv_event(ip_xprt_t *ipx)
4001193Smws {
4011193Smws 	ip_hdr_t *iph;
4021193Smws 	nvlist_t *nvl;
4031193Smws 	size_t size;
4041193Smws 	void *buf;
4051193Smws 	int err;
4061193Smws 
4071193Smws 	if ((iph = ip_xprt_recv(ipx, sizeof (ip_hdr_t))) == NULL)
4081193Smws 		return; /* connection broken */
4091193Smws 
4101193Smws 	if (bcmp(iph->iph_magic, IP_MAGIC, IP_MAGLEN) != 0) {
4111193Smws 		fmd_hdl_error(ip_hdl,
41212142SJames.Kremer@Sun.COM 		    "invalid hdr magic %x.%x.%x.%x from transport %s\n",
4131193Smws 		    iph->iph_magic[0], iph->iph_magic[1], iph->iph_magic[2],
41412142SJames.Kremer@Sun.COM 		    iph->iph_magic[3], IPX_ID(ipx));
4151193Smws 		ip_stat.ips_badmagic.fmds_value.ui64++;
4161193Smws 		return;
4171193Smws 	}
4181193Smws 
4191193Smws 	size = ntohl(iph->iph_size);
4201193Smws 
4211193Smws 	if ((buf = ip_xprt_recv(ipx, size)) == NULL)
4221193Smws 		return; /* connection broken */
4231193Smws 
4241193Smws 	if ((err = nvlist_unpack(buf, size, &nvl, 0)) != 0) {
4251193Smws 		fmd_hdl_error(ip_hdl, "failed to unpack event from "
42612142SJames.Kremer@Sun.COM 		    "transport %s: %s\n",
42712142SJames.Kremer@Sun.COM 		    IPX_ID(ipx), strerror(err));
4281193Smws 		ip_stat.ips_unpackfail.fmds_value.ui64++;
4299120SStephen.Hanson@Sun.COM 	} else {
4309120SStephen.Hanson@Sun.COM 		if (ip_domain_name)
4319120SStephen.Hanson@Sun.COM 			fmd_xprt_add_domain(ip_hdl, nvl, ip_domain_name);
4321193Smws 		fmd_xprt_post(ip_hdl, ipx->ipx_xprt, nvl, 0);
4339120SStephen.Hanson@Sun.COM 	}
4341193Smws 
4351193Smws 	if (fmd_xprt_error(ip_hdl, ipx->ipx_xprt)) {
4361193Smws 		fmd_hdl_error(ip_hdl, "protocol error on transport %p",
4371193Smws 		    (void *)ipx->ipx_xprt);
4381193Smws 		ipx->ipx_done++;
4391193Smws 	}
44012142SJames.Kremer@Sun.COM 	ip_debug(IP_DEBUG_FINEST, "Recv event %d bytes from %s",
44112142SJames.Kremer@Sun.COM 	    size, IPX_ID(ipx));
4421193Smws }
4431193Smws 
4441193Smws static void
ip_xprt_thread(void * arg)4451193Smws ip_xprt_thread(void *arg)
4461193Smws {
4471193Smws 	ip_xprt_t *ipx = arg;
4481570Scindi 	struct sockaddr_storage sa;
4491193Smws 	socklen_t salen = sizeof (sa);
4501193Smws 	struct pollfd pfd;
4511193Smws 
45212142SJames.Kremer@Sun.COM 	ip_debug(IP_DEBUG_FINER, "Enter ip_xprt_thread");
45312142SJames.Kremer@Sun.COM 
4541193Smws 	while (!ip_quit && !ipx->ipx_done) {
4551193Smws 		if (ipx->ipx_xprt != NULL || (ipx->ipx_flags & FMD_XPRT_ACCEPT))
4561193Smws 			pfd.events = POLLIN;
4571193Smws 		else
4581193Smws 			pfd.events = POLLOUT;
4591193Smws 
4601193Smws 		pfd.fd = ipx->ipx_fd;
4611193Smws 		pfd.revents = 0;
4621193Smws 
4631193Smws 		if (poll(&pfd, 1, -1) <= 0)
4641193Smws 			continue; /* loop around and check ip_quit */
4651193Smws 
4661193Smws 		if (pfd.revents & (POLLHUP | POLLERR)) {
46712142SJames.Kremer@Sun.COM 			ip_debug(IP_DEBUG_FINE, "hangup fd %d\n", ipx->ipx_fd);
4681193Smws 			break;
4691193Smws 		}
4701193Smws 
4711193Smws 		if (pfd.revents & POLLOUT) {
4721193Smws 			/*
4731193Smws 			 * Once we're connected, there's no reason to have our
4741193Smws 			 * calls to recv() and send() be non-blocking since we
4751193Smws 			 * we have separate threads for each: clear O_NONBLOCK.
4761193Smws 			 */
4771193Smws 			(void) fcntl(ipx->ipx_fd, F_SETFL,
4781193Smws 			    fcntl(ipx->ipx_fd, F_GETFL, 0) & ~O_NONBLOCK);
4791193Smws 
4801570Scindi 			if (getpeername(ipx->ipx_fd, (struct sockaddr *)&sa,
4811570Scindi 			    &salen) != 0) {
48212581SJames.Kremer@Sun.COM 				ip_debug(IP_DEBUG_FINE,
48312581SJames.Kremer@Sun.COM 				    "Not connected, no remote name for fd %d. "
48412581SJames.Kremer@Sun.COM 				    " Will retry.",
48512581SJames.Kremer@Sun.COM 				    ipx->ipx_fd);
4861193Smws 				bzero(&sa, sizeof (sa));
48712307SJames.Kremer@Sun.COM 				break;
4881193Smws 			}
48912142SJames.Kremer@Sun.COM 			ip_xprt_set_addr(ipx, (struct sockaddr *)&sa);
4901570Scindi 			ipx->ipx_xprt = fmd_xprt_open(ip_hdl, ipx->ipx_flags,
49112142SJames.Kremer@Sun.COM 			    ip_xprt_auth(ipx), ipx);
4921193Smws 
49312142SJames.Kremer@Sun.COM 			ip_debug(IP_DEBUG_FINE, "connect fd %d ipx %p",
49412142SJames.Kremer@Sun.COM 			    ipx->ipx_fd, (void *)ipx);
4951193Smws 			continue;
4961193Smws 		}
4971193Smws 
4981193Smws 		if (pfd.revents & POLLIN) {
4991193Smws 			if (ipx->ipx_xprt == NULL)
5001193Smws 				ip_xprt_accept(ipx);
5011193Smws 			else
5021193Smws 				ip_xprt_recv_event(ipx);
5031193Smws 		}
5041193Smws 	}
5051193Smws 
50612581SJames.Kremer@Sun.COM 	ipx->ipx_cinfo->ipc_timer = fmd_timer_install(ip_hdl, ipx, NULL, 0);
50712581SJames.Kremer@Sun.COM 	ip_debug(IP_DEBUG_FINE, "close fd %d (timer %d)", ipx->ipx_fd,
50812581SJames.Kremer@Sun.COM 	    (int)ipx->ipx_cinfo->ipc_timer);
5091193Smws }
5101193Smws 
5111193Smws static void
ip_xprt_create(fmd_xprt_t * xp,int fd,int flags,ip_cinfo_t * cinfo,char * addr)51212142SJames.Kremer@Sun.COM ip_xprt_create(fmd_xprt_t *xp, int fd, int flags, ip_cinfo_t *cinfo, char *addr)
5131193Smws {
5141193Smws 	ip_xprt_t *ipx = fmd_hdl_zalloc(ip_hdl, sizeof (ip_xprt_t), FMD_SLEEP);
5151193Smws 
51612142SJames.Kremer@Sun.COM 	ip_debug(IP_DEBUG_FINER, "Enter ip_xprt_create %p", (void *)ipx);
51712142SJames.Kremer@Sun.COM 
5181193Smws 	ipx->ipx_xprt = xp;
5191193Smws 	ipx->ipx_flags = flags;
5201193Smws 	ipx->ipx_fd = fd;
5211193Smws 	ipx->ipx_tid = fmd_thr_create(ip_hdl, ip_xprt_thread, ipx);
52212142SJames.Kremer@Sun.COM 	ipx->ipx_cinfo = cinfo;
52312142SJames.Kremer@Sun.COM 	ipx->ipx_addr = fmd_hdl_strdup(ip_hdl, addr, FMD_SLEEP);
5241193Smws 
5251193Smws 	if (ipx->ipx_xprt != NULL)
5261193Smws 		fmd_xprt_setspecific(ip_hdl, ipx->ipx_xprt, ipx);
5271193Smws 
5281193Smws 	(void) pthread_mutex_lock(&ip_lock);
5291193Smws 
5301193Smws 	ipx->ipx_next = ip_xps;
5311193Smws 	ip_xps = ipx;
5321193Smws 
5331193Smws 	(void) pthread_mutex_unlock(&ip_lock);
5341193Smws }
5351193Smws 
5361193Smws static void
ip_xprt_destroy(ip_xprt_t * ipx)5371193Smws ip_xprt_destroy(ip_xprt_t *ipx)
5381193Smws {
5391193Smws 	ip_xprt_t *ipp, **ppx = &ip_xps;
5401193Smws 
54112142SJames.Kremer@Sun.COM 	ip_debug(IP_DEBUG_FINER, "Enter ip_xprt_destory %s %p",
54212142SJames.Kremer@Sun.COM 	    IPX_ID(ipx), (void *)ipx);
54312142SJames.Kremer@Sun.COM 
5441193Smws 	(void) pthread_mutex_lock(&ip_lock);
5451193Smws 
5461193Smws 	for (ipp = *ppx; ipp != NULL; ipp = ipp->ipx_next) {
5471193Smws 		if (ipp != ipx)
5481193Smws 			ppx = &ipp->ipx_next;
5491193Smws 		else
5501193Smws 			break;
5511193Smws 	}
5521193Smws 
5531193Smws 	if (ipp != ipx) {
5541193Smws 		(void) pthread_mutex_unlock(&ip_lock);
5551193Smws 		fmd_hdl_abort(ip_hdl, "ipx %p not on xps list\n", (void *)ipx);
5561193Smws 	}
5571193Smws 
5581193Smws 	*ppx = ipx->ipx_next;
5591193Smws 	ipx->ipx_next = NULL;
5601193Smws 
5611193Smws 	(void) pthread_mutex_unlock(&ip_lock);
5621193Smws 
56312581SJames.Kremer@Sun.COM 	if (ipx->ipx_spnd_timer)
56412581SJames.Kremer@Sun.COM 		fmd_timer_remove(ip_hdl, ipx->ipx_spnd_timer);
56512581SJames.Kremer@Sun.COM 
5661193Smws 	fmd_thr_signal(ip_hdl, ipx->ipx_tid);
5671193Smws 	fmd_thr_destroy(ip_hdl, ipx->ipx_tid);
5681193Smws 
5691193Smws 	if (ipx->ipx_xprt != NULL)
5701193Smws 		fmd_xprt_close(ip_hdl, ipx->ipx_xprt);
5711193Smws 
5721193Smws 	fmd_hdl_free(ip_hdl, ipx->ipx_sndbuf.ipb_buf, ipx->ipx_sndbuf.ipb_size);
5731193Smws 	fmd_hdl_free(ip_hdl, ipx->ipx_rcvbuf.ipb_buf, ipx->ipx_rcvbuf.ipb_size);
5741193Smws 
5751193Smws 	(void) close(ipx->ipx_fd);
57612142SJames.Kremer@Sun.COM 	if (ipx->ipx_addr) {
57712142SJames.Kremer@Sun.COM 		fmd_hdl_strfree(ip_hdl, ipx->ipx_addr);
57812142SJames.Kremer@Sun.COM 		ipx->ipx_addr = NULL;
57912142SJames.Kremer@Sun.COM 	}
5801193Smws 	fmd_hdl_free(ip_hdl, ipx, sizeof (ip_xprt_t));
5811193Smws }
5821193Smws 
5831193Smws /*
58412142SJames.Kremer@Sun.COM  * Loop through the addresses in the connection info structure that were
58512142SJames.Kremer@Sun.COM  * created by getaddrinfo() in ip_setup_addr during initialization (_fmd_init)
5861193Smws  * and for each one attempt to create a socket and initialize it.  If we are
5871193Smws  * successful, return zero.  If we fail, we check ip_retry: if it is non-zero
5881193Smws  * we return the last errno and let our caller retry ip_xprt_setup() later.  If
5891193Smws  * ip_retry reaches zero, we call fmd_hdl_abort() with an appropriate message.
5901193Smws  */
5911193Smws static int
ip_xprt_setup(fmd_hdl_t * hdl,ip_cinfo_t * cinfo)59212142SJames.Kremer@Sun.COM ip_xprt_setup(fmd_hdl_t *hdl, ip_cinfo_t *cinfo)
5931193Smws {
5941193Smws 	int err, fd, oflags, xflags, optval = 1;
5951193Smws 	struct addrinfo *aip;
5961193Smws 	const char *s1, *s2;
59712581SJames.Kremer@Sun.COM 	struct addrinfo *ail = cinfo->ipc_addr;
59812142SJames.Kremer@Sun.COM 
59912581SJames.Kremer@Sun.COM 	ip_debug(IP_DEBUG_FINER, "Enter ip_xprt_setup %s\n",
60012581SJames.Kremer@Sun.COM 	    cinfo->ipc_name == NULL ? "localhost" : cinfo->ipc_name);
6011193Smws 
6029120SStephen.Hanson@Sun.COM 	/*
6039120SStephen.Hanson@Sun.COM 	 * Set up flags as specified in the .conf file. Note that these are
6049120SStephen.Hanson@Sun.COM 	 * mostly only used for testing purposes, allowing the transport to
6059120SStephen.Hanson@Sun.COM 	 * be set up in various modes.
6069120SStephen.Hanson@Sun.COM 	 */
60712581SJames.Kremer@Sun.COM 	xflags = (ip_rdonly == FMD_B_TRUE) ? FMD_XPRT_RDONLY : FMD_XPRT_RDWR;
60812581SJames.Kremer@Sun.COM 	if (cinfo->ipc_accept)
60912581SJames.Kremer@Sun.COM 		xflags |= FMD_XPRT_ACCEPT;
6109120SStephen.Hanson@Sun.COM 	if (ip_external == FMD_B_TRUE)
6119120SStephen.Hanson@Sun.COM 		xflags |= FMD_XPRT_EXTERNAL;
6129120SStephen.Hanson@Sun.COM 	if (ip_no_remote_repair == FMD_B_TRUE)
6139120SStephen.Hanson@Sun.COM 		xflags |= FMD_XPRT_NO_REMOTE_REPAIR;
6149120SStephen.Hanson@Sun.COM 	if (ip_hconly == FMD_B_TRUE)
6159120SStephen.Hanson@Sun.COM 		xflags |= FMD_XPRT_HCONLY;
6169120SStephen.Hanson@Sun.COM 	if (ip_hc_present_only == FMD_B_TRUE)
6179120SStephen.Hanson@Sun.COM 		xflags |= FMD_XPRT_HC_PRESENT_ONLY;
6181193Smws 
61912142SJames.Kremer@Sun.COM 	for (aip = ail; aip != NULL; aip = aip->ai_next) {
6201193Smws 		if (aip->ai_family != AF_INET && aip->ai_family != AF_INET6)
6211193Smws 			continue; /* ignore anything that isn't IPv4 or IPv6 */
6221193Smws 
6231193Smws 		if ((fd = socket(aip->ai_family,
6241193Smws 		    aip->ai_socktype, aip->ai_protocol)) == -1) {
6251193Smws 			err = errno;
6261193Smws 			continue;
6271193Smws 		}
6281193Smws 
6291193Smws 		oflags = fcntl(fd, F_GETFL, 0);
6301193Smws 		(void) fcntl(fd, F_SETFL, oflags | O_NONBLOCK);
6311193Smws 
6321193Smws 		if (xflags & FMD_XPRT_ACCEPT) {
6331193Smws 			err = setsockopt(fd, SOL_SOCKET,
6341193Smws 			    SO_REUSEADDR, &optval, sizeof (optval)) != 0 ||
6351193Smws 			    bind(fd, aip->ai_addr, aip->ai_addrlen) != 0 ||
6361193Smws 			    listen(fd, ip_qlen) != 0;
6371193Smws 		} else {
63812142SJames.Kremer@Sun.COM 			err = connect(fd, aip->ai_addr, aip->ai_addrlen);
63912142SJames.Kremer@Sun.COM 			if (err)
64012142SJames.Kremer@Sun.COM 				err = errno;
64112142SJames.Kremer@Sun.COM 			if (err == EINPROGRESS)
64212142SJames.Kremer@Sun.COM 				err = 0;
6431193Smws 		}
6441193Smws 
6451193Smws 		if (err == 0) {
64612142SJames.Kremer@Sun.COM 			ip_xprt_create(NULL, fd, xflags, cinfo, NULL);
64712142SJames.Kremer@Sun.COM 			ip_debug(IP_DEBUG_FINER, "Exit ip_xprt_setup");
6481193Smws 			return (0);
6491193Smws 		}
6501193Smws 
65112142SJames.Kremer@Sun.COM 		ip_debug(IP_DEBUG_FINE, "Error=%d errno=%d", err, errno);
65212142SJames.Kremer@Sun.COM 
6531193Smws 		err = errno;
6541193Smws 		(void) close(fd);
6551193Smws 	}
6561193Smws 
65712581SJames.Kremer@Sun.COM 	if (cinfo->ipc_name != NULL) {
6581193Smws 		s1 = "failed to connect to";
65912581SJames.Kremer@Sun.COM 		s2 = cinfo->ipc_name;
6601193Smws 	} else {
6611193Smws 		s1 = "failed to listen on";
6621193Smws 		s2 = ip_port;
6631193Smws 	}
6641193Smws 
66512581SJames.Kremer@Sun.COM 	if (err == EACCES || cinfo->ipc_retry-- == 0)
6661193Smws 		fmd_hdl_abort(hdl, "%s %s: %s\n", s1, s2, strerror(err));
6671193Smws 
66812142SJames.Kremer@Sun.COM 	ip_debug(IP_DEBUG_FINE, "%s %s: %s (will retry)\n",
66912142SJames.Kremer@Sun.COM 	    s1, s2, strerror(err));
67012142SJames.Kremer@Sun.COM 	ip_debug(IP_DEBUG_FINER, "Exit ip_xprt_setup");
67112142SJames.Kremer@Sun.COM 	return (err);
67212142SJames.Kremer@Sun.COM }
67312142SJames.Kremer@Sun.COM 
67412142SJames.Kremer@Sun.COM /*
67512142SJames.Kremer@Sun.COM  * Free address based resources
67612142SJames.Kremer@Sun.COM  */
67712142SJames.Kremer@Sun.COM static void
ip_addr_cleanup()67812142SJames.Kremer@Sun.COM ip_addr_cleanup()
67912142SJames.Kremer@Sun.COM {
68012581SJames.Kremer@Sun.COM 	ip_cinfo_t *conn;
68112581SJames.Kremer@Sun.COM 
68212581SJames.Kremer@Sun.COM 	(void) pthread_mutex_lock(&ip_conns_lock);
68312581SJames.Kremer@Sun.COM 	conn = ip_conns;
68412581SJames.Kremer@Sun.COM 	while (conn != NULL) {
68512581SJames.Kremer@Sun.COM 		ip_conns = conn->ipc_next;
68612581SJames.Kremer@Sun.COM 		if (conn->ipc_addr != NULL)
68712581SJames.Kremer@Sun.COM 			freeaddrinfo(conn->ipc_addr);
68812581SJames.Kremer@Sun.COM 		conn->ipc_addr = NULL;
68912581SJames.Kremer@Sun.COM 		if (conn->ipc_timer)
69012581SJames.Kremer@Sun.COM 			fmd_timer_remove(ip_hdl, conn->ipc_timer);
69112581SJames.Kremer@Sun.COM 		fmd_hdl_strfree(ip_hdl, conn->ipc_name);
69212581SJames.Kremer@Sun.COM 		fmd_hdl_free(ip_hdl, conn, sizeof (ip_cinfo_t));
69312581SJames.Kremer@Sun.COM 		conn = ip_conns;
69412142SJames.Kremer@Sun.COM 	}
69512581SJames.Kremer@Sun.COM 	(void) pthread_mutex_unlock(&ip_conns_lock);
69612581SJames.Kremer@Sun.COM 
69712142SJames.Kremer@Sun.COM 	fmd_prop_free_string(ip_hdl, ip_port);
69812142SJames.Kremer@Sun.COM }
69912142SJames.Kremer@Sun.COM 
70012581SJames.Kremer@Sun.COM static boolean_t
ip_argis_cinfo(void * arg)70112581SJames.Kremer@Sun.COM ip_argis_cinfo(void *arg)
70212142SJames.Kremer@Sun.COM {
70312581SJames.Kremer@Sun.COM 	boolean_t exists = B_FALSE;
70412581SJames.Kremer@Sun.COM 	ip_cinfo_t *conn;
70512581SJames.Kremer@Sun.COM 
70612581SJames.Kremer@Sun.COM 	(void) pthread_mutex_lock(&ip_conns_lock);
70712581SJames.Kremer@Sun.COM 	for (conn = ip_conns; conn != NULL; conn = conn->ipc_next) {
70812581SJames.Kremer@Sun.COM 		if (conn == arg) {
70912581SJames.Kremer@Sun.COM 			exists = B_TRUE;
71012581SJames.Kremer@Sun.COM 			break;
71112581SJames.Kremer@Sun.COM 		}
71212581SJames.Kremer@Sun.COM 	}
71312581SJames.Kremer@Sun.COM 	(void) pthread_mutex_unlock(&ip_conns_lock);
71412581SJames.Kremer@Sun.COM 
71512581SJames.Kremer@Sun.COM 	return (exists);
71612581SJames.Kremer@Sun.COM }
71712581SJames.Kremer@Sun.COM 
71812581SJames.Kremer@Sun.COM 
71912581SJames.Kremer@Sun.COM static ip_cinfo_t *
ip_create_cinfo(char * server,boolean_t accept)72012581SJames.Kremer@Sun.COM ip_create_cinfo(char *server, boolean_t accept)
72112581SJames.Kremer@Sun.COM {
72212581SJames.Kremer@Sun.COM 	int err;
72312142SJames.Kremer@Sun.COM 	struct addrinfo aih;
72412581SJames.Kremer@Sun.COM 	ip_cinfo_t *cinfo = fmd_hdl_zalloc(
72512581SJames.Kremer@Sun.COM 	    ip_hdl, sizeof (ip_cinfo_t), FMD_NOSLEEP);
72612581SJames.Kremer@Sun.COM 
72712581SJames.Kremer@Sun.COM 	if (cinfo == NULL)
72812581SJames.Kremer@Sun.COM 		return (NULL);
72912581SJames.Kremer@Sun.COM 
73012581SJames.Kremer@Sun.COM 	cinfo->ipc_accept = accept;
73112581SJames.Kremer@Sun.COM 	cinfo->ipc_retry = ip_retry;
73212581SJames.Kremer@Sun.COM 	if (server != NULL) {
73312581SJames.Kremer@Sun.COM 		cinfo->ipc_name = fmd_hdl_strdup(ip_hdl, server, FMD_NOSLEEP);
73412581SJames.Kremer@Sun.COM 		if (cinfo->ipc_name == NULL) {
73512581SJames.Kremer@Sun.COM 			fmd_hdl_free(ip_hdl, cinfo, sizeof (ip_cinfo_t));
73612581SJames.Kremer@Sun.COM 			return (NULL);
73712581SJames.Kremer@Sun.COM 		}
73812581SJames.Kremer@Sun.COM 	}
73912142SJames.Kremer@Sun.COM 
74012142SJames.Kremer@Sun.COM 	bzero(&aih, sizeof (aih));
74112142SJames.Kremer@Sun.COM 	aih.ai_flags = AI_ADDRCONFIG;
74212142SJames.Kremer@Sun.COM 	aih.ai_family = AF_UNSPEC;
74312142SJames.Kremer@Sun.COM 	aih.ai_socktype = SOCK_STREAM;
74412142SJames.Kremer@Sun.COM 	if (server != NULL) {
74512142SJames.Kremer@Sun.COM 		ip_debug(IP_DEBUG_FINE, "resolving %s:%s\n", server, ip_port);
74612142SJames.Kremer@Sun.COM 	} else {
74712142SJames.Kremer@Sun.COM 		aih.ai_flags |= AI_PASSIVE;
74812581SJames.Kremer@Sun.COM 		cinfo->ipc_name = fmd_hdl_strdup(
74912581SJames.Kremer@Sun.COM 		    ip_hdl, "localhost", FMD_NOSLEEP);
75012581SJames.Kremer@Sun.COM 		if (cinfo->ipc_name == NULL) {
75112581SJames.Kremer@Sun.COM 			fmd_hdl_free(ip_hdl, cinfo, sizeof (ip_cinfo_t));
75212581SJames.Kremer@Sun.COM 			return (NULL);
75312581SJames.Kremer@Sun.COM 		}
75412142SJames.Kremer@Sun.COM 	}
75512142SJames.Kremer@Sun.COM 
75612581SJames.Kremer@Sun.COM 	err = getaddrinfo(server, ip_port, &aih, &cinfo->ipc_addr);
75712142SJames.Kremer@Sun.COM 	if (err != 0) {
75812142SJames.Kremer@Sun.COM 		fmd_hdl_error(ip_hdl, "failed to resolve host %s port %s: %s\n",
75912581SJames.Kremer@Sun.COM 		    cinfo->ipc_name, ip_port, gai_strerror(err));
76012581SJames.Kremer@Sun.COM 		cinfo->ipc_addr = NULL;
76112581SJames.Kremer@Sun.COM 		fmd_hdl_strfree(ip_hdl, cinfo->ipc_name);
76212581SJames.Kremer@Sun.COM 		fmd_hdl_free(ip_hdl, cinfo, sizeof (ip_cinfo_t));
76312581SJames.Kremer@Sun.COM 		cinfo = NULL;
76412581SJames.Kremer@Sun.COM 	}
76512581SJames.Kremer@Sun.COM 	return (cinfo);
76612581SJames.Kremer@Sun.COM }
76712581SJames.Kremer@Sun.COM 
76812581SJames.Kremer@Sun.COM /*
76912581SJames.Kremer@Sun.COM  * Setup a single ip address for ip connection.
77012581SJames.Kremer@Sun.COM  * If unable to setup any of the addresses then all addresses will be cleaned up
77112581SJames.Kremer@Sun.COM  * and non-zero will be returned.
77212581SJames.Kremer@Sun.COM  */
77312581SJames.Kremer@Sun.COM static int
ip_setup_addr(char * server,boolean_t accept)77412581SJames.Kremer@Sun.COM ip_setup_addr(char *server, boolean_t accept)
77512581SJames.Kremer@Sun.COM {
77612581SJames.Kremer@Sun.COM 	int err = 0;
77712581SJames.Kremer@Sun.COM 	ip_cinfo_t *cinfo = ip_create_cinfo(server, accept);
77812581SJames.Kremer@Sun.COM 
77912581SJames.Kremer@Sun.COM 	if (cinfo == NULL) {
78012581SJames.Kremer@Sun.COM 		ip_addr_cleanup();
78112581SJames.Kremer@Sun.COM 		err++;
78212581SJames.Kremer@Sun.COM 	} else {
78312581SJames.Kremer@Sun.COM 		(void) pthread_mutex_lock(&ip_conns_lock);
78412581SJames.Kremer@Sun.COM 		cinfo->ipc_next = ip_conns;
78512581SJames.Kremer@Sun.COM 		ip_conns = cinfo;
78612581SJames.Kremer@Sun.COM 		(void) pthread_mutex_unlock(&ip_conns_lock);
78712142SJames.Kremer@Sun.COM 	}
78812142SJames.Kremer@Sun.COM 	return (err);
78912142SJames.Kremer@Sun.COM }
79012142SJames.Kremer@Sun.COM 
79112142SJames.Kremer@Sun.COM /*
79212581SJames.Kremer@Sun.COM  * Setup a ip addresses for an ip connection.  The address can be a comma
79312581SJames.Kremer@Sun.COM  * separated list of addresses as well.
79412581SJames.Kremer@Sun.COM  * If unable to setup any of the addresses then all addresses will be cleaned up
79512581SJames.Kremer@Sun.COM  * and non-zero will be returned.
79612142SJames.Kremer@Sun.COM  */
79712142SJames.Kremer@Sun.COM static int
ip_setup_addrs(char * server,boolean_t accept)79812581SJames.Kremer@Sun.COM ip_setup_addrs(char *server, boolean_t accept)
79912142SJames.Kremer@Sun.COM {
80012142SJames.Kremer@Sun.COM 	int err = 0;
80112581SJames.Kremer@Sun.COM 	char *addr = server;
80212581SJames.Kremer@Sun.COM 	char *p;
80312142SJames.Kremer@Sun.COM 
80412581SJames.Kremer@Sun.COM 	for (p = server; *p != '\0'; p++) {
80512581SJames.Kremer@Sun.COM 		if (*p == ',') {
80612581SJames.Kremer@Sun.COM 			*p = '\0';
80712581SJames.Kremer@Sun.COM 			err = ip_setup_addr(addr, accept);
80812581SJames.Kremer@Sun.COM 			*p = ',';
80912581SJames.Kremer@Sun.COM 			if (err)
81012581SJames.Kremer@Sun.COM 				return (err);
81112581SJames.Kremer@Sun.COM 			addr = ++p;
81212581SJames.Kremer@Sun.COM 			if (*addr == '\0')
81312581SJames.Kremer@Sun.COM 				break;
81412581SJames.Kremer@Sun.COM 		}
81512142SJames.Kremer@Sun.COM 	}
81612581SJames.Kremer@Sun.COM 	if (*addr != '\0') {
81712581SJames.Kremer@Sun.COM 		err = ip_setup_addr(addr, accept);
81812142SJames.Kremer@Sun.COM 	}
8191193Smws 	return (err);
8201193Smws }
8211193Smws 
8221193Smws /*
82312581SJames.Kremer@Sun.COM  * Starts all connections for each configured network address.  If there is an
82412581SJames.Kremer@Sun.COM  * error starting a connection a timer will be started for a retry.
82512581SJames.Kremer@Sun.COM  */
82612581SJames.Kremer@Sun.COM static void
ip_start_connections()82712581SJames.Kremer@Sun.COM ip_start_connections()
82812581SJames.Kremer@Sun.COM {
82912581SJames.Kremer@Sun.COM 	ip_cinfo_t *conn;
83012581SJames.Kremer@Sun.COM 
83112581SJames.Kremer@Sun.COM 	(void) pthread_mutex_lock(&ip_conns_lock);
83212581SJames.Kremer@Sun.COM 	for (conn = ip_conns; conn != NULL; conn = conn->ipc_next) {
83312581SJames.Kremer@Sun.COM 		if (ip_xprt_setup(ip_hdl, conn) != 0) {
83412581SJames.Kremer@Sun.COM 			conn->ipc_timer = fmd_timer_install(ip_hdl, conn, NULL,
83512581SJames.Kremer@Sun.COM 			    ip_sleep);
83612581SJames.Kremer@Sun.COM 		}
83712581SJames.Kremer@Sun.COM 	}
83812581SJames.Kremer@Sun.COM 	(void) pthread_mutex_unlock(&ip_conns_lock);
83912581SJames.Kremer@Sun.COM }
84012581SJames.Kremer@Sun.COM 
84112581SJames.Kremer@Sun.COM /*
84212307SJames.Kremer@Sun.COM  * Timeout handler for the transport module.  We use these types of timeouts:
8431193Smws  *
84412307SJames.Kremer@Sun.COM  * (a) arg is ip_cinfo_t: attempt ip_xprt_setup(), re-install timeout to retry
84512307SJames.Kremer@Sun.COM  * (b) arg is ip_xprt_t, FMD_XPRT_SUSPENDED: call fmd_xprt_resume() on arg
84612307SJames.Kremer@Sun.COM  * (c) arg is ip_xprt_t, !FMD_XPRT_SUSPENDED: call ip_xprt_destroy() on arg
84712307SJames.Kremer@Sun.COM  * (d) arg is NULL, ignore as this shouldn't happen
8481193Smws  *
8491193Smws  * Case (c) is required as we need to cause the module's main thread, which
8501193Smws  * runs this timeout handler, to join with the transport's auxiliary thread.
85112142SJames.Kremer@Sun.COM  * If the connection is a client then a timer will be installed to retry
85212142SJames.Kremer@Sun.COM  * connecting to the server.
8531193Smws  */
8541193Smws static void
ip_timeout(fmd_hdl_t * hdl,id_t id,void * arg)85512142SJames.Kremer@Sun.COM ip_timeout(fmd_hdl_t *hdl, id_t id, void *arg) {
85612307SJames.Kremer@Sun.COM 	int install_timer;
85712307SJames.Kremer@Sun.COM 	ip_cinfo_t *cinfo;
85812142SJames.Kremer@Sun.COM 	ip_xprt_t *ipx;
8591193Smws 
86012307SJames.Kremer@Sun.COM 	if (arg == NULL) {
86112307SJames.Kremer@Sun.COM 		fmd_hdl_error(hdl, "ip_timeout failed because hg arg is NULL");
86212581SJames.Kremer@Sun.COM 	} else if (ip_argis_cinfo(arg)) {
86312142SJames.Kremer@Sun.COM 		ip_debug(IP_DEBUG_FINER,
86412142SJames.Kremer@Sun.COM 			"Enter ip_timeout (a) install new timer");
86512581SJames.Kremer@Sun.COM 		cinfo = arg;
86612581SJames.Kremer@Sun.COM 		if ((ip_xprt_setup(hdl, arg) != 0) && !ip_quit)
86712581SJames.Kremer@Sun.COM 			cinfo->ipc_timer = fmd_timer_install(
86812581SJames.Kremer@Sun.COM 				hdl, cinfo, NULL, ip_sleep);
86912581SJames.Kremer@Sun.COM 		else
87012581SJames.Kremer@Sun.COM 			cinfo->ipc_timer = NULL;
8711193Smws 	} else {
87212142SJames.Kremer@Sun.COM 		ipx = arg;
87312142SJames.Kremer@Sun.COM 		if (ipx->ipx_flags & FMD_XPRT_SUSPENDED) {
87412581SJames.Kremer@Sun.COM 			ipx->ipx_spnd_timer = NULL;
87512142SJames.Kremer@Sun.COM 			ip_debug(IP_DEBUG_FINE, "timer %d waking ipx %p",
87612142SJames.Kremer@Sun.COM 				(int)id, arg);
87712142SJames.Kremer@Sun.COM 			ipx->ipx_flags &= ~FMD_XPRT_SUSPENDED;
87812142SJames.Kremer@Sun.COM 			fmd_xprt_resume(hdl, ipx->ipx_xprt);
87912142SJames.Kremer@Sun.COM 		} else {
88012142SJames.Kremer@Sun.COM 			ip_debug(IP_DEBUG_FINE, "timer %d closing ipx %p",
88112142SJames.Kremer@Sun.COM 				(int)id, arg);
88212307SJames.Kremer@Sun.COM 			cinfo = ipx->ipx_cinfo;
88312307SJames.Kremer@Sun.COM 			install_timer = (ipx->ipx_flags & FMD_XPRT_ACCEPT) !=
88412307SJames.Kremer@Sun.COM 				FMD_XPRT_ACCEPT;
88512142SJames.Kremer@Sun.COM 			ip_xprt_destroy(ipx);
88612581SJames.Kremer@Sun.COM 			if (install_timer && !ip_quit)
88712581SJames.Kremer@Sun.COM 				cinfo->ipc_timer = fmd_timer_install(
88812307SJames.Kremer@Sun.COM 					hdl, cinfo, NULL, ip_sleep);
88912581SJames.Kremer@Sun.COM 			else
89012581SJames.Kremer@Sun.COM 				cinfo->ipc_timer = NULL;
89112142SJames.Kremer@Sun.COM 		}
8921193Smws 	}
8931193Smws }
8941193Smws 
8951193Smws static const fmd_prop_t fmd_props[] = {
8961193Smws 	{ "ip_authority", FMD_TYPE_STRING, NULL },
8971193Smws 	{ "ip_bufsize", FMD_TYPE_SIZE, "4k" },
8981193Smws 	{ "ip_burp", FMD_TYPE_TIME, "0" },
8991193Smws 	{ "ip_enable", FMD_TYPE_BOOL, "false" },
9001193Smws 	{ "ip_mtbf", FMD_TYPE_INT32, "0" },
9019120SStephen.Hanson@Sun.COM 	{ "ip_external", FMD_TYPE_BOOL, "true" },
9029120SStephen.Hanson@Sun.COM 	{ "ip_no_remote_repair", FMD_TYPE_BOOL, "true" },
9039120SStephen.Hanson@Sun.COM 	{ "ip_hconly", FMD_TYPE_BOOL, "false" },
9049120SStephen.Hanson@Sun.COM 	{ "ip_rdonly", FMD_TYPE_BOOL, "false" },
9059120SStephen.Hanson@Sun.COM 	{ "ip_hc_present_only", FMD_TYPE_BOOL, "false" },
9069120SStephen.Hanson@Sun.COM 	{ "ip_domain_name", FMD_TYPE_STRING, NULL },
9071193Smws 	{ "ip_port", FMD_TYPE_STRING, "664" },
9081193Smws 	{ "ip_qlen", FMD_TYPE_INT32, "32" },
90912142SJames.Kremer@Sun.COM 	{ "ip_retry", FMD_TYPE_INT32, "-1" },	    /* -1=forever */
91012142SJames.Kremer@Sun.COM 	{ "ip_server", FMD_TYPE_STRING, NULL },	    /* server name */
9111193Smws 	{ "ip_sleep", FMD_TYPE_TIME, "10s" },
9121193Smws 	{ "ip_translate", FMD_TYPE_BOOL, "false" },
91312142SJames.Kremer@Sun.COM 	{ "ip_bind_addr", FMD_TYPE_STRING, NULL },  /* network interface addr */
91412142SJames.Kremer@Sun.COM 	{ "ip_debug_level", FMD_TYPE_INT32, "1" },  /* debug levels 0-3 */
9151193Smws 	{ NULL, 0, NULL }
9161193Smws };
9171193Smws 
9181193Smws static const fmd_hdl_ops_t fmd_ops = {
91912307SJames.Kremer@Sun.COM 	ip_fmdo_recv,		/* fmdo_recv */
9201193Smws 	ip_timeout,		/* fmdo_timeout */
9211193Smws 	NULL,			/* fmdo_close */
9221193Smws 	NULL,			/* fmdo_stats */
9231193Smws 	NULL,			/* fmdo_gc */
92412307SJames.Kremer@Sun.COM 	ip_fmdo_send,		/* fmdo_send */
9251193Smws };
9261193Smws 
9271193Smws static const fmd_hdl_info_t fmd_info = {
9281193Smws 	"IP Transport Agent", "1.0", &fmd_ops, fmd_props
9291193Smws };
9301193Smws 
9311193Smws /*
9321193Smws  * Initialize the ip-transport module as either a server or a client.  Note
9331193Smws  * that the ip-transport module is not enabled by default under Solaris:
93412142SJames.Kremer@Sun.COM  * at present we require a developer or tool to "setprop ip_enable true".
9351193Smws  * If ip-transport is needed in the future out-of-the-box on one or more Sun
9361193Smws  * platforms, the code to check 'ip_enable' should be replaced with:
9371193Smws  *
9381193Smws  * (a) configuring ip-transport to operate in client mode by default,
9391193Smws  * (b) a platform-specific configuration mechanism, or
9401193Smws  * (c) a means to assure security and prevent denial-of-service attacks.
9411193Smws  *
9421193Smws  * Note that (c) is only an issue when the transport module operates
9431193Smws  * in server mode (i.e. with the ip_server property set to NULL) on a
9441193Smws  * generic Solaris system which may be exposed directly to the Internet.
94512142SJames.Kremer@Sun.COM  * The property ip_bind_addr can be used to define a private network interface
94612142SJames.Kremer@Sun.COM  * to use so that the service is not exposed to the Internet.
9471193Smws  */
9481193Smws void
_fmd_init(fmd_hdl_t * hdl)9491193Smws _fmd_init(fmd_hdl_t *hdl)
9501193Smws {
95112581SJames.Kremer@Sun.COM 	char *addr, *auth, *p, *q, *r, *s;
95212581SJames.Kremer@Sun.COM 	int err;
9531193Smws 
9541193Smws 	if (fmd_hdl_register(hdl, FMD_API_VERSION, &fmd_info) != 0)
9551193Smws 		return; /* failed to register handle */
9561193Smws 
9571193Smws 	if (fmd_prop_get_int32(hdl, "ip_enable") == FMD_B_FALSE) {
9581193Smws 		fmd_hdl_unregister(hdl);
9591193Smws 		return;
9601193Smws 	}
9611193Smws 
9621193Smws 	(void) fmd_stat_create(hdl, FMD_STAT_NOALLOC,
9631193Smws 	    sizeof (ip_stat) / sizeof (fmd_stat_t), (fmd_stat_t *)&ip_stat);
9641193Smws 
9651193Smws 	ip_hdl = hdl;
9661193Smws 	(void) pthread_mutex_init(&ip_lock, NULL);
9671193Smws 
9681193Smws 	ip_burp = fmd_prop_get_int64(hdl, "ip_burp");
9691193Smws 	ip_mtbf = fmd_prop_get_int32(hdl, "ip_mtbf");
9709120SStephen.Hanson@Sun.COM 	ip_external = fmd_prop_get_int32(hdl, "ip_external");
9719120SStephen.Hanson@Sun.COM 	ip_no_remote_repair = fmd_prop_get_int32(hdl, "ip_no_remote_repair");
9729120SStephen.Hanson@Sun.COM 	ip_hconly = fmd_prop_get_int32(hdl, "ip_hconly");
9739120SStephen.Hanson@Sun.COM 	ip_rdonly = fmd_prop_get_int32(hdl, "ip_rdonly");
9749120SStephen.Hanson@Sun.COM 	ip_hc_present_only = fmd_prop_get_int32(hdl, "ip_hc_present_only");
9759120SStephen.Hanson@Sun.COM 	ip_domain_name = fmd_prop_get_string(hdl, "ip_domain_name");
9761193Smws 	ip_qlen = fmd_prop_get_int32(hdl, "ip_qlen");
9771193Smws 	ip_retry = fmd_prop_get_int32(hdl, "ip_retry");
9781193Smws 	ip_sleep = fmd_prop_get_int64(hdl, "ip_sleep");
9791193Smws 	ip_translate = fmd_prop_get_int32(hdl, "ip_translate");
9801193Smws 
9811193Smws 	ip_size = (size_t)fmd_prop_get_int64(hdl, "ip_bufsize");
9821193Smws 	ip_size = MAX(ip_size, sizeof (ip_hdr_t));
9831193Smws 	ip_port = fmd_prop_get_string(hdl, "ip_port");
98412142SJames.Kremer@Sun.COM 	ip_debug_level = fmd_prop_get_int32(hdl, "ip_debug_level");
9851193Smws 
98612581SJames.Kremer@Sun.COM 	ip_conns = NULL;
98712581SJames.Kremer@Sun.COM 	addr = fmd_prop_get_string(hdl, "ip_bind_addr");
98812581SJames.Kremer@Sun.COM 	if (addr != NULL) {
98912581SJames.Kremer@Sun.COM 		err = ip_setup_addrs(addr, B_TRUE);
99012581SJames.Kremer@Sun.COM 		if (err) {
99112581SJames.Kremer@Sun.COM 			fmd_hdl_abort(hdl, "Unable to setup ip_bind_addr %s",
99212581SJames.Kremer@Sun.COM 			    addr);
99312581SJames.Kremer@Sun.COM 			return;
99412581SJames.Kremer@Sun.COM 		}
99512581SJames.Kremer@Sun.COM 		fmd_prop_free_string(hdl, addr);
99612581SJames.Kremer@Sun.COM 	}
99712581SJames.Kremer@Sun.COM 	addr = fmd_prop_get_string(hdl, "ip_server");
99812581SJames.Kremer@Sun.COM 	if (addr != NULL) {
99912581SJames.Kremer@Sun.COM 		err = ip_setup_addrs(addr, B_FALSE);
100012581SJames.Kremer@Sun.COM 		if (err) {
100112581SJames.Kremer@Sun.COM 			fmd_hdl_abort(hdl, "Unable to setup ip_server %s",
100212581SJames.Kremer@Sun.COM 			    addr);
100312581SJames.Kremer@Sun.COM 			return;
100412581SJames.Kremer@Sun.COM 		}
100512581SJames.Kremer@Sun.COM 		fmd_prop_free_string(hdl, addr);
100612142SJames.Kremer@Sun.COM 	}
10071193Smws 
100812581SJames.Kremer@Sun.COM 	/*
100912581SJames.Kremer@Sun.COM 	 * If no specific connecitons configured then set up general server
101012581SJames.Kremer@Sun.COM 	 * listening on all network ports.
101112581SJames.Kremer@Sun.COM 	 */
101212581SJames.Kremer@Sun.COM 	if (ip_conns == NULL) {
101312581SJames.Kremer@Sun.COM 		if (ip_setup_addr(NULL, B_TRUE) != 0) {
101412581SJames.Kremer@Sun.COM 			fmd_hdl_abort(hdl, "Unable to setup server.");
101512581SJames.Kremer@Sun.COM 			return;
101612581SJames.Kremer@Sun.COM 		}
101712581SJames.Kremer@Sun.COM 	}
10181193Smws 
10191193Smws 	/*
10201193Smws 	 * If ip_authority is set, tokenize this string and turn it into an
10211193Smws 	 * FMA authority represented as a name-value pair list.  We will use
10221193Smws 	 * this authority for all transports created by this module.  If
10231193Smws 	 * ip_authority isn't set, we'll compute authorities on the fly.
10241193Smws 	 */
10251193Smws 	if ((auth = fmd_prop_get_string(hdl, "ip_authority")) != NULL) {
10261193Smws 		(void) nvlist_alloc(&ip_auth, 0, 0);
10271193Smws 		(void) nvlist_add_uint8(ip_auth,
10281193Smws 		    FM_VERSION, FM_FMRI_AUTH_VERSION);
10291193Smws 
1030*13093SRoger.Faulkner@Oracle.COM 		s = strdupa(auth);
10311193Smws 		fmd_prop_free_string(hdl, auth);
10321193Smws 
10331193Smws 		for (p = strtok_r(s, ",", &q); p != NULL;
10341193Smws 		    p = strtok_r(NULL, ",", &q)) {
10351193Smws 
10361193Smws 			if ((r = strchr(p, '=')) == NULL) {
103712142SJames.Kremer@Sun.COM 				ip_addr_cleanup();
10381193Smws 				fmd_hdl_abort(hdl, "ip_authority element <%s> "
10391193Smws 				    "must be in <name>=<value> form\n", p);
10401193Smws 			}
10411193Smws 
10421193Smws 			*r = '\0';
10431193Smws 			(void) nvlist_add_string(ip_auth, p, r + 1);
10441193Smws 			*r = '=';
10451193Smws 		}
10461193Smws 	}
10471193Smws 
104812581SJames.Kremer@Sun.COM 	ip_start_connections();
10491193Smws }
10501193Smws 
10511193Smws void
_fmd_fini(fmd_hdl_t * hdl)10521193Smws _fmd_fini(fmd_hdl_t *hdl)
10531193Smws {
10541193Smws 	ip_quit++; /* set quit flag before signalling auxiliary threads */
10551193Smws 
10561193Smws 	while (ip_xps != NULL)
10571193Smws 		ip_xprt_destroy(ip_xps);
10581193Smws 
10591193Smws 	if (ip_auth != NULL)
10601193Smws 		nvlist_free(ip_auth);
106112581SJames.Kremer@Sun.COM 
106212142SJames.Kremer@Sun.COM 	ip_addr_cleanup();
10631193Smws 
106412581SJames.Kremer@Sun.COM 	if (ip_domain_name != NULL)
106512581SJames.Kremer@Sun.COM 		fmd_prop_free_string(ip_hdl, ip_domain_name);
106612581SJames.Kremer@Sun.COM 
10671193Smws 	fmd_hdl_unregister(hdl);
10681193Smws }
1069