xref: /illumos-gate/usr/src/cmd/fm/modules/common/ip-transport/ip.c (revision a38ddfee9c8c6b6c5a2947ff52fd2338362a4444)
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 2006 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <sys/types.h>
30 #include <sys/socket.h>
31 #include <sys/sysmacros.h>
32 #include <sys/fm/protocol.h>
33 
34 #include <netinet/in.h>
35 #include <arpa/inet.h>
36 
37 #include <strings.h>
38 #include <unistd.h>
39 #include <pthread.h>
40 #include <alloca.h>
41 #include <fcntl.h>
42 #include <errno.h>
43 #include <netdb.h>
44 #include <poll.h>
45 
46 #include <fm/fmd_api.h>
47 
48 #define	IP_MAGIC	"\177FMA" /* magic string identifying a packet header */
49 #define	IP_MAGLEN	4	/* length of magic string */
50 
51 typedef struct ip_hdr {
52 	char iph_magic[IP_MAGLEN]; /* magic string */
53 	uint32_t iph_size;	/* packed size */
54 } ip_hdr_t;
55 
56 typedef struct ip_buf {
57 	void *ipb_buf;		/* data buffer */
58 	size_t ipb_size;	/* size of buffer */
59 } ip_buf_t;
60 
61 typedef struct ip_xprt {
62 	fmd_xprt_t *ipx_xprt;	/* transport handle */
63 	int ipx_flags;		/* transport flags */
64 	int ipx_fd;		/* socket file descriptor */
65 	int ipx_done;		/* flag indicating connection closed */
66 	pthread_t ipx_tid;	/* recv-side auxiliary thread */
67 	ip_buf_t ipx_sndbuf;	/* buffer for sending events */
68 	ip_buf_t ipx_rcvbuf;	/* buffer for receiving events */
69 	struct ip_xprt *ipx_next; /* next ip_xprt in global list */
70 } ip_xprt_t;
71 
72 typedef struct ip_stat {
73 	fmd_stat_t ips_accfail;	/* failed accepts */
74 	fmd_stat_t ips_badmagic; /* invalid packet headers */
75 	fmd_stat_t ips_packfail; /* failed packs */
76 	fmd_stat_t ips_unpackfail; /* failed unpacks */
77 } ip_stat_t;
78 
79 static void ip_xprt_create(fmd_xprt_t *, int, int);
80 static void ip_xprt_destroy(ip_xprt_t *);
81 
82 static ip_stat_t ip_stat = {
83 	{ "accfail", FMD_TYPE_UINT64, "failed accepts" },
84 	{ "badmagic", FMD_TYPE_UINT64, "invalid packet headers" },
85 	{ "packfail", FMD_TYPE_UINT64, "failed packs" },
86 	{ "unpackfail", FMD_TYPE_UINT64, "failed unpacks" },
87 };
88 
89 static fmd_hdl_t *ip_hdl;	/* module handle */
90 static pthread_mutex_t ip_lock;	/* lock for ip_xps list */
91 static ip_xprt_t *ip_xps;	/* list of active transports */
92 static nvlist_t *ip_auth;	/* authority to use for transport(s) */
93 static size_t ip_size;		/* default buffer size */
94 static volatile int ip_quit;	/* signal to quit */
95 static int ip_qlen;		/* queue length for listen(3SOCKET) */
96 static int ip_mtbf;		/* mtbf for simulating packet drop */
97 static hrtime_t ip_burp;	/* make mtbf slower by adding this much delay */
98 static int ip_translate;	/* call fmd_xprt_translate() before sending */
99 static char *ip_host;		/* host to connect to (or NULL if server) */
100 static char *ip_port;		/* port to connect to (or bind to if server) */
101 static struct addrinfo *ip_ail; /* addr info list for ip_host/ip_port */
102 static uint_t ip_retry;		/* retry count for ip_xprt_setup() */
103 static hrtime_t ip_sleep;	/* sleep delay for ip_xprt_setup() */
104 
105 /*
106  * Allocate space in ipx_sndbuf for a header and a packed XDR encoding of
107  * the specified nvlist, and then send the buffer to our remote peer.
108  */
109 /*ARGSUSED*/
110 static int
111 ip_xprt_send(fmd_hdl_t *hdl, fmd_xprt_t *xp, fmd_event_t *ep, nvlist_t *nvl)
112 {
113 	ip_xprt_t *ipx = fmd_xprt_getspecific(hdl, xp);
114 
115 	size_t size, nvsize;
116 	char *buf, *nvbuf;
117 	ip_hdr_t *iph;
118 	ssize_t r, n;
119 	int err;
120 
121 	/*
122 	 * For testing purposes, if ip_mtbf is non-zero, use this to pseudo-
123 	 * randomly simulate the need for retries.  If ip_burp is also set,
124 	 * then we also suspend the transport for a bit and wake it up again.
125 	 */
126 	if (ip_mtbf != 0 && gethrtime() % ip_mtbf == 0) {
127 		if (ip_burp != 0) {
128 			fmd_hdl_debug(ip_hdl, "burping ipx %p", (void *)ipx);
129 			ipx->ipx_flags |= FMD_XPRT_SUSPENDED;
130 			(void) fmd_timer_install(ip_hdl, ipx, NULL, ip_burp);
131 			fmd_xprt_suspend(ip_hdl, xp);
132 		}
133 		return (FMD_SEND_RETRY);
134 	}
135 
136 	if (ip_translate && (nvl = fmd_xprt_translate(hdl, xp, ep)) == NULL) {
137 		fmd_hdl_error(hdl, "failed to translate event %p", (void *)ep);
138 		return (FMD_SEND_FAILED);
139 	}
140 
141 	(void) nvlist_size(nvl, &nvsize, NV_ENCODE_XDR);
142 	size = r = sizeof (ip_hdr_t) + nvsize;
143 
144 	if (ipx->ipx_sndbuf.ipb_size < size) {
145 		fmd_hdl_free(hdl, ipx->ipx_sndbuf.ipb_buf,
146 		    ipx->ipx_sndbuf.ipb_size);
147 		ipx->ipx_sndbuf.ipb_size = P2ROUNDUP(size, 16);
148 		ipx->ipx_sndbuf.ipb_buf = fmd_hdl_alloc(hdl,
149 		    ipx->ipx_sndbuf.ipb_size, FMD_SLEEP);
150 	}
151 
152 	buf = ipx->ipx_sndbuf.ipb_buf;
153 	iph = (ip_hdr_t *)(uintptr_t)buf;
154 	nvbuf = buf + sizeof (ip_hdr_t);
155 
156 	bcopy(IP_MAGIC, iph->iph_magic, IP_MAGLEN);
157 	iph->iph_size = htonl(nvsize);
158 	err = nvlist_pack(nvl, &nvbuf, &nvsize, NV_ENCODE_XDR, 0);
159 
160 	if (ip_translate)
161 		nvlist_free(nvl);
162 
163 	if (err != 0) {
164 		fmd_hdl_error(ip_hdl, "failed to pack event for "
165 		    "transport %p: %s\n", (void *)ipx->ipx_xprt, strerror(err));
166 		ip_stat.ips_packfail.fmds_value.ui64++;
167 		return (FMD_SEND_FAILED);
168 	}
169 
170 	while (!ip_quit && r != 0) {
171 		if ((n = send(ipx->ipx_fd, buf, r, 0)) < 0) {
172 			if (errno != EINTR && errno != EWOULDBLOCK) {
173 				fmd_hdl_debug(ip_hdl,
174 				    "failed to send on ipx %p", (void *)ipx);
175 				return (FMD_SEND_FAILED);
176 			}
177 			continue;
178 		}
179 		buf += n;
180 		r -= n;
181 	}
182 
183 	return (FMD_SEND_SUCCESS);
184 }
185 
186 /*
187  * Receive a chunk of data of the specified size from our remote peer.  The
188  * data is received into ipx_rcvbuf, and then a pointer to the buffer is
189  * returned.  NOTE: The data is only valid until the next call to ip_xprt_recv.
190  * If the connection breaks or ip_quit is set during receive, NULL is returned.
191  */
192 static void *
193 ip_xprt_recv(ip_xprt_t *ipx, size_t size)
194 {
195 	char *buf = ipx->ipx_rcvbuf.ipb_buf;
196 	ssize_t n, r = size;
197 
198 	if (ipx->ipx_rcvbuf.ipb_size < size) {
199 		fmd_hdl_free(ip_hdl, ipx->ipx_rcvbuf.ipb_buf,
200 		    ipx->ipx_rcvbuf.ipb_size);
201 		ipx->ipx_rcvbuf.ipb_size = P2ROUNDUP(size, 16);
202 		ipx->ipx_rcvbuf.ipb_buf = buf = fmd_hdl_alloc(ip_hdl,
203 		    ipx->ipx_rcvbuf.ipb_size, FMD_SLEEP);
204 	}
205 
206 	while (!ip_quit && r != 0) {
207 		if ((n = recv(ipx->ipx_fd, buf, r, MSG_WAITALL)) == 0) {
208 			ipx->ipx_done++;
209 			return (NULL);
210 		}
211 
212 		if (n < 0) {
213 			if (errno != EINTR && errno != EWOULDBLOCK) {
214 				fmd_hdl_debug(ip_hdl,
215 				    "failed to recv on ipx %p", (void *)ipx);
216 			}
217 			continue;
218 		}
219 
220 		buf += n;
221 		r -= n;
222 	}
223 
224 	return (r ? NULL: ipx->ipx_rcvbuf.ipb_buf);
225 }
226 
227 static nvlist_t *
228 ip_xprt_auth(const struct sockaddr *sap)
229 {
230 	const struct sockaddr_in6 *sin6 = (const void *)sap;
231 	const struct sockaddr_in *sin = (const void *)sap;
232 
233 	char buf[INET6_ADDRSTRLEN + 16];
234 	struct in_addr v4addr;
235 	in_port_t port;
236 
237 	nvlist_t *nvl;
238 	size_t n;
239 	int err;
240 
241 	if (ip_auth != NULL)
242 		err = nvlist_dup(ip_auth, &nvl, 0);
243 	else
244 		err = nvlist_alloc(&nvl, 0, 0);
245 
246 	if (err != 0) {
247 		fmd_hdl_abort(ip_hdl, "failed to create nvlist for "
248 		    "authority: %s\n", strerror(err));
249 	}
250 
251 	if (ip_auth != NULL)
252 		return (nvl);
253 
254 	if (sap->sa_family == AF_INET6 &&
255 	    IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
256 		IN6_V4MAPPED_TO_INADDR(&sin6->sin6_addr, &v4addr);
257 		(void) inet_ntop(AF_INET, &v4addr, buf, sizeof (buf));
258 		port = ntohs(sin6->sin6_port);
259 	} else if (sap->sa_family == AF_INET6) {
260 		(void) inet_ntop(AF_INET6, &sin6->sin6_addr, buf, sizeof (buf));
261 		port = ntohs(sin6->sin6_port);
262 	} else {
263 		(void) inet_ntop(AF_INET, &sin->sin_addr, buf, sizeof (buf));
264 		port = ntohs(sin->sin_port);
265 	}
266 
267 	n = strlen(buf);
268 	(void) snprintf(buf + n, sizeof (buf) - n, ":%u", port);
269 	fmd_hdl_debug(ip_hdl, "ip_authority %s=%s\n", FM_FMRI_AUTH_SERVER, buf);
270 
271 	(void) nvlist_add_uint8(nvl, FM_VERSION, FM_FMRI_AUTH_VERSION);
272 	(void) nvlist_add_string(nvl, FM_FMRI_AUTH_SERVER, buf);
273 
274 	return (nvl);
275 }
276 
277 static void
278 ip_xprt_accept(ip_xprt_t *ipx)
279 {
280 	struct sockaddr_storage sa;
281 	socklen_t salen = sizeof (sa);
282 	fmd_xprt_t *xp;
283 	int fd;
284 
285 	if ((fd = accept(ipx->ipx_fd, (struct sockaddr *)&sa, &salen)) == -1) {
286 		fmd_hdl_error(ip_hdl, "failed to accept connection");
287 		ip_stat.ips_accfail.fmds_value.ui64++;
288 		return;
289 	}
290 
291 	xp = fmd_xprt_open(ip_hdl, ipx->ipx_flags,
292 	    ip_xprt_auth((struct sockaddr *)&sa), NULL);
293 	ip_xprt_create(xp, fd, ipx->ipx_flags);
294 }
295 
296 static void
297 ip_xprt_recv_event(ip_xprt_t *ipx)
298 {
299 	ip_hdr_t *iph;
300 	nvlist_t *nvl;
301 	size_t size;
302 	void *buf;
303 	int err;
304 
305 	if ((iph = ip_xprt_recv(ipx, sizeof (ip_hdr_t))) == NULL)
306 		return; /* connection broken */
307 
308 	if (bcmp(iph->iph_magic, IP_MAGIC, IP_MAGLEN) != 0) {
309 		fmd_hdl_error(ip_hdl,
310 		    "invalid hdr magic %x.%x.%x.%x from transport %p\n",
311 		    iph->iph_magic[0], iph->iph_magic[1], iph->iph_magic[2],
312 		    iph->iph_magic[3], (void *)ipx->ipx_xprt);
313 		ip_stat.ips_badmagic.fmds_value.ui64++;
314 		return;
315 	}
316 
317 	size = ntohl(iph->iph_size);
318 
319 	if ((buf = ip_xprt_recv(ipx, size)) == NULL)
320 		return; /* connection broken */
321 
322 	if ((err = nvlist_unpack(buf, size, &nvl, 0)) != 0) {
323 		fmd_hdl_error(ip_hdl, "failed to unpack event from "
324 		    "transport %p: %s\n", (void *)ipx->ipx_xprt, strerror(err));
325 		ip_stat.ips_unpackfail.fmds_value.ui64++;
326 	} else
327 		fmd_xprt_post(ip_hdl, ipx->ipx_xprt, nvl, 0);
328 
329 	if (fmd_xprt_error(ip_hdl, ipx->ipx_xprt)) {
330 		fmd_hdl_error(ip_hdl, "protocol error on transport %p",
331 		    (void *)ipx->ipx_xprt);
332 		ipx->ipx_done++;
333 	}
334 }
335 
336 static void
337 ip_xprt_thread(void *arg)
338 {
339 	ip_xprt_t *ipx = arg;
340 	struct sockaddr_storage sa;
341 	socklen_t salen = sizeof (sa);
342 	struct pollfd pfd;
343 	id_t id;
344 
345 	while (!ip_quit && !ipx->ipx_done) {
346 		if (ipx->ipx_xprt != NULL || (ipx->ipx_flags & FMD_XPRT_ACCEPT))
347 			pfd.events = POLLIN;
348 		else
349 			pfd.events = POLLOUT;
350 
351 		pfd.fd = ipx->ipx_fd;
352 		pfd.revents = 0;
353 
354 		if (poll(&pfd, 1, -1) <= 0)
355 			continue; /* loop around and check ip_quit */
356 
357 		if (pfd.revents & (POLLHUP | POLLERR)) {
358 			fmd_hdl_debug(ip_hdl, "hangup fd %d\n", ipx->ipx_fd);
359 			break;
360 		}
361 
362 		if (pfd.revents & POLLOUT) {
363 			/*
364 			 * Once we're connected, there's no reason to have our
365 			 * calls to recv() and send() be non-blocking since we
366 			 * we have separate threads for each: clear O_NONBLOCK.
367 			 */
368 			(void) fcntl(ipx->ipx_fd, F_SETFL,
369 			    fcntl(ipx->ipx_fd, F_GETFL, 0) & ~O_NONBLOCK);
370 
371 			if (getpeername(ipx->ipx_fd, (struct sockaddr *)&sa,
372 			    &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, ipx->ipx_flags,
379 			    ip_xprt_auth((struct sockaddr *)&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