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