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 (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. 24 */ 25 26 /* 27 * The core of ilbd daemon is a single-threaded event loop using 28 * event completion framework; it receives requests from client using 29 * the libilb functions, handles timeouts, initiates health checks, and 30 * populates the kernel state. 31 * 32 * The daemon has the following privileges (in addition to the basic ones): 33 * 34 * PRIV_PROC_OWNER, PRIV_NET_ICMPACCESS, 35 * PRIV_SYS_IP_CONFIG, PRIV_PROC_AUDIT 36 * 37 * The aforementioned privileges will be specified in the SMF manifest. 38 * 39 * AF_UNIX socket is used for IPC between libilb and this daemon as 40 * both processes will run on the same machine. 41 * 42 * To do health check, the daemon will create a timer for every health 43 * check probe. Each of these timers will be associated with the 44 * event port. When a timer goes off, the daemon will initiate a 45 * pipe to a separate process to execute the specific health check 46 * probe. This new process will run with the same user-id as that of 47 * ilbd daemon and will inherit all the privileges from the ilbd 48 * daemon parent process except the following: 49 * 50 * PRIV_PROC_OWNER, PRIV_PROC_AUDIT 51 * 52 * All health checks, will be implemented as external methods 53 * (binary or script). The following arguments will be passed 54 * to external methods: 55 * 56 * $1 VIP (literal IPv4 or IPv6 address) 57 * $2 Server IP (literal IPv4 or IPv6 address) 58 * $3 Protocol (UDP, TCP as a string) 59 * $4 The load balance mode, "DSR", "NAT", "HALF_NAT" 60 * $5 Numeric port range 61 * $6 maximum time (in seconds) the method 62 * should wait before returning failure. If the method runs for 63 * longer, it may be killed, and the test considered failed. 64 * 65 * Upon success, a health check method should print the RTT to the 66 * it finds to its STDOUT for ilbd to consume. The implicit unit 67 * is microseconds but only the number needs to be printed. If it 68 * cannot find the RTT, it should print 0. If the method decides 69 * that the server is dead, it should print -1 to its STDOUT. 70 * 71 * By default, an user-supplied health check probe process will 72 * also run with the same set of privileges as ILB's built-in 73 * probes. If the administrator has an user-supplied health check 74 * program that requires a larger privilege set, he/she will have 75 * to implement setuid program. 76 * 77 * Each health check will have a timeout, such that if the health 78 * check process is hung, it will be killed after the timeout interval 79 * and the daemon will notify the kernel ILB engine of the server's 80 * unresponsiveness, so that load distribution can be appropriately 81 * adjusted. If on the other hand the health check is successful 82 * the timeout timer is cancelled. 83 */ 84 85 #include <stdio.h> 86 #include <stdlib.h> 87 #include <strings.h> 88 #include <libgen.h> 89 #include <fcntl.h> 90 #include <stddef.h> 91 #include <signal.h> 92 #include <port.h> 93 #include <ctype.h> 94 #include <sys/types.h> 95 #include <sys/wait.h> 96 #include <sys/stat.h> 97 #include <sys/note.h> 98 #include <sys/resource.h> 99 #include <unistd.h> 100 #include <sys/socket.h> 101 #include <errno.h> 102 #include <ucred.h> 103 #include <priv_utils.h> 104 #include <net/if.h> 105 #include <libilb.h> 106 #include <assert.h> 107 #include <inet/ilb.h> 108 #include <libintl.h> 109 #include <fcntl.h> 110 #include <rpcsvc/daemon_utils.h> 111 #include "libilb_impl.h" 112 #include "ilbd.h" 113 114 /* 115 * NOTE: The following needs to be kept up to date. 116 */ 117 #define ILBD_VERSION "1.0" 118 #define ILBD_COPYRIGHT \ 119 "Copyright (c) 2005, 2010, Oracle and/or its affiliates. " \ 120 "All rights reserved.\n" 121 122 /* 123 * Global reply buffer to client request. Note that ilbd is single threaded, 124 * so a global buffer is OK. If ilbd becomes multi-threaded, this needs to 125 * be changed. 126 */ 127 static uint32_t reply_buf[ILBD_MSG_SIZE / sizeof (uint32_t)]; 128 129 static void 130 ilbd_free_cli(ilbd_client_t *cli) 131 { 132 (void) close(cli->cli_sd); 133 if (cli->cli_cmd == ILBD_SHOW_NAT) 134 ilbd_show_nat_cleanup(); 135 if (cli->cli_cmd == ILBD_SHOW_PERSIST) 136 ilbd_show_sticky_cleanup(); 137 if (cli->cli_saved_reply != NULL) 138 free(cli->cli_saved_reply); 139 free(cli->cli_pw_buf); 140 free(cli); 141 } 142 143 static void 144 ilbd_reset_kernel_state(void) 145 { 146 ilb_status_t rc; 147 ilb_name_cmd_t kcmd; 148 149 kcmd.cmd = ILB_DESTROY_RULE; 150 kcmd.flags = ILB_RULE_ALLRULES; 151 kcmd.name[0] = '\0'; 152 153 rc = do_ioctl(&kcmd, 0); 154 if (rc != ILB_STATUS_OK) 155 logdebug("ilbd_reset_kernel_state: do_ioctl failed: %s", 156 strerror(errno)); 157 } 158 159 /* Signal handler to do clean up. */ 160 /* ARGSUSED */ 161 static void 162 ilbd_cleanup(int sig) 163 { 164 (void) remove(SOCKET_PATH); 165 ilbd_reset_kernel_state(); 166 exit(0); 167 } 168 169 /* 170 * Create a socket and return it to caller. If there is a failure, this 171 * function calls exit(2). Hence it always returns a valid listener socket. 172 * 173 * Note that this function is called before ilbd becomes a daemon. So 174 * we call perror(3C) to print out error message directly so that SMF can 175 * catch them. 176 */ 177 static int 178 ilbd_create_client_socket(void) 179 { 180 int s; 181 mode_t omask; 182 struct sockaddr_un sa; 183 int sobufsz; 184 185 s = socket(PF_UNIX, SOCK_SEQPACKET, 0); 186 if (s == -1) { 187 perror("ilbd_create_client_socket: socket to" 188 " client failed"); 189 exit(errno); 190 } 191 if (fcntl(s, F_SETFD, FD_CLOEXEC) == -1) { 192 perror("ilbd_create_client_socket: fcntl(FD_CLOEXEC)"); 193 exit(errno); 194 } 195 196 sobufsz = ILBD_MSG_SIZE; 197 if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, &sobufsz, 198 sizeof (sobufsz)) != 0) { 199 perror("ilbd_creat_client_socket: setsockopt(SO_SNDBUF) " 200 "failed"); 201 exit(errno); 202 } 203 if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &sobufsz, 204 sizeof (sobufsz)) != 0) { 205 perror("ilbd_creat_client_socket: setsockopt(SO_RCVBUF) " 206 "failed"); 207 exit(errno); 208 } 209 210 /* 211 * since everybody can talk to us, we need to open up permissions 212 * we check peer privileges on a per-operation basis. 213 * This is no security issue as long as we're single-threaded. 214 */ 215 omask = umask(0); 216 217 /* just in case we didn't clean up properly after last exit */ 218 (void) remove(SOCKET_PATH); 219 220 bzero(&sa, sizeof (sa)); 221 sa.sun_family = AF_UNIX; 222 (void) strlcpy(sa.sun_path, SOCKET_PATH, sizeof (sa.sun_path)); 223 224 if (bind(s, (struct sockaddr *)&sa, sizeof (sa)) != 0) { 225 perror("ilbd_create_client_socket(): bind to client" 226 " socket failed"); 227 exit(errno); 228 } 229 230 /* re-instate old umask */ 231 (void) umask(omask); 232 233 #define QLEN 16 234 235 if (listen(s, QLEN) != 0) { 236 perror("ilbd_create_client_socket: listen to client" 237 " socket failed"); 238 exit(errno); 239 } 240 241 (void) signal(SIGHUP, SIG_IGN); 242 (void) signal(SIGPIPE, SIG_IGN); 243 (void) signal(SIGSTOP, SIG_IGN); 244 (void) signal(SIGTSTP, SIG_IGN); 245 (void) signal(SIGTTIN, SIG_IGN); 246 (void) signal(SIGTTOU, SIG_IGN); 247 248 (void) signal(SIGINT, ilbd_cleanup); 249 (void) signal(SIGTERM, ilbd_cleanup); 250 (void) signal(SIGQUIT, ilbd_cleanup); 251 252 return (s); 253 } 254 255 /* 256 * Return the minimum size of a given request. The returned size does not 257 * include the variable part of a request. 258 */ 259 static size_t 260 ilbd_cmd_size(const ilb_comm_t *ic) 261 { 262 size_t cmd_sz; 263 264 cmd_sz = sizeof (*ic); 265 switch (ic->ic_cmd) { 266 case ILBD_RETRIEVE_SG_NAMES: 267 case ILBD_RETRIEVE_RULE_NAMES: 268 case ILBD_RETRIEVE_HC_NAMES: 269 case ILBD_CMD_OK: 270 break; 271 case ILBD_CMD_ERROR: 272 cmd_sz += sizeof (ilb_status_t); 273 break; 274 case ILBD_RETRIEVE_SG_HOSTS: 275 case ILBD_CREATE_SERVERGROUP: 276 case ILBD_DESTROY_SERVERGROUP: 277 case ILBD_DESTROY_RULE: 278 case ILBD_ENABLE_RULE: 279 case ILBD_DISABLE_RULE: 280 case ILBD_RETRIEVE_RULE: 281 case ILBD_DESTROY_HC: 282 case ILBD_GET_HC_INFO: 283 case ILBD_GET_HC_SRVS: 284 cmd_sz += sizeof (ilbd_name_t); 285 break; 286 case ILBD_ENABLE_SERVER: 287 case ILBD_DISABLE_SERVER: 288 case ILBD_ADD_SERVER_TO_GROUP: 289 case ILBD_REM_SERVER_FROM_GROUP: 290 cmd_sz += sizeof (ilb_sg_info_t); 291 break; 292 case ILBD_SRV_ADDR2ID: 293 case ILBD_SRV_ID2ADDR: 294 cmd_sz += sizeof (ilb_sg_info_t) + sizeof (ilb_sg_srv_t); 295 break; 296 case ILBD_CREATE_RULE: 297 cmd_sz += sizeof (ilb_rule_info_t); 298 break; 299 case ILBD_CREATE_HC: 300 cmd_sz += sizeof (ilb_hc_info_t); 301 break; 302 case ILBD_SHOW_NAT: 303 case ILBD_SHOW_PERSIST: 304 cmd_sz += sizeof (ilb_show_info_t); 305 break; 306 } 307 308 return (cmd_sz); 309 } 310 311 /* 312 * Given a request and its size, check that the size is big enough to 313 * contain the variable part of a request. 314 */ 315 static ilb_status_t 316 ilbd_check_req_size(ilb_comm_t *ic, size_t ic_sz) 317 { 318 ilb_status_t rc = ILB_STATUS_OK; 319 ilb_sg_info_t *sg_info; 320 ilbd_namelist_t *nlist; 321 322 switch (ic->ic_cmd) { 323 case ILBD_CREATE_SERVERGROUP: 324 case ILBD_ENABLE_SERVER: 325 case ILBD_DISABLE_SERVER: 326 case ILBD_ADD_SERVER_TO_GROUP: 327 case ILBD_REM_SERVER_FROM_GROUP: 328 sg_info = (ilb_sg_info_t *)&ic->ic_data; 329 330 if (ic_sz < ilbd_cmd_size(ic) + sg_info->sg_srvcount * 331 sizeof (ilb_sg_srv_t)) { 332 rc = ILB_STATUS_EINVAL; 333 } 334 break; 335 case ILBD_ENABLE_RULE: 336 case ILBD_DISABLE_RULE: 337 case ILBD_DESTROY_RULE: 338 nlist = (ilbd_namelist_t *)&ic->ic_data; 339 340 if (ic_sz < ilbd_cmd_size(ic) + nlist->ilbl_count * 341 sizeof (ilbd_name_t)) { 342 rc = ILB_STATUS_EINVAL; 343 } 344 break; 345 } 346 return (rc); 347 } 348 349 /* 350 * this function *relies* on a complete message/data struct 351 * being passed in (currently via the SOCK_SEQPACKET socket type). 352 * 353 * Note that the size of ip is at most ILBD_MSG_SIZE. 354 */ 355 static ilb_status_t 356 consume_common_struct(ilb_comm_t *ic, size_t ic_sz, ilbd_client_t *cli, 357 int ev_port) 358 { 359 ilb_status_t rc; 360 struct passwd *ps; 361 size_t rbufsz; 362 ssize_t ret; 363 boolean_t standard_reply = B_TRUE; 364 ilbd_name_t name; 365 366 /* 367 * cli_ev must be overridden during handling of individual commands, 368 * if there's a special need; otherwise, leave this for 369 * the "default" case 370 */ 371 cli->cli_ev = ILBD_EVENT_REQ; 372 373 ps = &cli->cli_pw; 374 rbufsz = ILBD_MSG_SIZE; 375 376 /* Sanity check on the size of the static part of a request. */ 377 if (ic_sz < ilbd_cmd_size(ic)) { 378 rc = ILB_STATUS_EINVAL; 379 goto out; 380 } 381 382 switch (ic->ic_cmd) { 383 case ILBD_CREATE_SERVERGROUP: { 384 ilb_sg_info_t sg_info; 385 386 /* 387 * ilbd_create_sg() only needs the sg_name field. But it 388 * takes in a ilb_sg_info_t because it is used as a callback 389 * in ilbd_walk_sg_pgs(). 390 */ 391 (void) strlcpy(sg_info.sg_name, (char *)&(ic->ic_data), 392 sizeof (sg_info.sg_name)); 393 rc = ilbd_create_sg(&sg_info, ev_port, ps, 394 cli->cli_peer_ucredp); 395 break; 396 } 397 398 case ILBD_DESTROY_SERVERGROUP: 399 (void) strlcpy(name, (char *)&(ic->ic_data), sizeof (name)); 400 rc = ilbd_destroy_sg(name, ps, cli->cli_peer_ucredp); 401 break; 402 403 case ILBD_ADD_SERVER_TO_GROUP: 404 if ((rc = ilbd_check_req_size(ic, ic_sz)) != ILB_STATUS_OK) 405 break; 406 rc = ilbd_add_server_to_group((ilb_sg_info_t *)&ic->ic_data, 407 ev_port, ps, cli->cli_peer_ucredp); 408 break; 409 410 case ILBD_REM_SERVER_FROM_GROUP: 411 if ((rc = ilbd_check_req_size(ic, ic_sz)) != ILB_STATUS_OK) 412 break; 413 rc = ilbd_rem_server_from_group((ilb_sg_info_t *)&ic->ic_data, 414 ev_port, ps, cli->cli_peer_ucredp); 415 break; 416 417 case ILBD_ENABLE_SERVER: 418 if ((rc = ilbd_check_req_size(ic, ic_sz)) != ILB_STATUS_OK) 419 break; 420 rc = ilbd_enable_server((ilb_sg_info_t *)&ic->ic_data, ps, 421 cli->cli_peer_ucredp); 422 break; 423 424 case ILBD_DISABLE_SERVER: 425 if ((rc = ilbd_check_req_size(ic, ic_sz)) != ILB_STATUS_OK) 426 break; 427 rc = ilbd_disable_server((ilb_sg_info_t *)&ic->ic_data, ps, 428 cli->cli_peer_ucredp); 429 break; 430 431 case ILBD_SRV_ADDR2ID: 432 rc = ilbd_address_to_srvID((ilb_sg_info_t *)&ic->ic_data, 433 reply_buf, &rbufsz); 434 if (rc == ILB_STATUS_OK) 435 standard_reply = B_FALSE; 436 break; 437 438 case ILBD_SRV_ID2ADDR: 439 rc = ilbd_srvID_to_address((ilb_sg_info_t *)&ic->ic_data, 440 reply_buf, &rbufsz); 441 if (rc == ILB_STATUS_OK) 442 standard_reply = B_FALSE; 443 break; 444 445 case ILBD_RETRIEVE_SG_HOSTS: 446 (void) strlcpy(name, (char *)&(ic->ic_data), sizeof (name)); 447 rc = ilbd_retrieve_sg_hosts(name, reply_buf, &rbufsz); 448 if (rc == ILB_STATUS_OK) 449 standard_reply = B_FALSE; 450 break; 451 452 case ILBD_RETRIEVE_SG_NAMES: 453 case ILBD_RETRIEVE_RULE_NAMES: 454 case ILBD_RETRIEVE_HC_NAMES: 455 rc = ilbd_retrieve_names(ic->ic_cmd, reply_buf, &rbufsz); 456 if (rc == ILB_STATUS_OK) 457 standard_reply = B_FALSE; 458 break; 459 460 case ILBD_CREATE_RULE: 461 rc = ilbd_create_rule((ilb_rule_info_t *)&ic->ic_data, ev_port, 462 ps, cli->cli_peer_ucredp); 463 break; 464 465 case ILBD_DESTROY_RULE: 466 /* Copy the name to ensure that name is NULL terminated. */ 467 (void) strlcpy(name, (char *)&(ic->ic_data), sizeof (name)); 468 rc = ilbd_destroy_rule(name, ps, cli->cli_peer_ucredp); 469 break; 470 471 case ILBD_ENABLE_RULE: 472 (void) strlcpy(name, (char *)&(ic->ic_data), sizeof (name)); 473 rc = ilbd_enable_rule(name, ps, cli->cli_peer_ucredp); 474 break; 475 476 case ILBD_DISABLE_RULE: 477 (void) strlcpy(name, (char *)&(ic->ic_data), sizeof (name)); 478 rc = ilbd_disable_rule(name, ps, cli->cli_peer_ucredp); 479 break; 480 481 case ILBD_RETRIEVE_RULE: 482 (void) strlcpy(name, (char *)&(ic->ic_data), sizeof (name)); 483 rc = ilbd_retrieve_rule(name, reply_buf, &rbufsz); 484 if (rc == ILB_STATUS_OK) 485 standard_reply = B_FALSE; 486 break; 487 488 case ILBD_CREATE_HC: 489 rc = ilbd_create_hc((ilb_hc_info_t *)&ic->ic_data, ev_port, ps, 490 cli->cli_peer_ucredp); 491 break; 492 493 case ILBD_DESTROY_HC: 494 (void) strlcpy(name, (char *)&(ic->ic_data), sizeof (name)); 495 rc = ilbd_destroy_hc(name, ps, cli->cli_peer_ucredp); 496 break; 497 498 case ILBD_GET_HC_INFO: 499 (void) strlcpy(name, (char *)&(ic->ic_data), sizeof (name)); 500 rc = ilbd_get_hc_info(name, reply_buf, &rbufsz); 501 if (rc == ILB_STATUS_OK) 502 standard_reply = B_FALSE; 503 break; 504 505 case ILBD_GET_HC_SRVS: 506 (void) strlcpy(name, (char *)&(ic->ic_data), sizeof (name)); 507 rc = ilbd_get_hc_srvs(name, reply_buf, &rbufsz); 508 if (rc == ILB_STATUS_OK) 509 standard_reply = B_FALSE; 510 break; 511 512 case ILBD_SHOW_NAT: 513 rc = ilbd_show_nat(cli, ic, reply_buf, &rbufsz); 514 if (rc == ILB_STATUS_OK) 515 standard_reply = B_FALSE; 516 break; 517 518 case ILBD_SHOW_PERSIST: 519 rc = ilbd_show_sticky(cli, ic, reply_buf, &rbufsz); 520 if (rc == ILB_STATUS_OK) 521 standard_reply = B_FALSE; 522 break; 523 524 default: 525 logdebug("consume_common_struct: unknown command"); 526 rc = ILB_STATUS_INVAL_CMD; 527 break; 528 } 529 530 out: 531 /* 532 * The message exchange is always in pairs, request/response. If 533 * a transaction requires multiple exchanges, the client will send 534 * in multiple requests to get multiple responses. The show-nat and 535 * show-persist request are examples of this. The end of transaction 536 * is marked with ic_flags set to ILB_COMM_END. 537 */ 538 539 /* This is the standard reply. */ 540 if (standard_reply) { 541 if (rc == ILB_STATUS_OK) 542 ilbd_reply_ok(reply_buf, &rbufsz); 543 else 544 ilbd_reply_err(reply_buf, &rbufsz, rc); 545 } 546 547 if ((ret = send(cli->cli_sd, reply_buf, rbufsz, 0)) != rbufsz) { 548 if (ret == -1) { 549 if (errno != EWOULDBLOCK) { 550 logdebug("consume_common_struct: send: %s", 551 strerror(errno)); 552 rc = ILB_STATUS_SEND; 553 goto err_out; 554 } 555 /* 556 * The reply is blocked, save the reply. handle_req() 557 * will associate the event port for the re-send. 558 */ 559 assert(cli->cli_saved_reply == NULL); 560 if ((cli->cli_saved_reply = malloc(rbufsz)) == NULL) { 561 /* 562 * Set the error to ILB_STATUS_SEND so that 563 * handle_req() will free the client. 564 */ 565 logdebug("consume_common_struct: failure to " 566 "allocate memory to save reply"); 567 rc = ILB_STATUS_SEND; 568 goto err_out; 569 } 570 bcopy(reply_buf, cli->cli_saved_reply, rbufsz); 571 cli->cli_saved_size = rbufsz; 572 return (ILB_STATUS_EWOULDBLOCK); 573 } 574 } 575 err_out: 576 return (rc); 577 } 578 579 /* 580 * Accept a new client request. A struct ilbd_client_t is allocated to 581 * store the client info. The accepted socket is port_associate() with 582 * the given port. And the allocated ilbd_client_t struct is passed as 583 * the user pointer. 584 */ 585 static void 586 new_req(int ev_port, int listener, void *ev_obj) 587 { 588 struct sockaddr sa; 589 int sa_len; 590 int new_sd; 591 int sflags; 592 ilbd_client_t *cli; 593 int res; 594 uid_t uid; 595 596 sa_len = sizeof (sa); 597 if ((new_sd = accept(listener, &sa, &sa_len)) == -1) { 598 /* don't log if we're out of file descriptors */ 599 if (errno != EINTR && errno != EMFILE) 600 logperror("new_req: accept failed"); 601 goto done; 602 } 603 604 /* Set the new socket to be non-blocking. */ 605 if ((sflags = fcntl(new_sd, F_GETFL, 0)) == -1) { 606 logperror("new_req: fcntl(F_GETFL)"); 607 goto clean_up; 608 } 609 if (fcntl(new_sd, F_SETFL, sflags | O_NONBLOCK) == -1) { 610 logperror("new_req: fcntl(F_SETFL)"); 611 goto clean_up; 612 } 613 if (fcntl(new_sd, F_SETFD, FD_CLOEXEC) == -1) { 614 logperror("new_req: fcntl(FD_CLOEXEC)"); 615 goto clean_up; 616 } 617 if ((cli = calloc(1, sizeof (ilbd_client_t))) == NULL) { 618 logerr("new_req: malloc(ilbd_client_t)"); 619 goto clean_up; 620 } 621 res = getpeerucred(new_sd, &cli->cli_peer_ucredp); 622 if (res == -1) { 623 logperror("new_req: getpeerucred failed"); 624 free(cli); 625 goto clean_up; 626 } 627 if ((uid = ucred_getruid(cli->cli_peer_ucredp)) == (uid_t)-1) { 628 logperror("new_req: ucred_getruid failed"); 629 free(cli); 630 goto clean_up; 631 } 632 cli->cli_pw_bufsz = (size_t)sysconf(_SC_GETPW_R_SIZE_MAX); 633 if ((cli->cli_pw_buf = malloc(cli->cli_pw_bufsz)) == NULL) { 634 free(cli); 635 logerr("new_req: malloc(cli_pw_buf)"); 636 goto clean_up; 637 } 638 if (getpwuid_r(uid, &cli->cli_pw, cli->cli_pw_buf, 639 cli->cli_pw_bufsz) == NULL) { 640 free(cli->cli_pw_buf); 641 free(cli); 642 logperror("new_req: invalid user"); 643 goto clean_up; 644 } 645 cli->cli_ev = ILBD_EVENT_REQ; 646 cli->cli_sd = new_sd; 647 cli->cli_cmd = ILBD_BAD_CMD; 648 cli->cli_saved_reply = NULL; 649 cli->cli_saved_size = 0; 650 if (port_associate(ev_port, PORT_SOURCE_FD, new_sd, POLLRDNORM, 651 cli) == -1) { 652 logperror("new_req: port_associate(cli) failed"); 653 free(cli->cli_pw_buf); 654 free(cli); 655 clean_up: 656 (void) close(new_sd); 657 } 658 659 done: 660 /* Re-associate the listener with the event port. */ 661 if (port_associate(ev_port, PORT_SOURCE_FD, listener, POLLRDNORM, 662 ev_obj) == -1) { 663 logperror("new_req: port_associate(listener) failed"); 664 exit(1); 665 } 666 } 667 668 static void 669 handle_req(int ev_port, ilbd_event_t event, ilbd_client_t *cli) 670 { 671 /* All request should be smaller than ILBD_MSG_SIZE */ 672 union { 673 ilb_comm_t ic; 674 uint32_t buf[ILBD_MSG_SIZE / sizeof (uint32_t)]; 675 } ic_u; 676 int rc = ILB_STATUS_OK; 677 ssize_t r; 678 679 if (event == ILBD_EVENT_REQ) { 680 /* 681 * Something is wrong with the client since there is a 682 * pending reply, the client should not send us another 683 * request. Kill this client. 684 */ 685 if (cli->cli_saved_reply != NULL) { 686 logerr("handle_req: misbehaving client, more than one " 687 "outstanding request"); 688 rc = ILB_STATUS_INTERNAL; 689 goto err_out; 690 } 691 692 /* 693 * Our socket is message based so we should be able 694 * to get the request in one single read. 695 */ 696 r = recv(cli->cli_sd, (void *)ic_u.buf, sizeof (ic_u.buf), 0); 697 if (r < 0) { 698 if (errno != EINTR) { 699 logperror("handle_req: read failed"); 700 rc = ILB_STATUS_READ; 701 goto err_out; 702 } 703 /* 704 * If interrupted, just re-associate the cli_sd 705 * with the port. 706 */ 707 goto done; 708 } 709 cli->cli_cmd = ic_u.ic.ic_cmd; 710 711 rc = consume_common_struct(&ic_u.ic, r, cli, ev_port); 712 if (rc == ILB_STATUS_EWOULDBLOCK) 713 goto blocked; 714 /* Fatal error communicating with client, free it. */ 715 if (rc == ILB_STATUS_SEND) 716 goto err_out; 717 } else { 718 assert(event == ILBD_EVENT_REP_OK); 719 assert(cli->cli_saved_reply != NULL); 720 721 /* 722 * The reply to client was previously blocked, we will 723 * send again. 724 */ 725 if (send(cli->cli_sd, cli->cli_saved_reply, 726 cli->cli_saved_size, 0) != cli->cli_saved_size) { 727 if (errno != EWOULDBLOCK) { 728 logdebug("handle_req: send: %s", 729 strerror(errno)); 730 rc = ILB_STATUS_SEND; 731 goto err_out; 732 } 733 goto blocked; 734 } 735 free(cli->cli_saved_reply); 736 cli->cli_saved_reply = NULL; 737 cli->cli_saved_size = 0; 738 } 739 done: 740 /* Re-associate with the event port for more requests. */ 741 cli->cli_ev = ILBD_EVENT_REQ; 742 if (port_associate(ev_port, PORT_SOURCE_FD, cli->cli_sd, 743 POLLRDNORM, cli) == -1) { 744 logperror("handle_req: port_associate(POLLRDNORM)"); 745 rc = ILB_STATUS_INTERNAL; 746 goto err_out; 747 } 748 return; 749 750 blocked: 751 /* Re-associate with the event port. */ 752 cli->cli_ev = ILBD_EVENT_REP_OK; 753 if (port_associate(ev_port, PORT_SOURCE_FD, cli->cli_sd, POLLWRNORM, 754 cli) == -1) { 755 logperror("handle_req: port_associate(POLLWRNORM)"); 756 rc = ILB_STATUS_INTERNAL; 757 goto err_out; 758 } 759 return; 760 761 err_out: 762 ilbd_free_cli(cli); 763 } 764 765 static void 766 i_ilbd_read_config(int ev_port) 767 { 768 logdebug("i_ilbd_read_config: port %d", ev_port); 769 (void) ilbd_walk_sg_pgs(ilbd_create_sg, &ev_port, NULL); 770 (void) ilbd_walk_hc_pgs(ilbd_create_hc, &ev_port, NULL); 771 (void) ilbd_walk_rule_pgs(ilbd_create_rule, &ev_port, NULL); 772 } 773 774 /* 775 * main event loop for ilbd 776 * asserts that argument 'listener' is a server socket ready to accept() on. 777 */ 778 static void 779 main_loop(int listener) 780 { 781 port_event_t p_ev; 782 int ev_port, ev_port_obj; 783 ilbd_event_obj_t ev_obj; 784 ilbd_timer_event_obj_t timer_ev_obj; 785 786 ev_port = port_create(); 787 if (ev_port == -1) { 788 logperror("main_loop: port_create failed"); 789 exit(-1); 790 } 791 ilbd_hc_timer_init(ev_port, &timer_ev_obj); 792 793 ev_obj.ev = ILBD_EVENT_NEW_REQ; 794 if (port_associate(ev_port, PORT_SOURCE_FD, listener, POLLRDNORM, 795 &ev_obj) == -1) { 796 logperror("main_loop: port_associate failed"); 797 exit(1); 798 } 799 800 i_ilbd_read_config(ev_port); 801 ilbd_hc_timer_update(&timer_ev_obj); 802 803 _NOTE(CONSTCOND) 804 while (B_TRUE) { 805 int r; 806 ilbd_event_t event; 807 ilbd_client_t *cli; 808 809 r = port_get(ev_port, &p_ev, NULL); 810 if (r == -1) { 811 if (errno == EINTR) 812 continue; 813 logperror("main_loop: port_get failed"); 814 break; 815 } 816 817 ev_port_obj = p_ev.portev_object; 818 event = ((ilbd_event_obj_t *)p_ev.portev_user)->ev; 819 820 switch (event) { 821 case ILBD_EVENT_TIMER: 822 ilbd_hc_timeout(); 823 break; 824 825 case ILBD_EVENT_PROBE: 826 ilbd_hc_probe_return(ev_port, ev_port_obj, 827 p_ev.portev_events, 828 (ilbd_hc_probe_event_t *)p_ev.portev_user); 829 break; 830 831 case ILBD_EVENT_NEW_REQ: 832 assert(ev_port_obj == listener); 833 /* 834 * An error happens in the listener. Exit 835 * for now.... 836 */ 837 if (p_ev.portev_events & (POLLHUP|POLLERR)) { 838 logerr("main_loop: listener error"); 839 exit(1); 840 } 841 new_req(ev_port, ev_port_obj, &ev_obj); 842 break; 843 844 case ILBD_EVENT_REP_OK: 845 case ILBD_EVENT_REQ: 846 cli = (ilbd_client_t *)p_ev.portev_user; 847 assert(ev_port_obj == cli->cli_sd); 848 849 /* 850 * An error happens in the newly accepted 851 * client request. Clean up the client. 852 * this also happens when client closes socket, 853 * so not necessarily a reason for alarm 854 */ 855 if (p_ev.portev_events & (POLLHUP|POLLERR)) { 856 ilbd_free_cli(cli); 857 break; 858 } 859 860 handle_req(ev_port, event, cli); 861 break; 862 863 default: 864 logerr("main_loop: unknown event %d", event); 865 exit(EXIT_FAILURE); 866 break; 867 } 868 869 ilbd_hc_timer_update(&timer_ev_obj); 870 } 871 } 872 873 static void 874 i_ilbd_setup_lists(void) 875 { 876 i_setup_sg_hlist(); 877 i_setup_rule_hlist(); 878 i_ilbd_setup_hc_list(); 879 } 880 881 /* 882 * Usage message - call only during startup. it will print its 883 * message on stderr and exit 884 */ 885 static void 886 Usage(char *name) 887 { 888 (void) fprintf(stderr, gettext("Usage: %s [-d|--debug]\n"), name); 889 exit(1); 890 } 891 892 static void 893 print_version(char *name) 894 { 895 (void) printf("%s %s\n", basename(name), ILBD_VERSION); 896 (void) printf(gettext(ILBD_COPYRIGHT)); 897 exit(0); 898 } 899 900 /* 901 * Increase the file descriptor limit for handling a lot of health check 902 * processes (each requires a pipe). 903 * 904 * Note that this function is called before ilbd becomes a daemon. So 905 * we call perror(3C) to print out error message directly so that SMF 906 * can catch them. 907 */ 908 static void 909 set_rlim(void) 910 { 911 struct rlimit rlp; 912 913 if (getrlimit(RLIMIT_NOFILE, &rlp) == -1) { 914 perror("ilbd: getrlimit"); 915 exit(errno); 916 } 917 rlp.rlim_cur = rlp.rlim_max; 918 if (setrlimit(RLIMIT_NOFILE, &rlp) == -1) { 919 perror("ilbd: setrlimit"); 920 exit(errno); 921 } 922 } 923 924 int 925 main(int argc, char **argv) 926 { 927 int s; 928 int c; 929 930 (void) setlocale(LC_ALL, ""); 931 #if !defined(TEXT_DOMAIN) 932 #define TEXT_DOMAIN "SYS_TEST" 933 #endif 934 static const char daemon_dir[] = DAEMON_DIR; 935 936 (void) textdomain(TEXT_DOMAIN); 937 938 while ((c = getopt(argc, argv, ":V?d(debug)")) != -1) { 939 switch ((char)c) { 940 case '?': Usage(argv[0]); 941 /* not reached */ 942 break; 943 case 'V': print_version(argv[0]); 944 /* not reached */ 945 break; 946 case 'd': ilbd_enable_debug(); 947 break; 948 default: Usage(argv[0]); 949 /* not reached */ 950 break; 951 } 952 } 953 954 /* 955 * Whenever the daemon starts, it needs to start with a clean 956 * slate in the kernel. We need sys_ip_config privilege for 957 * this. 958 */ 959 ilbd_reset_kernel_state(); 960 961 /* Increase the limit on the number of file descriptors. */ 962 set_rlim(); 963 964 /* 965 * ilbd daemon starts off as root, just so it can create 966 * /var/run/daemon if one does not exist. After that is done 967 * the daemon switches to "daemon" uid. This is similar to what 968 * rpcbind does. 969 */ 970 if (mkdir(daemon_dir, DAEMON_DIR_MODE) == 0 || errno == EEXIST) { 971 (void) chmod(daemon_dir, DAEMON_DIR_MODE); 972 (void) chown(daemon_dir, DAEMON_UID, DAEMON_GID); 973 } else { 974 perror("main: mkdir failed"); 975 exit(errno); 976 } 977 /* 978 * Now lets switch ilbd as uid = daemon, gid = daemon with a 979 * trimmed down privilege set 980 */ 981 if (__init_daemon_priv(PU_RESETGROUPS | PU_LIMITPRIVS | PU_INHERITPRIVS, 982 DAEMON_UID, DAEMON_GID, PRIV_PROC_OWNER, PRIV_PROC_AUDIT, 983 PRIV_NET_ICMPACCESS, PRIV_SYS_IP_CONFIG, NULL) == -1) { 984 (void) fprintf(stderr, "Insufficient privileges\n"); 985 exit(EXIT_FAILURE); 986 } 987 988 /* 989 * Opens a PF_UNIX socket to the client. No privilege needed 990 * for this. 991 */ 992 s = ilbd_create_client_socket(); 993 994 /* 995 * Daemonify if ilbd is not running with -d option 996 * Need proc_fork privilege for this 997 */ 998 if (!is_debugging_on()) { 999 logdebug("daemonizing..."); 1000 if (daemon(0, 0) != 0) { 1001 logperror("daemon failed"); 1002 exit(EXIT_FAILURE); 1003 } 1004 } 1005 (void) priv_set(PRIV_OFF, PRIV_INHERITABLE, PRIV_PROC_OWNER, 1006 PRIV_PROC_AUDIT, NULL); 1007 1008 /* if daemonified then set up syslog */ 1009 if (!is_debugging_on()) 1010 openlog("ilbd", LOG_PID, LOG_DAEMON); 1011 1012 i_ilbd_setup_lists(); 1013 1014 main_loop(s); 1015 1016 /* 1017 * if we come here, then we experienced an error or a shutdown 1018 * indicator, so clean up after ourselves. 1019 */ 1020 logdebug("main(): terminating"); 1021 1022 (void) remove(SOCKET_PATH); 1023 ilbd_reset_kernel_state(); 1024 1025 return (0); 1026 } 1027