xref: /illumos-gate/usr/src/cmd/fm/modules/common/ip-transport/ip.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include <sys/types.h>
28 #include <sys/socket.h>
29 #include <sys/sysmacros.h>
30 #include <sys/fm/protocol.h>
31 
32 #include <netinet/in.h>
33 #include <arpa/inet.h>
34 
35 #include <strings.h>
36 #include <unistd.h>
37 #include <pthread.h>
38 #include <alloca.h>
39 #include <fcntl.h>
40 #include <errno.h>
41 #include <netdb.h>
42 #include <poll.h>
43 
44 #include <fm/fmd_api.h>
45 
46 #define	IP_MAGIC	"\177FMA" /* magic string identifying a packet header */
47 #define	IP_MAGLEN	4	/* length of magic string */
48 
49 typedef struct ip_hdr {
50 	char iph_magic[IP_MAGLEN]; /* magic string */
51 	uint32_t iph_size;	/* packed size */
52 } ip_hdr_t;
53 
54 typedef struct ip_buf {
55 	void *ipb_buf;		/* data buffer */
56 	size_t ipb_size;	/* size of buffer */
57 } ip_buf_t;
58 
59 typedef struct ip_xprt {
60 	fmd_xprt_t *ipx_xprt;	/* transport handle */
61 	int ipx_flags;		/* transport flags */
62 	int ipx_fd;		/* socket file descriptor */
63 	int ipx_done;		/* flag indicating connection closed */
64 	pthread_t ipx_tid;	/* recv-side auxiliary thread */
65 	ip_buf_t ipx_sndbuf;	/* buffer for sending events */
66 	ip_buf_t ipx_rcvbuf;	/* buffer for receiving events */
67 	struct ip_xprt *ipx_next; /* next ip_xprt in global list */
68 } ip_xprt_t;
69 
70 typedef struct ip_stat {
71 	fmd_stat_t ips_accfail;	/* failed accepts */
72 	fmd_stat_t ips_badmagic; /* invalid packet headers */
73 	fmd_stat_t ips_packfail; /* failed packs */
74 	fmd_stat_t ips_unpackfail; /* failed unpacks */
75 } ip_stat_t;
76 
77 static void ip_xprt_create(fmd_xprt_t *, int, int);
78 static void ip_xprt_destroy(ip_xprt_t *);
79 
80 static ip_stat_t ip_stat = {
81 	{ "accfail", FMD_TYPE_UINT64, "failed accepts" },
82 	{ "badmagic", FMD_TYPE_UINT64, "invalid packet headers" },
83 	{ "packfail", FMD_TYPE_UINT64, "failed packs" },
84 	{ "unpackfail", FMD_TYPE_UINT64, "failed unpacks" },
85 };
86 
87 static fmd_hdl_t *ip_hdl;	/* module handle */
88 static pthread_mutex_t ip_lock;	/* lock for ip_xps list */
89 static ip_xprt_t *ip_xps;	/* list of active transports */
90 static nvlist_t *ip_auth;	/* authority to use for transport(s) */
91 static size_t ip_size;		/* default buffer size */
92 static volatile int ip_quit;	/* signal to quit */
93 static int ip_qlen;		/* queue length for listen(3SOCKET) */
94 static int ip_mtbf;		/* mtbf for simulating packet drop */
95 static int ip_external;		/* set transport to be "external" */
96 static int ip_no_remote_repair;	/* disallow remote repair */
97 static int ip_hconly;		/* only cache faults that are hc-scheme */
98 static int ip_rdonly;		/* force transport to be rdonly */
99 static int ip_hc_present_only;	/* only cache faults if hc-scheme and present */
100 static char *ip_domain_name;	/* set domain name for received list.suspects */
101 static hrtime_t ip_burp;	/* make mtbf slower by adding this much delay */
102 static int ip_translate;	/* call fmd_xprt_translate() before sending */
103 static char *ip_host;		/* host to connect to (or NULL if server) */
104 static char *ip_port;		/* port to connect to (or bind to if server) */
105 static struct addrinfo *ip_ail; /* addr info list for ip_host/ip_port */
106 static uint_t ip_retry;		/* retry count for ip_xprt_setup() */
107 static hrtime_t ip_sleep;	/* sleep delay for ip_xprt_setup() */
108 
109 /*
110  * Allocate space in ipx_sndbuf for a header and a packed XDR encoding of
111  * the specified nvlist, and then send the buffer to our remote peer.
112  */
113 /*ARGSUSED*/
114 static int
115 ip_xprt_send(fmd_hdl_t *hdl, fmd_xprt_t *xp, fmd_event_t *ep, nvlist_t *nvl)
116 {
117 	ip_xprt_t *ipx = fmd_xprt_getspecific(hdl, xp);
118 
119 	size_t size, nvsize;
120 	char *buf, *nvbuf;
121 	ip_hdr_t *iph;
122 	ssize_t r, n;
123 	int err;
124 
125 	/*
126 	 * For testing purposes, if ip_mtbf is non-zero, use this to pseudo-
127 	 * randomly simulate the need for retries.  If ip_burp is also set,
128 	 * then we also suspend the transport for a bit and wake it up again.
129 	 */
130 	if (ip_mtbf != 0 && gethrtime() % ip_mtbf == 0) {
131 		if (ip_burp != 0) {
132 			fmd_hdl_debug(ip_hdl, "burping ipx %p", (void *)ipx);
133 			ipx->ipx_flags |= FMD_XPRT_SUSPENDED;
134 			(void) fmd_timer_install(ip_hdl, ipx, NULL, ip_burp);
135 			fmd_xprt_suspend(ip_hdl, xp);
136 		}
137 		return (FMD_SEND_RETRY);
138 	}
139 
140 	if (ip_translate && (nvl = fmd_xprt_translate(hdl, xp, ep)) == NULL) {
141 		fmd_hdl_error(hdl, "failed to translate event %p", (void *)ep);
142 		return (FMD_SEND_FAILED);
143 	}
144 
145 	(void) nvlist_size(nvl, &nvsize, NV_ENCODE_XDR);
146 	size = r = sizeof (ip_hdr_t) + nvsize;
147 
148 	if (ipx->ipx_sndbuf.ipb_size < size) {
149 		fmd_hdl_free(hdl, ipx->ipx_sndbuf.ipb_buf,
150 		    ipx->ipx_sndbuf.ipb_size);
151 		ipx->ipx_sndbuf.ipb_size = P2ROUNDUP(size, 16);
152 		ipx->ipx_sndbuf.ipb_buf = fmd_hdl_alloc(hdl,
153 		    ipx->ipx_sndbuf.ipb_size, FMD_SLEEP);
154 	}
155 
156 	buf = ipx->ipx_sndbuf.ipb_buf;
157 	iph = (ip_hdr_t *)(uintptr_t)buf;
158 	nvbuf = buf + sizeof (ip_hdr_t);
159 
160 	bcopy(IP_MAGIC, iph->iph_magic, IP_MAGLEN);
161 	iph->iph_size = htonl(nvsize);
162 	err = nvlist_pack(nvl, &nvbuf, &nvsize, NV_ENCODE_XDR, 0);
163 
164 	if (ip_translate)
165 		nvlist_free(nvl);
166 
167 	if (err != 0) {
168 		fmd_hdl_error(ip_hdl, "failed to pack event for "
169 		    "transport %p: %s\n", (void *)ipx->ipx_xprt, strerror(err));
170 		ip_stat.ips_packfail.fmds_value.ui64++;
171 		return (FMD_SEND_FAILED);
172 	}
173 
174 	while (!ip_quit && r != 0) {
175 		if ((n = send(ipx->ipx_fd, buf, r, 0)) < 0) {
176 			if (errno != EINTR && errno != EWOULDBLOCK) {
177 				fmd_hdl_debug(ip_hdl,
178 				    "failed to send on ipx %p", (void *)ipx);
179 				return (FMD_SEND_FAILED);
180 			}
181 			continue;
182 		}
183 		buf += n;
184 		r -= n;
185 	}
186 
187 	return (FMD_SEND_SUCCESS);
188 }
189 
190 /*
191  * Receive a chunk of data of the specified size from our remote peer.  The
192  * data is received into ipx_rcvbuf, and then a pointer to the buffer is
193  * returned.  NOTE: The data is only valid until the next call to ip_xprt_recv.
194  * If the connection breaks or ip_quit is set during receive, NULL is returned.
195  */
196 static void *
197 ip_xprt_recv(ip_xprt_t *ipx, size_t size)
198 {
199 	char *buf = ipx->ipx_rcvbuf.ipb_buf;
200 	ssize_t n, r = size;
201 
202 	if (ipx->ipx_rcvbuf.ipb_size < size) {
203 		fmd_hdl_free(ip_hdl, ipx->ipx_rcvbuf.ipb_buf,
204 		    ipx->ipx_rcvbuf.ipb_size);
205 		ipx->ipx_rcvbuf.ipb_size = P2ROUNDUP(size, 16);
206 		ipx->ipx_rcvbuf.ipb_buf = buf = fmd_hdl_alloc(ip_hdl,
207 		    ipx->ipx_rcvbuf.ipb_size, FMD_SLEEP);
208 	}
209 
210 	while (!ip_quit && r != 0) {
211 		if ((n = recv(ipx->ipx_fd, buf, r, MSG_WAITALL)) == 0) {
212 			ipx->ipx_done++;
213 			return (NULL);
214 		}
215 
216 		if (n < 0) {
217 			if (errno != EINTR && errno != EWOULDBLOCK) {
218 				fmd_hdl_debug(ip_hdl,
219 				    "failed to recv on ipx %p", (void *)ipx);
220 			}
221 			continue;
222 		}
223 
224 		buf += n;
225 		r -= n;
226 	}
227 
228 	return (r ? NULL: ipx->ipx_rcvbuf.ipb_buf);
229 }
230 
231 static nvlist_t *
232 ip_xprt_auth(const struct sockaddr *sap)
233 {
234 	const struct sockaddr_in6 *sin6 = (const void *)sap;
235 	const struct sockaddr_in *sin = (const void *)sap;
236 
237 	char buf[INET6_ADDRSTRLEN + 16];
238 	struct in_addr v4addr;
239 	in_port_t port;
240 
241 	nvlist_t *nvl;
242 	size_t n;
243 	int err;
244 
245 	if (ip_auth != NULL)
246 		err = nvlist_dup(ip_auth, &nvl, 0);
247 	else
248 		err = nvlist_alloc(&nvl, 0, 0);
249 
250 	if (err != 0) {
251 		fmd_hdl_abort(ip_hdl, "failed to create nvlist for "
252 		    "authority: %s\n", strerror(err));
253 	}
254 
255 	if (ip_auth != NULL)
256 		return (nvl);
257 
258 	if (sap->sa_family == AF_INET6 &&
259 	    IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
260 		IN6_V4MAPPED_TO_INADDR(&sin6->sin6_addr, &v4addr);
261 		(void) inet_ntop(AF_INET, &v4addr, buf, sizeof (buf));
262 		port = ntohs(sin6->sin6_port);
263 	} else if (sap->sa_family == AF_INET6) {
264 		(void) inet_ntop(AF_INET6, &sin6->sin6_addr, buf, sizeof (buf));
265 		port = ntohs(sin6->sin6_port);
266 	} else {
267 		(void) inet_ntop(AF_INET, &sin->sin_addr, buf, sizeof (buf));
268 		port = ntohs(sin->sin_port);
269 	}
270 
271 	n = strlen(buf);
272 	(void) snprintf(buf + n, sizeof (buf) - n, ":%u", port);
273 	fmd_hdl_debug(ip_hdl, "ip_authority %s=%s\n", FM_FMRI_AUTH_SERVER, buf);
274 
275 	(void) nvlist_add_uint8(nvl, FM_VERSION, FM_FMRI_AUTH_VERSION);
276 	(void) nvlist_add_string(nvl, FM_FMRI_AUTH_SERVER, buf);
277 
278 	return (nvl);
279 }
280 
281 static void
282 ip_xprt_accept(ip_xprt_t *ipx)
283 {
284 	struct sockaddr_storage sa;
285 	socklen_t salen = sizeof (sa);
286 	fmd_xprt_t *xp;
287 	int fd;
288 
289 	if ((fd = accept(ipx->ipx_fd, (struct sockaddr *)&sa, &salen)) == -1) {
290 		fmd_hdl_error(ip_hdl, "failed to accept connection");
291 		ip_stat.ips_accfail.fmds_value.ui64++;
292 		return;
293 	}
294 
295 	xp = fmd_xprt_open(ip_hdl, ipx->ipx_flags,
296 	    ip_xprt_auth((struct sockaddr *)&sa), NULL);
297 	ip_xprt_create(xp, fd, ipx->ipx_flags);
298 }
299 
300 static void
301 ip_xprt_recv_event(ip_xprt_t *ipx)
302 {
303 	ip_hdr_t *iph;
304 	nvlist_t *nvl;
305 	size_t size;
306 	void *buf;
307 	int err;
308 
309 	if ((iph = ip_xprt_recv(ipx, sizeof (ip_hdr_t))) == NULL)
310 		return; /* connection broken */
311 
312 	if (bcmp(iph->iph_magic, IP_MAGIC, IP_MAGLEN) != 0) {
313 		fmd_hdl_error(ip_hdl,
314 		    "invalid hdr magic %x.%x.%x.%x from transport %p\n",
315 		    iph->iph_magic[0], iph->iph_magic[1], iph->iph_magic[2],
316 		    iph->iph_magic[3], (void *)ipx->ipx_xprt);
317 		ip_stat.ips_badmagic.fmds_value.ui64++;
318 		return;
319 	}
320 
321 	size = ntohl(iph->iph_size);
322 
323 	if ((buf = ip_xprt_recv(ipx, size)) == NULL)
324 		return; /* connection broken */
325 
326 	if ((err = nvlist_unpack(buf, size, &nvl, 0)) != 0) {
327 		fmd_hdl_error(ip_hdl, "failed to unpack event from "
328 		    "transport %p: %s\n", (void *)ipx->ipx_xprt, strerror(err));
329 		ip_stat.ips_unpackfail.fmds_value.ui64++;
330 	} else {
331 		if (ip_domain_name)
332 			fmd_xprt_add_domain(ip_hdl, nvl, ip_domain_name);
333 		fmd_xprt_post(ip_hdl, ipx->ipx_xprt, nvl, 0);
334 	}
335 
336 	if (fmd_xprt_error(ip_hdl, ipx->ipx_xprt)) {
337 		fmd_hdl_error(ip_hdl, "protocol error on transport %p",
338 		    (void *)ipx->ipx_xprt);
339 		ipx->ipx_done++;
340 	}
341 }
342 
343 static void
344 ip_xprt_thread(void *arg)
345 {
346 	ip_xprt_t *ipx = arg;
347 	struct sockaddr_storage sa;
348 	socklen_t salen = sizeof (sa);
349 	struct pollfd pfd;
350 	id_t id;
351 
352 	while (!ip_quit && !ipx->ipx_done) {
353 		if (ipx->ipx_xprt != NULL || (ipx->ipx_flags & FMD_XPRT_ACCEPT))
354 			pfd.events = POLLIN;
355 		else
356 			pfd.events = POLLOUT;
357 
358 		pfd.fd = ipx->ipx_fd;
359 		pfd.revents = 0;
360 
361 		if (poll(&pfd, 1, -1) <= 0)
362 			continue; /* loop around and check ip_quit */
363 
364 		if (pfd.revents & (POLLHUP | POLLERR)) {
365 			fmd_hdl_debug(ip_hdl, "hangup fd %d\n", ipx->ipx_fd);
366 			break;
367 		}
368 
369 		if (pfd.revents & POLLOUT) {
370 			/*
371 			 * Once we're connected, there's no reason to have our
372 			 * calls to recv() and send() be non-blocking since we
373 			 * we have separate threads for each: clear O_NONBLOCK.
374 			 */
375 			(void) fcntl(ipx->ipx_fd, F_SETFL,
376 			    fcntl(ipx->ipx_fd, F_GETFL, 0) & ~O_NONBLOCK);
377 
378 			if (getpeername(ipx->ipx_fd, (struct sockaddr *)&sa,
379 			    &salen) != 0) {
380 				fmd_hdl_error(ip_hdl, "failed to get peer name "
381 				    "for fd %d", ipx->ipx_fd);
382 				bzero(&sa, sizeof (sa));
383 			}
384 
385 			ipx->ipx_xprt = fmd_xprt_open(ip_hdl, ipx->ipx_flags,
386 			    ip_xprt_auth((struct sockaddr *)&sa), ipx);
387 
388 			fmd_hdl_debug(ip_hdl, "connect fd %d\n", ipx->ipx_fd);
389 			continue;
390 		}
391 
392 		if (pfd.revents & POLLIN) {
393 			if (ipx->ipx_xprt == NULL)
394 				ip_xprt_accept(ipx);
395 			else
396 				ip_xprt_recv_event(ipx);
397 		}
398 	}
399 
400 	id = fmd_timer_install(ip_hdl, ipx, NULL, 0);
401 	fmd_hdl_debug(ip_hdl, "close fd %d (timer %d)\n", ipx->ipx_fd, (int)id);
402 }
403 
404 static void
405 ip_xprt_create(fmd_xprt_t *xp, int fd, int flags)
406 {
407 	ip_xprt_t *ipx = fmd_hdl_zalloc(ip_hdl, sizeof (ip_xprt_t), FMD_SLEEP);
408 
409 	ipx->ipx_xprt = xp;
410 	ipx->ipx_flags = flags;
411 	ipx->ipx_fd = fd;
412 	ipx->ipx_tid = fmd_thr_create(ip_hdl, ip_xprt_thread, ipx);
413 
414 	if (ipx->ipx_xprt != NULL)
415 		fmd_xprt_setspecific(ip_hdl, ipx->ipx_xprt, ipx);
416 
417 	(void) pthread_mutex_lock(&ip_lock);
418 
419 	ipx->ipx_next = ip_xps;
420 	ip_xps = ipx;
421 
422 	(void) pthread_mutex_unlock(&ip_lock);
423 }
424 
425 static void
426 ip_xprt_destroy(ip_xprt_t *ipx)
427 {
428 	ip_xprt_t *ipp, **ppx = &ip_xps;
429 
430 	(void) pthread_mutex_lock(&ip_lock);
431 
432 	for (ipp = *ppx; ipp != NULL; ipp = ipp->ipx_next) {
433 		if (ipp != ipx)
434 			ppx = &ipp->ipx_next;
435 		else
436 			break;
437 	}
438 
439 	if (ipp != ipx) {
440 		(void) pthread_mutex_unlock(&ip_lock);
441 		fmd_hdl_abort(ip_hdl, "ipx %p not on xps list\n", (void *)ipx);
442 	}
443 
444 	*ppx = ipx->ipx_next;
445 	ipx->ipx_next = NULL;
446 
447 	(void) pthread_mutex_unlock(&ip_lock);
448 
449 	fmd_thr_signal(ip_hdl, ipx->ipx_tid);
450 	fmd_thr_destroy(ip_hdl, ipx->ipx_tid);
451 
452 	if (ipx->ipx_xprt != NULL)
453 		fmd_xprt_close(ip_hdl, ipx->ipx_xprt);
454 
455 	fmd_hdl_free(ip_hdl, ipx->ipx_sndbuf.ipb_buf, ipx->ipx_sndbuf.ipb_size);
456 	fmd_hdl_free(ip_hdl, ipx->ipx_rcvbuf.ipb_buf, ipx->ipx_rcvbuf.ipb_size);
457 
458 	(void) close(ipx->ipx_fd);
459 	fmd_hdl_free(ip_hdl, ipx, sizeof (ip_xprt_t));
460 }
461 
462 /*
463  * Loop through the addresses that were returned by getaddrinfo() in _fmd_init
464  * and for each one attempt to create a socket and initialize it.  If we are
465  * successful, return zero.  If we fail, we check ip_retry: if it is non-zero
466  * we return the last errno and let our caller retry ip_xprt_setup() later.  If
467  * ip_retry reaches zero, we call fmd_hdl_abort() with an appropriate message.
468  */
469 static int
470 ip_xprt_setup(fmd_hdl_t *hdl)
471 {
472 	int err, fd, oflags, xflags, optval = 1;
473 	struct addrinfo *aip;
474 	const char *s1, *s2;
475 
476 	/*
477 	 * Set up flags as specified in the .conf file. Note that these are
478 	 * mostly only used for testing purposes, allowing the transport to
479 	 * be set up in various modes.
480 	 */
481 	if (ip_host != NULL)
482 		xflags = (ip_rdonly == FMD_B_TRUE) ? FMD_XPRT_RDONLY :
483 		    FMD_XPRT_RDWR;
484 	else
485 		xflags = ((ip_rdonly == FMD_B_TRUE) ? FMD_XPRT_RDONLY :
486 		    FMD_XPRT_RDWR) | FMD_XPRT_ACCEPT;
487 
488 	if (ip_external == FMD_B_TRUE)
489 		xflags |= FMD_XPRT_EXTERNAL;
490 	if (ip_no_remote_repair == FMD_B_TRUE)
491 		xflags |= FMD_XPRT_NO_REMOTE_REPAIR;
492 	if (ip_hconly == FMD_B_TRUE)
493 		xflags |= FMD_XPRT_HCONLY;
494 	if (ip_hc_present_only == FMD_B_TRUE)
495 		xflags |= FMD_XPRT_HC_PRESENT_ONLY;
496 
497 	for (aip = ip_ail; aip != NULL; aip = aip->ai_next) {
498 		if (aip->ai_family != AF_INET && aip->ai_family != AF_INET6)
499 			continue; /* ignore anything that isn't IPv4 or IPv6 */
500 
501 		if ((fd = socket(aip->ai_family,
502 		    aip->ai_socktype, aip->ai_protocol)) == -1) {
503 			err = errno;
504 			continue;
505 		}
506 
507 		oflags = fcntl(fd, F_GETFL, 0);
508 		(void) fcntl(fd, F_SETFL, oflags | O_NONBLOCK);
509 
510 		if (xflags & FMD_XPRT_ACCEPT) {
511 			err = setsockopt(fd, SOL_SOCKET,
512 			    SO_REUSEADDR, &optval, sizeof (optval)) != 0 ||
513 			    bind(fd, aip->ai_addr, aip->ai_addrlen) != 0 ||
514 			    listen(fd, ip_qlen) != 0;
515 		} else {
516 			err = connect(fd, aip->ai_addr,
517 			    aip->ai_addrlen) != 0 && errno != EINPROGRESS;
518 		}
519 
520 		if (err == 0) {
521 			ip_xprt_create(NULL, fd, xflags);
522 			freeaddrinfo(ip_ail);
523 			ip_ail = NULL;
524 			return (0);
525 		}
526 
527 		err = errno;
528 		(void) close(fd);
529 	}
530 
531 	if (ip_host != NULL) {
532 		s1 = "failed to connect to";
533 		s2 = ip_host;
534 	} else {
535 		s1 = "failed to listen on";
536 		s2 = ip_port;
537 	}
538 
539 	if (err == EACCES || ip_retry-- == 0)
540 		fmd_hdl_abort(hdl, "%s %s: %s\n", s1, s2, strerror(err));
541 
542 	fmd_hdl_debug(hdl, "%s %s: %s (will retry)\n", s1, s2, strerror(err));
543 	return (err);
544 }
545 
546 /*
547  * Timeout handler for the transport module.  We use three types of timeouts:
548  *
549  * (a) arg is NULL: attempt ip_xprt_setup(), re-install timeout to retry
550  * (b) arg is non-NULL, FMD_XPRT_SUSPENDED: call fmd_xprt_resume() on arg
551  * (c) arg is non-NULL, !FMD_XPRT_SUSPENDED: call ip_xprt_destroy() on arg
552  *
553  * Case (c) is required as we need to cause the module's main thread, which
554  * runs this timeout handler, to join with the transport's auxiliary thread.
555  */
556 static void
557 ip_timeout(fmd_hdl_t *hdl, id_t id, void *arg)
558 {
559 	ip_xprt_t *ipx = arg;
560 
561 	if (ipx == NULL) {
562 		if (ip_xprt_setup(hdl) != 0)
563 			(void) fmd_timer_install(hdl, NULL, NULL, ip_sleep);
564 	} else if (ipx->ipx_flags & FMD_XPRT_SUSPENDED) {
565 		fmd_hdl_debug(hdl, "timer %d waking ipx %p\n", (int)id, arg);
566 		ipx->ipx_flags &= ~FMD_XPRT_SUSPENDED;
567 		fmd_xprt_resume(hdl, ipx->ipx_xprt);
568 	} else {
569 		fmd_hdl_debug(hdl, "timer %d closing ipx %p\n", (int)id, arg);
570 		ip_xprt_destroy(ipx);
571 	}
572 }
573 
574 static const fmd_prop_t fmd_props[] = {
575 	{ "ip_authority", FMD_TYPE_STRING, NULL },
576 	{ "ip_bufsize", FMD_TYPE_SIZE, "4k" },
577 	{ "ip_burp", FMD_TYPE_TIME, "0" },
578 	{ "ip_enable", FMD_TYPE_BOOL, "false" },
579 	{ "ip_mtbf", FMD_TYPE_INT32, "0" },
580 	{ "ip_external", FMD_TYPE_BOOL, "true" },
581 	{ "ip_no_remote_repair", FMD_TYPE_BOOL, "true" },
582 	{ "ip_hconly", FMD_TYPE_BOOL, "false" },
583 	{ "ip_rdonly", FMD_TYPE_BOOL, "false" },
584 	{ "ip_hc_present_only", FMD_TYPE_BOOL, "false" },
585 	{ "ip_domain_name", FMD_TYPE_STRING, NULL },
586 	{ "ip_port", FMD_TYPE_STRING, "664" },
587 	{ "ip_qlen", FMD_TYPE_INT32, "32" },
588 	{ "ip_retry", FMD_TYPE_UINT32, "50" },
589 	{ "ip_server", FMD_TYPE_STRING, NULL },
590 	{ "ip_sleep", FMD_TYPE_TIME, "10s" },
591 	{ "ip_translate", FMD_TYPE_BOOL, "false" },
592 	{ NULL, 0, NULL }
593 };
594 
595 static const fmd_hdl_ops_t fmd_ops = {
596 	NULL,			/* fmdo_recv */
597 	ip_timeout,		/* fmdo_timeout */
598 	NULL,			/* fmdo_close */
599 	NULL,			/* fmdo_stats */
600 	NULL,			/* fmdo_gc */
601 	ip_xprt_send,		/* fmdo_send */
602 };
603 
604 static const fmd_hdl_info_t fmd_info = {
605 	"IP Transport Agent", "1.0", &fmd_ops, fmd_props
606 };
607 
608 /*
609  * Initialize the ip-transport module as either a server or a client.  Note
610  * that the ip-transport module is not enabled by default under Solaris:
611  * at present we require a developer or tool to setprop ip_enable=true.
612  * If ip-transport is needed in the future out-of-the-box on one or more Sun
613  * platforms, the code to check 'ip_enable' should be replaced with:
614  *
615  * (a) configuring ip-transport to operate in client mode by default,
616  * (b) a platform-specific configuration mechanism, or
617  * (c) a means to assure security and prevent denial-of-service attacks.
618  *
619  * Note that (c) is only an issue when the transport module operates
620  * in server mode (i.e. with the ip_server property set to NULL) on a
621  * generic Solaris system which may be exposed directly to the Internet.
622  */
623 void
624 _fmd_init(fmd_hdl_t *hdl)
625 {
626 	struct addrinfo aih;
627 	char *auth, *p, *q, *r, *s;
628 	int err;
629 
630 	if (fmd_hdl_register(hdl, FMD_API_VERSION, &fmd_info) != 0)
631 		return; /* failed to register handle */
632 
633 	if (fmd_prop_get_int32(hdl, "ip_enable") == FMD_B_FALSE) {
634 		fmd_hdl_unregister(hdl);
635 		return;
636 	}
637 
638 	(void) fmd_stat_create(hdl, FMD_STAT_NOALLOC,
639 	    sizeof (ip_stat) / sizeof (fmd_stat_t), (fmd_stat_t *)&ip_stat);
640 
641 	ip_hdl = hdl;
642 	(void) pthread_mutex_init(&ip_lock, NULL);
643 
644 	ip_burp = fmd_prop_get_int64(hdl, "ip_burp");
645 	ip_mtbf = fmd_prop_get_int32(hdl, "ip_mtbf");
646 	ip_external = fmd_prop_get_int32(hdl, "ip_external");
647 	ip_no_remote_repair = fmd_prop_get_int32(hdl, "ip_no_remote_repair");
648 	ip_hconly = fmd_prop_get_int32(hdl, "ip_hconly");
649 	ip_rdonly = fmd_prop_get_int32(hdl, "ip_rdonly");
650 	ip_hc_present_only = fmd_prop_get_int32(hdl, "ip_hc_present_only");
651 	ip_domain_name = fmd_prop_get_string(hdl, "ip_domain_name");
652 	ip_qlen = fmd_prop_get_int32(hdl, "ip_qlen");
653 	ip_retry = fmd_prop_get_int32(hdl, "ip_retry");
654 	ip_sleep = fmd_prop_get_int64(hdl, "ip_sleep");
655 	ip_translate = fmd_prop_get_int32(hdl, "ip_translate");
656 
657 	ip_size = (size_t)fmd_prop_get_int64(hdl, "ip_bufsize");
658 	ip_size = MAX(ip_size, sizeof (ip_hdr_t));
659 
660 	ip_host = fmd_prop_get_string(hdl, "ip_server");
661 	ip_port = fmd_prop_get_string(hdl, "ip_port");
662 
663 	bzero(&aih, sizeof (aih));
664 	aih.ai_flags = AI_ADDRCONFIG;
665 	aih.ai_family = AF_UNSPEC;
666 	aih.ai_socktype = SOCK_STREAM;
667 
668 	if (ip_host != NULL)
669 		fmd_hdl_debug(hdl, "resolving %s:%s\n", ip_host, ip_port);
670 	else
671 		aih.ai_flags |= AI_PASSIVE;
672 
673 	err = getaddrinfo(ip_host, ip_port, &aih, &ip_ail);
674 
675 	if (err != 0) {
676 		fmd_prop_free_string(hdl, ip_host);
677 		fmd_prop_free_string(hdl, ip_port);
678 
679 		fmd_hdl_abort(hdl, "failed to resolve host %s port %s: %s\n",
680 		    ip_host ? ip_host : "<none>", ip_port, gai_strerror(err));
681 	}
682 
683 	/*
684 	 * If ip_authority is set, tokenize this string and turn it into an
685 	 * FMA authority represented as a name-value pair list.  We will use
686 	 * this authority for all transports created by this module.  If
687 	 * ip_authority isn't set, we'll compute authorities on the fly.
688 	 */
689 	if ((auth = fmd_prop_get_string(hdl, "ip_authority")) != NULL) {
690 		(void) nvlist_alloc(&ip_auth, 0, 0);
691 		(void) nvlist_add_uint8(ip_auth,
692 		    FM_VERSION, FM_FMRI_AUTH_VERSION);
693 
694 		s = alloca(strlen(auth) + 1);
695 		(void) strcpy(s, auth);
696 		fmd_prop_free_string(hdl, auth);
697 
698 		for (p = strtok_r(s, ",", &q); p != NULL;
699 		    p = strtok_r(NULL, ",", &q)) {
700 
701 			if ((r = strchr(p, '=')) == NULL) {
702 				fmd_prop_free_string(hdl, ip_host);
703 				fmd_prop_free_string(hdl, ip_port);
704 				freeaddrinfo(ip_ail);
705 
706 				fmd_hdl_abort(hdl, "ip_authority element <%s> "
707 				    "must be in <name>=<value> form\n", p);
708 			}
709 
710 			*r = '\0';
711 			(void) nvlist_add_string(ip_auth, p, r + 1);
712 			*r = '=';
713 		}
714 	}
715 
716 	/*
717 	 * Call ip_xprt_setup() to connect or bind.  If it fails and ip_retry
718 	 * is non-zero, install a timer to try again after 'ip_sleep' nsecs.
719 	 */
720 	if (ip_xprt_setup(hdl) != 0)
721 		(void) fmd_timer_install(hdl, NULL, NULL, ip_sleep);
722 }
723 
724 void
725 _fmd_fini(fmd_hdl_t *hdl)
726 {
727 	ip_quit++; /* set quit flag before signalling auxiliary threads */
728 
729 	while (ip_xps != NULL)
730 		ip_xprt_destroy(ip_xps);
731 
732 	if (ip_auth != NULL)
733 		nvlist_free(ip_auth);
734 	if (ip_ail != NULL)
735 		freeaddrinfo(ip_ail);
736 
737 	fmd_prop_free_string(hdl, ip_host);
738 	fmd_prop_free_string(hdl, ip_port);
739 
740 	fmd_hdl_unregister(hdl);
741 }
742