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