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.
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.
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]
23 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright (c) 2016 by Delphix. All rights reserved.
28 * The core of ilbd daemon is a single-threaded event loop using
29 * event completion framework; it receives requests from client using
30 * the libilb functions, handles timeouts, initiates health checks, and
31 * populates the kernel state.
33 * The daemon has the following privileges (in addition to the basic ones):
35 * PRIV_PROC_OWNER, PRIV_NET_ICMPACCESS,
36 * PRIV_SYS_IP_CONFIG, PRIV_PROC_AUDIT
38 * The aforementioned privileges will be specified in the SMF manifest.
40 * AF_UNIX socket is used for IPC between libilb and this daemon as
41 * both processes will run on the same machine.
43 * To do health check, the daemon will create a timer for every health
44 * check probe. Each of these timers will be associated with the
45 * event port. When a timer goes off, the daemon will initiate a
46 * pipe to a separate process to execute the specific health check
47 * probe. This new process will run with the same user-id as that of
48 * ilbd daemon and will inherit all the privileges from the ilbd
49 * daemon parent process except the following:
51 * PRIV_PROC_OWNER, PRIV_PROC_AUDIT
53 * All health checks, will be implemented as external methods
54 * (binary or script). The following arguments will be passed
55 * to external methods:
57 * $1 VIP (literal IPv4 or IPv6 address)
58 * $2 Server IP (literal IPv4 or IPv6 address)
59 * $3 Protocol (UDP, TCP as a string)
60 * $4 The load balance mode, "DSR", "NAT", "HALF_NAT"
61 * $5 Numeric port range
62 * $6 maximum time (in seconds) the method
63 * should wait before returning failure. If the method runs for
64 * longer, it may be killed, and the test considered failed.
66 * Upon success, a health check method should print the RTT to the
67 * it finds to its STDOUT for ilbd to consume. The implicit unit
68 * is microseconds but only the number needs to be printed. If it
69 * cannot find the RTT, it should print 0. If the method decides
70 * that the server is dead, it should print -1 to its STDOUT.
72 * By default, an user-supplied health check probe process will
73 * also run with the same set of privileges as ILB's built-in
74 * probes. If the administrator has an user-supplied health check
75 * program that requires a larger privilege set, they will have
76 * to implement setuid program.
78 * Each health check will have a timeout, such that if the health
79 * check process is hung, it will be killed after the timeout interval
80 * and the daemon will notify the kernel ILB engine of the server's
81 * unresponsiveness, so that load distribution can be appropriately
82 * adjusted. If on the other hand the health check is successful
83 * the timeout timer is cancelled.
95 #include <sys/types.h>
99 #include <sys/resource.h>
101 #include <sys/socket.h>
104 #include <priv_utils.h>
108 #include <inet/ilb.h>
111 #include <rpcsvc/daemon_utils.h>
113 #include "libilb_impl.h"
117 * NOTE: The following needs to be kept up to date.
119 #define ILBD_VERSION "1.0"
120 #define ILBD_COPYRIGHT \
121 "Copyright (c) 2005, 2010, Oracle and/or its affiliates. " \
122 "All rights reserved.\n"
125 * Global reply buffer to client request. Note that ilbd is single threaded,
126 * so a global buffer is OK. If ilbd becomes multi-threaded, this needs to
129 static uint32_t reply_buf
[ILBD_MSG_SIZE
/ sizeof (uint32_t)];
132 ilbd_free_cli(ilbd_client_t
*cli
)
134 (void) close(cli
->cli_sd
);
135 if (cli
->cli_cmd
== ILBD_SHOW_NAT
)
136 ilbd_show_nat_cleanup();
137 if (cli
->cli_cmd
== ILBD_SHOW_PERSIST
)
138 ilbd_show_sticky_cleanup();
139 free(cli
->cli_saved_reply
);
140 if (cli
->cli_peer_ucredp
!= NULL
)
141 ucred_free(cli
->cli_peer_ucredp
);
142 free(cli
->cli_pw_buf
);
147 ilbd_reset_kernel_state(void)
152 kcmd
.cmd
= ILB_DESTROY_RULE
;
153 kcmd
.flags
= ILB_RULE_ALLRULES
;
156 rc
= do_ioctl(&kcmd
, 0);
157 if (rc
!= ILB_STATUS_OK
)
158 logdebug("ilbd_reset_kernel_state: do_ioctl failed: %s",
162 /* Signal handler to do clean up. */
165 ilbd_cleanup(int sig
)
167 (void) remove(SOCKET_PATH
);
168 ilbd_reset_kernel_state();
173 * Create a socket and return it to caller. If there is a failure, this
174 * function calls exit(2). Hence it always returns a valid listener socket.
176 * Note that this function is called before ilbd becomes a daemon. So
177 * we call perror(3C) to print out error message directly so that SMF can
181 ilbd_create_client_socket(void)
185 struct sockaddr_un sa
;
188 s
= socket(PF_UNIX
, SOCK_SEQPACKET
, 0);
190 perror("ilbd_create_client_socket: socket to"
194 if (fcntl(s
, F_SETFD
, FD_CLOEXEC
) == -1) {
195 perror("ilbd_create_client_socket: fcntl(FD_CLOEXEC)");
199 sobufsz
= ILBD_MSG_SIZE
;
200 if (setsockopt(s
, SOL_SOCKET
, SO_SNDBUF
, &sobufsz
,
201 sizeof (sobufsz
)) != 0) {
202 perror("ilbd_creat_client_socket: setsockopt(SO_SNDBUF) "
206 if (setsockopt(s
, SOL_SOCKET
, SO_RCVBUF
, &sobufsz
,
207 sizeof (sobufsz
)) != 0) {
208 perror("ilbd_creat_client_socket: setsockopt(SO_RCVBUF) "
214 * since everybody can talk to us, we need to open up permissions
215 * we check peer privileges on a per-operation basis.
216 * This is no security issue as long as we're single-threaded.
220 /* just in case we didn't clean up properly after last exit */
221 (void) remove(SOCKET_PATH
);
223 bzero(&sa
, sizeof (sa
));
224 sa
.sun_family
= AF_UNIX
;
225 (void) strlcpy(sa
.sun_path
, SOCKET_PATH
, sizeof (sa
.sun_path
));
227 if (bind(s
, (struct sockaddr
*)&sa
, sizeof (sa
)) != 0) {
228 perror("ilbd_create_client_socket(): bind to client"
233 /* re-instate old umask */
238 if (listen(s
, QLEN
) != 0) {
239 perror("ilbd_create_client_socket: listen to client"
244 (void) signal(SIGHUP
, SIG_IGN
);
245 (void) signal(SIGPIPE
, SIG_IGN
);
246 (void) signal(SIGSTOP
, SIG_IGN
);
247 (void) signal(SIGTSTP
, SIG_IGN
);
248 (void) signal(SIGTTIN
, SIG_IGN
);
249 (void) signal(SIGTTOU
, SIG_IGN
);
251 (void) signal(SIGINT
, ilbd_cleanup
);
252 (void) signal(SIGTERM
, ilbd_cleanup
);
253 (void) signal(SIGQUIT
, ilbd_cleanup
);
259 * Return the minimum size of a given request. The returned size does not
260 * include the variable part of a request.
263 ilbd_cmd_size(const ilb_comm_t
*ic
)
267 cmd_sz
= sizeof (*ic
);
268 switch (ic
->ic_cmd
) {
269 case ILBD_RETRIEVE_SG_NAMES
:
270 case ILBD_RETRIEVE_RULE_NAMES
:
271 case ILBD_RETRIEVE_HC_NAMES
:
275 cmd_sz
+= sizeof (ilb_status_t
);
277 case ILBD_RETRIEVE_SG_HOSTS
:
278 case ILBD_CREATE_SERVERGROUP
:
279 case ILBD_DESTROY_SERVERGROUP
:
280 case ILBD_DESTROY_RULE
:
281 case ILBD_ENABLE_RULE
:
282 case ILBD_DISABLE_RULE
:
283 case ILBD_RETRIEVE_RULE
:
284 case ILBD_DESTROY_HC
:
285 case ILBD_GET_HC_INFO
:
286 case ILBD_GET_HC_SRVS
:
287 cmd_sz
+= sizeof (ilbd_name_t
);
289 case ILBD_ENABLE_SERVER
:
290 case ILBD_DISABLE_SERVER
:
291 case ILBD_ADD_SERVER_TO_GROUP
:
292 case ILBD_REM_SERVER_FROM_GROUP
:
293 cmd_sz
+= sizeof (ilb_sg_info_t
);
295 case ILBD_SRV_ADDR2ID
:
296 case ILBD_SRV_ID2ADDR
:
297 cmd_sz
+= sizeof (ilb_sg_info_t
) + sizeof (ilb_sg_srv_t
);
299 case ILBD_CREATE_RULE
:
300 cmd_sz
+= sizeof (ilb_rule_info_t
);
303 cmd_sz
+= sizeof (ilb_hc_info_t
);
306 case ILBD_SHOW_PERSIST
:
307 cmd_sz
+= sizeof (ilb_show_info_t
);
315 * Given a request and its size, check that the size is big enough to
316 * contain the variable part of a request.
319 ilbd_check_req_size(ilb_comm_t
*ic
, size_t ic_sz
)
321 ilb_status_t rc
= ILB_STATUS_OK
;
322 ilb_sg_info_t
*sg_info
;
323 ilbd_namelist_t
*nlist
;
325 switch (ic
->ic_cmd
) {
326 case ILBD_CREATE_SERVERGROUP
:
327 case ILBD_ENABLE_SERVER
:
328 case ILBD_DISABLE_SERVER
:
329 case ILBD_ADD_SERVER_TO_GROUP
:
330 case ILBD_REM_SERVER_FROM_GROUP
:
331 sg_info
= (ilb_sg_info_t
*)&ic
->ic_data
;
333 if (ic_sz
< ilbd_cmd_size(ic
) + sg_info
->sg_srvcount
*
334 sizeof (ilb_sg_srv_t
)) {
335 rc
= ILB_STATUS_EINVAL
;
338 case ILBD_ENABLE_RULE
:
339 case ILBD_DISABLE_RULE
:
340 case ILBD_DESTROY_RULE
:
341 nlist
= (ilbd_namelist_t
*)&ic
->ic_data
;
343 if (ic_sz
< ilbd_cmd_size(ic
) + nlist
->ilbl_count
*
344 sizeof (ilbd_name_t
)) {
345 rc
= ILB_STATUS_EINVAL
;
353 * this function *relies* on a complete message/data struct
354 * being passed in (currently via the SOCK_SEQPACKET socket type).
356 * Note that the size of ip is at most ILBD_MSG_SIZE.
359 consume_common_struct(ilb_comm_t
*ic
, size_t ic_sz
, ilbd_client_t
*cli
,
366 boolean_t standard_reply
= B_TRUE
;
370 * cli_ev must be overridden during handling of individual commands,
371 * if there's a special need; otherwise, leave this for
374 cli
->cli_ev
= ILBD_EVENT_REQ
;
377 rbufsz
= ILBD_MSG_SIZE
;
379 /* Sanity check on the size of the static part of a request. */
380 if (ic_sz
< ilbd_cmd_size(ic
)) {
381 rc
= ILB_STATUS_EINVAL
;
385 switch (ic
->ic_cmd
) {
386 case ILBD_CREATE_SERVERGROUP
: {
387 ilb_sg_info_t sg_info
;
390 * ilbd_create_sg() only needs the sg_name field. But it
391 * takes in a ilb_sg_info_t because it is used as a callback
392 * in ilbd_walk_sg_pgs().
394 (void) strlcpy(sg_info
.sg_name
, (char *)&(ic
->ic_data
),
395 sizeof (sg_info
.sg_name
));
396 rc
= ilbd_create_sg(&sg_info
, ev_port
, ps
,
397 cli
->cli_peer_ucredp
);
401 case ILBD_DESTROY_SERVERGROUP
:
402 (void) strlcpy(name
, (char *)&(ic
->ic_data
), sizeof (name
));
403 rc
= ilbd_destroy_sg(name
, ps
, cli
->cli_peer_ucredp
);
406 case ILBD_ADD_SERVER_TO_GROUP
:
407 if ((rc
= ilbd_check_req_size(ic
, ic_sz
)) != ILB_STATUS_OK
)
409 rc
= ilbd_add_server_to_group((ilb_sg_info_t
*)&ic
->ic_data
,
410 ev_port
, ps
, cli
->cli_peer_ucredp
);
413 case ILBD_REM_SERVER_FROM_GROUP
:
414 if ((rc
= ilbd_check_req_size(ic
, ic_sz
)) != ILB_STATUS_OK
)
416 rc
= ilbd_rem_server_from_group((ilb_sg_info_t
*)&ic
->ic_data
,
417 ev_port
, ps
, cli
->cli_peer_ucredp
);
420 case ILBD_ENABLE_SERVER
:
421 if ((rc
= ilbd_check_req_size(ic
, ic_sz
)) != ILB_STATUS_OK
)
423 rc
= ilbd_enable_server((ilb_sg_info_t
*)&ic
->ic_data
, ps
,
424 cli
->cli_peer_ucredp
);
427 case ILBD_DISABLE_SERVER
:
428 if ((rc
= ilbd_check_req_size(ic
, ic_sz
)) != ILB_STATUS_OK
)
430 rc
= ilbd_disable_server((ilb_sg_info_t
*)&ic
->ic_data
, ps
,
431 cli
->cli_peer_ucredp
);
434 case ILBD_SRV_ADDR2ID
:
435 rc
= ilbd_address_to_srvID((ilb_sg_info_t
*)&ic
->ic_data
,
437 if (rc
== ILB_STATUS_OK
)
438 standard_reply
= B_FALSE
;
441 case ILBD_SRV_ID2ADDR
:
442 rc
= ilbd_srvID_to_address((ilb_sg_info_t
*)&ic
->ic_data
,
444 if (rc
== ILB_STATUS_OK
)
445 standard_reply
= B_FALSE
;
448 case ILBD_RETRIEVE_SG_HOSTS
:
449 (void) strlcpy(name
, (char *)&(ic
->ic_data
), sizeof (name
));
450 rc
= ilbd_retrieve_sg_hosts(name
, reply_buf
, &rbufsz
);
451 if (rc
== ILB_STATUS_OK
)
452 standard_reply
= B_FALSE
;
455 case ILBD_RETRIEVE_SG_NAMES
:
456 case ILBD_RETRIEVE_RULE_NAMES
:
457 case ILBD_RETRIEVE_HC_NAMES
:
458 rc
= ilbd_retrieve_names(ic
->ic_cmd
, reply_buf
, &rbufsz
);
459 if (rc
== ILB_STATUS_OK
)
460 standard_reply
= B_FALSE
;
463 case ILBD_CREATE_RULE
:
464 rc
= ilbd_create_rule((ilb_rule_info_t
*)&ic
->ic_data
, ev_port
,
465 ps
, cli
->cli_peer_ucredp
);
468 case ILBD_DESTROY_RULE
:
469 /* Copy the name to ensure that name is NULL terminated. */
470 (void) strlcpy(name
, (char *)&(ic
->ic_data
), sizeof (name
));
471 rc
= ilbd_destroy_rule(name
, ps
, cli
->cli_peer_ucredp
);
474 case ILBD_ENABLE_RULE
:
475 (void) strlcpy(name
, (char *)&(ic
->ic_data
), sizeof (name
));
476 rc
= ilbd_enable_rule(name
, ps
, cli
->cli_peer_ucredp
);
479 case ILBD_DISABLE_RULE
:
480 (void) strlcpy(name
, (char *)&(ic
->ic_data
), sizeof (name
));
481 rc
= ilbd_disable_rule(name
, ps
, cli
->cli_peer_ucredp
);
484 case ILBD_RETRIEVE_RULE
:
485 (void) strlcpy(name
, (char *)&(ic
->ic_data
), sizeof (name
));
486 rc
= ilbd_retrieve_rule(name
, reply_buf
, &rbufsz
);
487 if (rc
== ILB_STATUS_OK
)
488 standard_reply
= B_FALSE
;
492 rc
= ilbd_create_hc((ilb_hc_info_t
*)&ic
->ic_data
, ev_port
, ps
,
493 cli
->cli_peer_ucredp
);
496 case ILBD_DESTROY_HC
:
497 (void) strlcpy(name
, (char *)&(ic
->ic_data
), sizeof (name
));
498 rc
= ilbd_destroy_hc(name
, ps
, cli
->cli_peer_ucredp
);
501 case ILBD_GET_HC_INFO
:
502 (void) strlcpy(name
, (char *)&(ic
->ic_data
), sizeof (name
));
503 rc
= ilbd_get_hc_info(name
, reply_buf
, &rbufsz
);
504 if (rc
== ILB_STATUS_OK
)
505 standard_reply
= B_FALSE
;
508 case ILBD_GET_HC_SRVS
:
509 (void) strlcpy(name
, (char *)&(ic
->ic_data
), sizeof (name
));
510 rc
= ilbd_get_hc_srvs(name
, reply_buf
, &rbufsz
);
511 if (rc
== ILB_STATUS_OK
)
512 standard_reply
= B_FALSE
;
516 rc
= ilbd_show_nat(cli
, ic
, reply_buf
, &rbufsz
);
517 if (rc
== ILB_STATUS_OK
)
518 standard_reply
= B_FALSE
;
521 case ILBD_SHOW_PERSIST
:
522 rc
= ilbd_show_sticky(cli
, ic
, reply_buf
, &rbufsz
);
523 if (rc
== ILB_STATUS_OK
)
524 standard_reply
= B_FALSE
;
528 logdebug("consume_common_struct: unknown command");
529 rc
= ILB_STATUS_INVAL_CMD
;
535 * The message exchange is always in pairs, request/response. If
536 * a transaction requires multiple exchanges, the client will send
537 * in multiple requests to get multiple responses. The show-nat and
538 * show-persist request are examples of this. The end of transaction
539 * is marked with ic_flags set to ILB_COMM_END.
542 /* This is the standard reply. */
543 if (standard_reply
) {
544 if (rc
== ILB_STATUS_OK
)
545 ilbd_reply_ok(reply_buf
, &rbufsz
);
547 ilbd_reply_err(reply_buf
, &rbufsz
, rc
);
550 if ((ret
= send(cli
->cli_sd
, reply_buf
, rbufsz
, 0)) != rbufsz
) {
552 if (errno
!= EWOULDBLOCK
) {
553 logdebug("consume_common_struct: send: %s",
555 rc
= ILB_STATUS_SEND
;
559 * The reply is blocked, save the reply. handle_req()
560 * will associate the event port for the re-send.
562 assert(cli
->cli_saved_reply
== NULL
);
563 if ((cli
->cli_saved_reply
= malloc(rbufsz
)) == NULL
) {
565 * Set the error to ILB_STATUS_SEND so that
566 * handle_req() will free the client.
568 logdebug("consume_common_struct: failure to "
569 "allocate memory to save reply");
570 rc
= ILB_STATUS_SEND
;
573 bcopy(reply_buf
, cli
->cli_saved_reply
, rbufsz
);
574 cli
->cli_saved_size
= rbufsz
;
575 return (ILB_STATUS_EWOULDBLOCK
);
583 * Accept a new client request. A struct ilbd_client_t is allocated to
584 * store the client info. The accepted socket is port_associate() with
585 * the given port. And the allocated ilbd_client_t struct is passed as
589 new_req(int ev_port
, int listener
, void *ev_obj
)
595 ilbd_client_t
*cli
= NULL
;
598 struct passwd
*result
;
600 sa_len
= sizeof (sa
);
601 if ((new_sd
= accept(listener
, &sa
, &sa_len
)) == -1) {
602 /* don't log if we're out of file descriptors */
603 if (errno
!= EINTR
&& errno
!= EMFILE
)
604 logperror("new_req: accept failed");
608 /* Set the new socket to be non-blocking. */
609 if ((sflags
= fcntl(new_sd
, F_GETFL
, 0)) == -1) {
610 logperror("new_req: fcntl(F_GETFL)");
613 if (fcntl(new_sd
, F_SETFL
, sflags
| O_NONBLOCK
) == -1) {
614 logperror("new_req: fcntl(F_SETFL)");
617 if (fcntl(new_sd
, F_SETFD
, FD_CLOEXEC
) == -1) {
618 logperror("new_req: fcntl(FD_CLOEXEC)");
621 if ((cli
= calloc(1, sizeof (ilbd_client_t
))) == NULL
) {
622 logerr("new_req: malloc(ilbd_client_t)");
625 res
= getpeerucred(new_sd
, &cli
->cli_peer_ucredp
);
627 logperror("new_req: getpeerucred failed");
630 if ((uid
= ucred_getruid(cli
->cli_peer_ucredp
)) == (uid_t
)-1) {
631 logperror("new_req: ucred_getruid failed");
634 cli
->cli_pw_bufsz
= (size_t)sysconf(_SC_GETPW_R_SIZE_MAX
);
635 if ((cli
->cli_pw_buf
= malloc(cli
->cli_pw_bufsz
)) == NULL
) {
636 logerr("new_req: malloc(cli_pw_buf)");
639 getpwuid_r(uid
, &cli
->cli_pw
, cli
->cli_pw_buf
, cli
->cli_pw_bufsz
,
642 logperror("new_req: invalid user");
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
,
652 logperror("new_req: port_associate(cli) failed");
655 if (cli
->cli_peer_ucredp
!= NULL
)
656 ucred_free(cli
->cli_peer_ucredp
);
657 free(cli
->cli_pw_buf
);
660 (void) close(new_sd
);
664 /* Re-associate the listener with the event port. */
665 if (port_associate(ev_port
, PORT_SOURCE_FD
, listener
, POLLRDNORM
,
667 logperror("new_req: port_associate(listener) failed");
673 handle_req(int ev_port
, ilbd_event_t event
, ilbd_client_t
*cli
)
675 /* All request should be smaller than ILBD_MSG_SIZE */
678 uint32_t buf
[ILBD_MSG_SIZE
/ sizeof (uint32_t)];
680 int rc
= ILB_STATUS_OK
;
683 if (event
== ILBD_EVENT_REQ
) {
685 * Something is wrong with the client since there is a
686 * pending reply, the client should not send us another
687 * request. Kill this client.
689 if (cli
->cli_saved_reply
!= NULL
) {
690 logerr("handle_req: misbehaving client, more than one "
691 "outstanding request");
692 rc
= ILB_STATUS_INTERNAL
;
697 * Our socket is message based so we should be able
698 * to get the request in one single read.
700 r
= recv(cli
->cli_sd
, (void *)ic_u
.buf
, sizeof (ic_u
.buf
), 0);
702 if (errno
!= EINTR
) {
703 logperror("handle_req: read failed");
704 rc
= ILB_STATUS_READ
;
708 * If interrupted, just re-associate the cli_sd
713 cli
->cli_cmd
= ic_u
.ic
.ic_cmd
;
715 rc
= consume_common_struct(&ic_u
.ic
, r
, cli
, ev_port
);
716 if (rc
== ILB_STATUS_EWOULDBLOCK
)
718 /* Fatal error communicating with client, free it. */
719 if (rc
== ILB_STATUS_SEND
)
722 assert(event
== ILBD_EVENT_REP_OK
);
723 assert(cli
->cli_saved_reply
!= NULL
);
726 * The reply to client was previously blocked, we will
729 if (send(cli
->cli_sd
, cli
->cli_saved_reply
,
730 cli
->cli_saved_size
, 0) != cli
->cli_saved_size
) {
731 if (errno
!= EWOULDBLOCK
) {
732 logdebug("handle_req: send: %s",
734 rc
= ILB_STATUS_SEND
;
739 free(cli
->cli_saved_reply
);
740 cli
->cli_saved_reply
= NULL
;
741 cli
->cli_saved_size
= 0;
744 /* Re-associate with the event port for more requests. */
745 cli
->cli_ev
= ILBD_EVENT_REQ
;
746 if (port_associate(ev_port
, PORT_SOURCE_FD
, cli
->cli_sd
,
747 POLLRDNORM
, cli
) == -1) {
748 logperror("handle_req: port_associate(POLLRDNORM)");
749 rc
= ILB_STATUS_INTERNAL
;
755 /* Re-associate with the event port. */
756 cli
->cli_ev
= ILBD_EVENT_REP_OK
;
757 if (port_associate(ev_port
, PORT_SOURCE_FD
, cli
->cli_sd
, POLLWRNORM
,
759 logperror("handle_req: port_associate(POLLWRNORM)");
760 rc
= ILB_STATUS_INTERNAL
;
770 i_ilbd_read_config(int ev_port
)
772 logdebug("i_ilbd_read_config: port %d", ev_port
);
773 (void) ilbd_walk_sg_pgs(ilbd_create_sg
, &ev_port
, NULL
);
774 (void) ilbd_walk_hc_pgs(ilbd_create_hc
, &ev_port
, NULL
);
775 (void) ilbd_walk_rule_pgs(ilbd_create_rule
, &ev_port
, NULL
);
779 * main event loop for ilbd
780 * asserts that argument 'listener' is a server socket ready to accept() on.
783 main_loop(int listener
)
786 int ev_port
, ev_port_obj
;
787 ilbd_event_obj_t ev_obj
;
788 ilbd_timer_event_obj_t timer_ev_obj
;
790 ev_port
= port_create();
792 logperror("main_loop: port_create failed");
795 ilbd_hc_timer_init(ev_port
, &timer_ev_obj
);
797 ev_obj
.ev
= ILBD_EVENT_NEW_REQ
;
798 if (port_associate(ev_port
, PORT_SOURCE_FD
, listener
, POLLRDNORM
,
800 logperror("main_loop: port_associate failed");
804 i_ilbd_read_config(ev_port
);
805 ilbd_hc_timer_update(&timer_ev_obj
);
813 r
= port_get(ev_port
, &p_ev
, NULL
);
817 logperror("main_loop: port_get failed");
821 ev_port_obj
= p_ev
.portev_object
;
822 event
= ((ilbd_event_obj_t
*)p_ev
.portev_user
)->ev
;
825 case ILBD_EVENT_TIMER
:
829 case ILBD_EVENT_PROBE
:
830 ilbd_hc_probe_return(ev_port
, ev_port_obj
,
832 (ilbd_hc_probe_event_t
*)p_ev
.portev_user
);
835 case ILBD_EVENT_NEW_REQ
:
836 assert(ev_port_obj
== listener
);
838 * An error happens in the listener. Exit
841 if (p_ev
.portev_events
& (POLLHUP
|POLLERR
)) {
842 logerr("main_loop: listener error");
845 new_req(ev_port
, ev_port_obj
, &ev_obj
);
848 case ILBD_EVENT_REP_OK
:
850 cli
= (ilbd_client_t
*)p_ev
.portev_user
;
851 assert(ev_port_obj
== cli
->cli_sd
);
854 * An error happens in the newly accepted
855 * client request. Clean up the client.
856 * this also happens when client closes socket,
857 * so not necessarily a reason for alarm
859 if (p_ev
.portev_events
& (POLLHUP
|POLLERR
)) {
864 handle_req(ev_port
, event
, cli
);
868 logerr("main_loop: unknown event %d", event
);
873 ilbd_hc_timer_update(&timer_ev_obj
);
878 i_ilbd_setup_lists(void)
881 i_setup_rule_hlist();
882 i_ilbd_setup_hc_list();
886 * Usage message - call only during startup. it will print its
887 * message on stderr and exit
892 (void) fprintf(stderr
, gettext("Usage: %s [-d|--debug]\n"), name
);
897 print_version(char *name
)
899 (void) printf("%s %s\n", basename(name
), ILBD_VERSION
);
900 (void) printf(gettext(ILBD_COPYRIGHT
));
905 * Increase the file descriptor limit for handling a lot of health check
906 * processes (each requires a pipe).
908 * Note that this function is called before ilbd becomes a daemon. So
909 * we call perror(3C) to print out error message directly so that SMF
917 if (getrlimit(RLIMIT_NOFILE
, &rlp
) == -1) {
918 perror("ilbd: getrlimit");
921 rlp
.rlim_cur
= rlp
.rlim_max
;
922 if (setrlimit(RLIMIT_NOFILE
, &rlp
) == -1) {
923 perror("ilbd: setrlimit");
929 main(int argc
, char **argv
)
934 (void) setlocale(LC_ALL
, "");
935 #if !defined(TEXT_DOMAIN)
936 #define TEXT_DOMAIN "SYS_TEST"
938 static const char daemon_dir
[] = DAEMON_DIR
;
940 (void) textdomain(TEXT_DOMAIN
);
942 while ((c
= getopt(argc
, argv
, ":V?d(debug)")) != -1) {
944 case '?': Usage(argv
[0]);
947 case 'V': print_version(argv
[0]);
950 case 'd': ilbd_enable_debug();
952 default: Usage(argv
[0]);
959 * Whenever the daemon starts, it needs to start with a clean
960 * slate in the kernel. We need sys_ip_config privilege for
963 ilbd_reset_kernel_state();
965 /* Increase the limit on the number of file descriptors. */
969 * ilbd daemon starts off as root, just so it can create
970 * /var/run/daemon if one does not exist. After that is done
971 * the daemon switches to "daemon" uid. This is similar to what
974 if (mkdir(daemon_dir
, DAEMON_DIR_MODE
) == 0 || errno
== EEXIST
) {
975 (void) chmod(daemon_dir
, DAEMON_DIR_MODE
);
976 (void) chown(daemon_dir
, DAEMON_UID
, DAEMON_GID
);
978 perror("main: mkdir failed");
982 * Now lets switch ilbd as uid = daemon, gid = daemon with a
983 * trimmed down privilege set
985 if (__init_daemon_priv(PU_RESETGROUPS
| PU_LIMITPRIVS
| PU_INHERITPRIVS
,
986 DAEMON_UID
, DAEMON_GID
, PRIV_PROC_OWNER
, PRIV_PROC_AUDIT
,
987 PRIV_NET_ICMPACCESS
, PRIV_SYS_IP_CONFIG
, NULL
) == -1) {
988 (void) fprintf(stderr
, "Insufficient privileges\n");
993 * Opens a PF_UNIX socket to the client. No privilege needed
996 s
= ilbd_create_client_socket();
999 * Daemonify if ilbd is not running with -d option
1000 * Need proc_fork privilege for this
1002 if (!is_debugging_on()) {
1003 logdebug("daemonizing...");
1004 if (daemon(0, 0) != 0) {
1005 logperror("daemon failed");
1009 (void) priv_set(PRIV_OFF
, PRIV_INHERITABLE
, PRIV_PROC_OWNER
,
1010 PRIV_PROC_AUDIT
, NULL
);
1012 /* if daemonified then set up syslog */
1013 if (!is_debugging_on())
1014 openlog("ilbd", LOG_PID
, LOG_DAEMON
);
1016 i_ilbd_setup_lists();
1021 * if we come here, then we experienced an error or a shutdown
1022 * indicator, so clean up after ourselves.
1024 logdebug("main(): terminating");
1026 (void) remove(SOCKET_PATH
);
1027 ilbd_reset_kernel_state();