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