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