1dbed73cbSSangeeta Misra /*
2dbed73cbSSangeeta Misra  * CDDL HEADER START
3dbed73cbSSangeeta Misra  *
4dbed73cbSSangeeta Misra  * The contents of this file are subject to the terms of the
5dbed73cbSSangeeta Misra  * Common Development and Distribution License (the "License").
6dbed73cbSSangeeta Misra  * You may not use this file except in compliance with the License.
7dbed73cbSSangeeta Misra  *
8dbed73cbSSangeeta Misra  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9dbed73cbSSangeeta Misra  * or http://www.opensolaris.org/os/licensing.
10dbed73cbSSangeeta Misra  * See the License for the specific language governing permissions
11dbed73cbSSangeeta Misra  * and limitations under the License.
12dbed73cbSSangeeta Misra  *
13dbed73cbSSangeeta Misra  * When distributing Covered Code, include this CDDL HEADER in each
14dbed73cbSSangeeta Misra  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15dbed73cbSSangeeta Misra  * If applicable, add the following below this CDDL HEADER, with the
16dbed73cbSSangeeta Misra  * fields enclosed by brackets "[]" replaced with your own identifying
17dbed73cbSSangeeta Misra  * information: Portions Copyright [yyyy] [name of copyright owner]
18dbed73cbSSangeeta Misra  *
19dbed73cbSSangeeta Misra  * CDDL HEADER END
20dbed73cbSSangeeta Misra  */
21dbed73cbSSangeeta Misra 
22dbed73cbSSangeeta Misra /*
230bc1cad2SSangeeta Misra  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
24*48bbca81SDaniel Hoffman  * Copyright (c) 2016 by Delphix. All rights reserved.
25dbed73cbSSangeeta Misra  */
26dbed73cbSSangeeta Misra 
27dbed73cbSSangeeta Misra /*
28dbed73cbSSangeeta Misra  * The core of ilbd daemon is a single-threaded event loop using
29dbed73cbSSangeeta Misra  * event completion framework; it receives requests from client using
30dbed73cbSSangeeta Misra  * the libilb functions, handles timeouts, initiates health checks, and
31dbed73cbSSangeeta Misra  * populates the kernel state.
32dbed73cbSSangeeta Misra  *
33dbed73cbSSangeeta Misra  * The daemon has the following privileges (in addition to the basic ones):
34dbed73cbSSangeeta Misra  *
35dbed73cbSSangeeta Misra  * 	PRIV_PROC_OWNER, PRIV_NET_ICMPACCESS,
36dbed73cbSSangeeta Misra  *	PRIV_SYS_IP_CONFIG, PRIV_PROC_AUDIT
37dbed73cbSSangeeta Misra  *
38dbed73cbSSangeeta Misra  * The aforementioned  privileges will be specified in the SMF manifest.
39dbed73cbSSangeeta Misra  *
40dbed73cbSSangeeta Misra  * AF_UNIX socket is used for IPC between libilb and this daemon as
41dbed73cbSSangeeta Misra  * both processes will run on the same machine.
42dbed73cbSSangeeta Misra  *
43dbed73cbSSangeeta Misra  * To do health check, the daemon will create a timer for every health
44dbed73cbSSangeeta Misra  * check probe. Each of these timers will be  associated with the
45dbed73cbSSangeeta Misra  * event port. When a timer goes off, the daemon will initiate a
46dbed73cbSSangeeta Misra  * pipe to a separate process to execute the specific health check
47dbed73cbSSangeeta Misra  * probe. This new process will run with the same user-id as that of
48dbed73cbSSangeeta Misra  * ilbd daemon and will inherit all the privileges from the ilbd
49dbed73cbSSangeeta Misra  * daemon parent process except the following:
50dbed73cbSSangeeta Misra  *
51dbed73cbSSangeeta Misra  * PRIV_PROC_OWNER, PRIV_PROC_AUDIT
52dbed73cbSSangeeta Misra  *
53dbed73cbSSangeeta Misra  * All health checks, will be implemented as external methods
54dbed73cbSSangeeta Misra  * (binary or script). The following arguments will be passed
55dbed73cbSSangeeta Misra  * to external methods:
56dbed73cbSSangeeta Misra  *
57dbed73cbSSangeeta Misra  *	$1	VIP (literal IPv4 or IPv6 address)
58dbed73cbSSangeeta Misra  *	$2	Server IP (literal IPv4 or IPv6 address)
59dbed73cbSSangeeta Misra  *	$3	Protocol (UDP, TCP as a string)
60dbed73cbSSangeeta Misra  *	$4	The load balance mode, "DSR", "NAT", "HALF_NAT"
61dbed73cbSSangeeta Misra  *	$5	Numeric port range
62dbed73cbSSangeeta Misra  *	$6	maximum time (in seconds) the method
63dbed73cbSSangeeta Misra  * should wait before returning failure. If the method runs for
64dbed73cbSSangeeta Misra  * longer, it may be killed, and the test considered failed.
65dbed73cbSSangeeta Misra  *
66dbed73cbSSangeeta Misra  * Upon success, a health check method should print the RTT to the
67dbed73cbSSangeeta Misra  * it finds to its STDOUT for ilbd to consume.  The implicit unit
68dbed73cbSSangeeta Misra  * is microseconds but only the number needs to be printed.  If it
69dbed73cbSSangeeta Misra  * cannot find the RTT, it should print 0.  If the method decides
70dbed73cbSSangeeta Misra  * that the server is dead, it should print -1 to its STDOUT.
71dbed73cbSSangeeta Misra  *
72dbed73cbSSangeeta Misra  * By default, an user-supplied health check probe process will
73dbed73cbSSangeeta Misra  * also run with the same set of privileges as ILB's built-in
74dbed73cbSSangeeta Misra  * probes.  If the administrator has an user-supplied health check
75*48bbca81SDaniel Hoffman  * program that requires a larger privilege set, they will have
76dbed73cbSSangeeta Misra  * to implement setuid program.
77dbed73cbSSangeeta Misra  *
78dbed73cbSSangeeta Misra  * Each health check will have a timeout, such that if the health
79dbed73cbSSangeeta Misra  * check process is hung, it will be killed after the timeout interval
80dbed73cbSSangeeta Misra  * and the daemon will notify the kernel ILB engine of the server's
81dbed73cbSSangeeta Misra  * unresponsiveness, so that load distribution can be appropriately
82dbed73cbSSangeeta Misra  * adjusted.  If on the other hand the health check is successful
83dbed73cbSSangeeta Misra  * the timeout timer is cancelled.
84dbed73cbSSangeeta Misra  */
85dbed73cbSSangeeta Misra 
86dbed73cbSSangeeta Misra #include <stdio.h>
87dbed73cbSSangeeta Misra #include <stdlib.h>
88dbed73cbSSangeeta Misra #include <strings.h>
89dbed73cbSSangeeta Misra #include <libgen.h>
90dbed73cbSSangeeta Misra #include <fcntl.h>
91dbed73cbSSangeeta Misra #include <stddef.h>
92dbed73cbSSangeeta Misra #include <signal.h>
93dbed73cbSSangeeta Misra #include <port.h>
94dbed73cbSSangeeta Misra #include <ctype.h>
95dbed73cbSSangeeta Misra #include <sys/types.h>
96dbed73cbSSangeeta Misra #include <sys/wait.h>
97dbed73cbSSangeeta Misra #include <sys/stat.h>
98dbed73cbSSangeeta Misra #include <sys/note.h>
99dbed73cbSSangeeta Misra #include <sys/resource.h>
100dbed73cbSSangeeta Misra #include <unistd.h>
101dbed73cbSSangeeta Misra #include <sys/socket.h>
102dbed73cbSSangeeta Misra #include <errno.h>
103dbed73cbSSangeeta Misra #include <ucred.h>
104dbed73cbSSangeeta Misra #include <priv_utils.h>
105dbed73cbSSangeeta Misra #include <net/if.h>
106dbed73cbSSangeeta Misra #include <libilb.h>
107dbed73cbSSangeeta Misra #include <assert.h>
108dbed73cbSSangeeta Misra #include <inet/ilb.h>
109dbed73cbSSangeeta Misra #include <libintl.h>
110dbed73cbSSangeeta Misra #include <fcntl.h>
111dbed73cbSSangeeta Misra #include <rpcsvc/daemon_utils.h>
112dbed73cbSSangeeta Misra #include "libilb_impl.h"
113dbed73cbSSangeeta Misra #include "ilbd.h"
114dbed73cbSSangeeta Misra 
115dbed73cbSSangeeta Misra /*
116dbed73cbSSangeeta Misra  * NOTE: The following needs to be kept up to date.
117dbed73cbSSangeeta Misra  */
118dbed73cbSSangeeta Misra #define	ILBD_VERSION	"1.0"
119dbed73cbSSangeeta Misra #define	ILBD_COPYRIGHT	\
1200bc1cad2SSangeeta Misra 	"Copyright (c) 2005, 2010, Oracle and/or its affiliates. " \
1210bc1cad2SSangeeta Misra 	"All rights reserved.\n"
122dbed73cbSSangeeta Misra 
123dbed73cbSSangeeta Misra /*
124dbed73cbSSangeeta Misra  * Global reply buffer to client request.  Note that ilbd is single threaded,
125dbed73cbSSangeeta Misra  * so a global buffer is OK.  If ilbd becomes multi-threaded, this needs to
126dbed73cbSSangeeta Misra  * be changed.
127dbed73cbSSangeeta Misra  */
128dbed73cbSSangeeta Misra static uint32_t reply_buf[ILBD_MSG_SIZE / sizeof (uint32_t)];
129dbed73cbSSangeeta Misra 
130dbed73cbSSangeeta Misra static void
ilbd_free_cli(ilbd_client_t * cli)131dbed73cbSSangeeta Misra ilbd_free_cli(ilbd_client_t *cli)
132dbed73cbSSangeeta Misra {
133dbed73cbSSangeeta Misra 	(void) close(cli->cli_sd);
134dbed73cbSSangeeta Misra 	if (cli->cli_cmd == ILBD_SHOW_NAT)
135dbed73cbSSangeeta Misra 		ilbd_show_nat_cleanup();
136dbed73cbSSangeeta Misra 	if (cli->cli_cmd == ILBD_SHOW_PERSIST)
137dbed73cbSSangeeta Misra 		ilbd_show_sticky_cleanup();
138dbed73cbSSangeeta Misra 	if (cli->cli_saved_reply != NULL)
139dbed73cbSSangeeta Misra 		free(cli->cli_saved_reply);
14056b8f71eSSerghei Samsi 	if (cli->cli_peer_ucredp != NULL)
14156b8f71eSSerghei Samsi 		ucred_free(cli->cli_peer_ucredp);
142dbed73cbSSangeeta Misra 	free(cli->cli_pw_buf);
143dbed73cbSSangeeta Misra 	free(cli);
144dbed73cbSSangeeta Misra }
145dbed73cbSSangeeta Misra 
146dbed73cbSSangeeta Misra static void
ilbd_reset_kernel_state(void)147dbed73cbSSangeeta Misra ilbd_reset_kernel_state(void)
148dbed73cbSSangeeta Misra {
149dbed73cbSSangeeta Misra 	ilb_status_t	rc;
150dbed73cbSSangeeta Misra 	ilb_name_cmd_t	kcmd;
151dbed73cbSSangeeta Misra 
152dbed73cbSSangeeta Misra 	kcmd.cmd = ILB_DESTROY_RULE;
153dbed73cbSSangeeta Misra 	kcmd.flags = ILB_RULE_ALLRULES;
154dbed73cbSSangeeta Misra 	kcmd.name[0] = '\0';
155dbed73cbSSangeeta Misra 
156dbed73cbSSangeeta Misra 	rc = do_ioctl(&kcmd, 0);
157dbed73cbSSangeeta Misra 	if (rc != ILB_STATUS_OK)
158dbed73cbSSangeeta Misra 		logdebug("ilbd_reset_kernel_state: do_ioctl failed: %s",
159dbed73cbSSangeeta Misra 		    strerror(errno));
160dbed73cbSSangeeta Misra }
161dbed73cbSSangeeta Misra 
162dbed73cbSSangeeta Misra /* Signal handler to do clean up. */
163dbed73cbSSangeeta Misra /* ARGSUSED */
164dbed73cbSSangeeta Misra static void
ilbd_cleanup(int sig)165dbed73cbSSangeeta Misra ilbd_cleanup(int sig)
166dbed73cbSSangeeta Misra {
167dbed73cbSSangeeta Misra 	(void) remove(SOCKET_PATH);
168dbed73cbSSangeeta Misra 	ilbd_reset_kernel_state();
169dbed73cbSSangeeta Misra 	exit(0);
170dbed73cbSSangeeta Misra }
171dbed73cbSSangeeta Misra 
172dbed73cbSSangeeta Misra /*
173dbed73cbSSangeeta Misra  * Create a socket and return it to caller.  If there is a failure, this
174dbed73cbSSangeeta Misra  * function calls exit(2).  Hence it always returns a valid listener socket.
175dbed73cbSSangeeta Misra  *
176dbed73cbSSangeeta Misra  * Note that this function is called before ilbd becomes a daemon.  So
177dbed73cbSSangeeta Misra  * we call perror(3C) to print out error message directly so that SMF can
178dbed73cbSSangeeta Misra  * catch them.
179dbed73cbSSangeeta Misra  */
180dbed73cbSSangeeta Misra static int
ilbd_create_client_socket(void)181dbed73cbSSangeeta Misra ilbd_create_client_socket(void)
182dbed73cbSSangeeta Misra {
183dbed73cbSSangeeta Misra 	int			s;
184dbed73cbSSangeeta Misra 	mode_t			omask;
185dbed73cbSSangeeta Misra 	struct sockaddr_un	sa;
186dbed73cbSSangeeta Misra 	int			sobufsz;
187dbed73cbSSangeeta Misra 
188dbed73cbSSangeeta Misra 	s = socket(PF_UNIX, SOCK_SEQPACKET, 0);
189dbed73cbSSangeeta Misra 	if (s == -1) {
190dbed73cbSSangeeta Misra 		perror("ilbd_create_client_socket: socket to"
191dbed73cbSSangeeta Misra 		    " client failed");
192dbed73cbSSangeeta Misra 		exit(errno);
193dbed73cbSSangeeta Misra 	}
194dbed73cbSSangeeta Misra 	if (fcntl(s, F_SETFD, FD_CLOEXEC) == -1) {
195dbed73cbSSangeeta Misra 		perror("ilbd_create_client_socket: fcntl(FD_CLOEXEC)");
196dbed73cbSSangeeta Misra 		exit(errno);
197dbed73cbSSangeeta Misra 	}
198dbed73cbSSangeeta Misra 
199dbed73cbSSangeeta Misra 	sobufsz = ILBD_MSG_SIZE;
200dbed73cbSSangeeta Misra 	if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, &sobufsz,
201dbed73cbSSangeeta Misra 	    sizeof (sobufsz)) != 0) {
202dbed73cbSSangeeta Misra 		perror("ilbd_creat_client_socket: setsockopt(SO_SNDBUF) "
203dbed73cbSSangeeta Misra 		    "failed");
204dbed73cbSSangeeta Misra 		exit(errno);
205dbed73cbSSangeeta Misra 	}
206dbed73cbSSangeeta Misra 	if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &sobufsz,
207dbed73cbSSangeeta Misra 	    sizeof (sobufsz)) != 0) {
208dbed73cbSSangeeta Misra 		perror("ilbd_creat_client_socket: setsockopt(SO_RCVBUF) "
209dbed73cbSSangeeta Misra 		    "failed");
210dbed73cbSSangeeta Misra 		exit(errno);
211dbed73cbSSangeeta Misra 	}
212dbed73cbSSangeeta Misra 
213dbed73cbSSangeeta Misra 	/*
214dbed73cbSSangeeta Misra 	 * since everybody can talk to us, we need to open up permissions
215dbed73cbSSangeeta Misra 	 * we check peer privileges on a per-operation basis.
216dbed73cbSSangeeta Misra 	 * This is no security issue as long as we're single-threaded.
217dbed73cbSSangeeta Misra 	 */
218dbed73cbSSangeeta Misra 	omask = umask(0);
219dbed73cbSSangeeta Misra 
220dbed73cbSSangeeta Misra 	/* just in case we didn't clean up properly after last exit */
221dbed73cbSSangeeta Misra 	(void) remove(SOCKET_PATH);
222dbed73cbSSangeeta Misra 
223dbed73cbSSangeeta Misra 	bzero(&sa, sizeof (sa));
224dbed73cbSSangeeta Misra 	sa.sun_family = AF_UNIX;
225dbed73cbSSangeeta Misra 	(void) strlcpy(sa.sun_path, SOCKET_PATH, sizeof (sa.sun_path));
226dbed73cbSSangeeta Misra 
227dbed73cbSSangeeta Misra 	if (bind(s, (struct sockaddr *)&sa, sizeof (sa)) != 0) {
228dbed73cbSSangeeta Misra 		perror("ilbd_create_client_socket(): bind to client"
229dbed73cbSSangeeta Misra 		    " socket failed");
230dbed73cbSSangeeta Misra 		exit(errno);
231dbed73cbSSangeeta Misra 	}
232dbed73cbSSangeeta Misra 
233dbed73cbSSangeeta Misra 	/* re-instate old umask */
234dbed73cbSSangeeta Misra 	(void) umask(omask);
235dbed73cbSSangeeta Misra 
236dbed73cbSSangeeta Misra #define	QLEN	16
237dbed73cbSSangeeta Misra 
238dbed73cbSSangeeta Misra 	if (listen(s, QLEN) != 0) {
239dbed73cbSSangeeta Misra 		perror("ilbd_create_client_socket: listen to client"
240dbed73cbSSangeeta Misra 		    " socket failed");
241dbed73cbSSangeeta Misra 		exit(errno);
242dbed73cbSSangeeta Misra 	}
243dbed73cbSSangeeta Misra 
244dbed73cbSSangeeta Misra 	(void) signal(SIGHUP, SIG_IGN);
245dbed73cbSSangeeta Misra 	(void) signal(SIGPIPE, SIG_IGN);
246dbed73cbSSangeeta Misra 	(void) signal(SIGSTOP, SIG_IGN);
247dbed73cbSSangeeta Misra 	(void) signal(SIGTSTP, SIG_IGN);
248dbed73cbSSangeeta Misra 	(void) signal(SIGTTIN, SIG_IGN);
249dbed73cbSSangeeta Misra 	(void) signal(SIGTTOU, SIG_IGN);
250dbed73cbSSangeeta Misra 
251dbed73cbSSangeeta Misra 	(void) signal(SIGINT, ilbd_cleanup);
252dbed73cbSSangeeta Misra 	(void) signal(SIGTERM, ilbd_cleanup);
253dbed73cbSSangeeta Misra 	(void) signal(SIGQUIT, ilbd_cleanup);
254dbed73cbSSangeeta Misra 
255dbed73cbSSangeeta Misra 	return (s);
256dbed73cbSSangeeta Misra }
257dbed73cbSSangeeta Misra 
258dbed73cbSSangeeta Misra /*
259dbed73cbSSangeeta Misra  * Return the minimum size of a given request.  The returned size does not
260dbed73cbSSangeeta Misra  * include the variable part of a request.
261dbed73cbSSangeeta Misra  */
262dbed73cbSSangeeta Misra static size_t
ilbd_cmd_size(const ilb_comm_t * ic)263dbed73cbSSangeeta Misra ilbd_cmd_size(const ilb_comm_t *ic)
264dbed73cbSSangeeta Misra {
265dbed73cbSSangeeta Misra 	size_t cmd_sz;
266dbed73cbSSangeeta Misra 
267dbed73cbSSangeeta Misra 	cmd_sz = sizeof (*ic);
268dbed73cbSSangeeta Misra 	switch (ic->ic_cmd) {
269dbed73cbSSangeeta Misra 	case ILBD_RETRIEVE_SG_NAMES:
270dbed73cbSSangeeta Misra 	case ILBD_RETRIEVE_RULE_NAMES:
271dbed73cbSSangeeta Misra 	case ILBD_RETRIEVE_HC_NAMES:
272dbed73cbSSangeeta Misra 	case ILBD_CMD_OK:
273dbed73cbSSangeeta Misra 		break;
274dbed73cbSSangeeta Misra 	case ILBD_CMD_ERROR:
275dbed73cbSSangeeta Misra 		cmd_sz += sizeof (ilb_status_t);
276dbed73cbSSangeeta Misra 		break;
277dbed73cbSSangeeta Misra 	case ILBD_RETRIEVE_SG_HOSTS:
278dbed73cbSSangeeta Misra 	case ILBD_CREATE_SERVERGROUP:
279dbed73cbSSangeeta Misra 	case ILBD_DESTROY_SERVERGROUP:
280dbed73cbSSangeeta Misra 	case ILBD_DESTROY_RULE:
281dbed73cbSSangeeta Misra 	case ILBD_ENABLE_RULE:
282dbed73cbSSangeeta Misra 	case ILBD_DISABLE_RULE:
283dbed73cbSSangeeta Misra 	case ILBD_RETRIEVE_RULE:
284dbed73cbSSangeeta Misra 	case ILBD_DESTROY_HC:
285dbed73cbSSangeeta Misra 	case ILBD_GET_HC_INFO:
286dbed73cbSSangeeta Misra 	case ILBD_GET_HC_SRVS:
287dbed73cbSSangeeta Misra 		cmd_sz += sizeof (ilbd_name_t);
288dbed73cbSSangeeta Misra 		break;
289dbed73cbSSangeeta Misra 	case ILBD_ENABLE_SERVER:
290dbed73cbSSangeeta Misra 	case ILBD_DISABLE_SERVER:
291dbed73cbSSangeeta Misra 	case ILBD_ADD_SERVER_TO_GROUP:
292dbed73cbSSangeeta Misra 	case ILBD_REM_SERVER_FROM_GROUP:
293dbed73cbSSangeeta Misra 		cmd_sz += sizeof (ilb_sg_info_t);
294dbed73cbSSangeeta Misra 		break;
295dbed73cbSSangeeta Misra 	case ILBD_SRV_ADDR2ID:
296dbed73cbSSangeeta Misra 	case ILBD_SRV_ID2ADDR:
297dbed73cbSSangeeta Misra 		cmd_sz += sizeof (ilb_sg_info_t) + sizeof (ilb_sg_srv_t);
298dbed73cbSSangeeta Misra 		break;
299dbed73cbSSangeeta Misra 	case ILBD_CREATE_RULE:
300dbed73cbSSangeeta Misra 		cmd_sz += sizeof (ilb_rule_info_t);
301dbed73cbSSangeeta Misra 		break;
302dbed73cbSSangeeta Misra 	case ILBD_CREATE_HC:
303dbed73cbSSangeeta Misra 		cmd_sz += sizeof (ilb_hc_info_t);
304dbed73cbSSangeeta Misra 		break;
305dbed73cbSSangeeta Misra 	case ILBD_SHOW_NAT:
306dbed73cbSSangeeta Misra 	case ILBD_SHOW_PERSIST:
307dbed73cbSSangeeta Misra 		cmd_sz += sizeof (ilb_show_info_t);
308dbed73cbSSangeeta Misra 		break;
309dbed73cbSSangeeta Misra 	}
310dbed73cbSSangeeta Misra 
311dbed73cbSSangeeta Misra 	return (cmd_sz);
312dbed73cbSSangeeta Misra }
313dbed73cbSSangeeta Misra 
314dbed73cbSSangeeta Misra /*
315dbed73cbSSangeeta Misra  * Given a request and its size, check that the size is big enough to
316dbed73cbSSangeeta Misra  * contain the variable part of a request.
317dbed73cbSSangeeta Misra  */
318dbed73cbSSangeeta Misra static ilb_status_t
ilbd_check_req_size(ilb_comm_t * ic,size_t ic_sz)319dbed73cbSSangeeta Misra ilbd_check_req_size(ilb_comm_t *ic, size_t ic_sz)
320dbed73cbSSangeeta Misra {
321dbed73cbSSangeeta Misra 	ilb_status_t rc = ILB_STATUS_OK;
322dbed73cbSSangeeta Misra 	ilb_sg_info_t *sg_info;
323dbed73cbSSangeeta Misra 	ilbd_namelist_t *nlist;
324dbed73cbSSangeeta Misra 
325dbed73cbSSangeeta Misra 	switch (ic->ic_cmd) {
326dbed73cbSSangeeta Misra 	case ILBD_CREATE_SERVERGROUP:
327dbed73cbSSangeeta Misra 	case ILBD_ENABLE_SERVER:
328dbed73cbSSangeeta Misra 	case ILBD_DISABLE_SERVER:
329dbed73cbSSangeeta Misra 	case ILBD_ADD_SERVER_TO_GROUP:
330dbed73cbSSangeeta Misra 	case ILBD_REM_SERVER_FROM_GROUP:
331dbed73cbSSangeeta Misra 		sg_info = (ilb_sg_info_t *)&ic->ic_data;
332dbed73cbSSangeeta Misra 
333dbed73cbSSangeeta Misra 		if (ic_sz < ilbd_cmd_size(ic) + sg_info->sg_srvcount *
334dbed73cbSSangeeta Misra 		    sizeof (ilb_sg_srv_t)) {
335dbed73cbSSangeeta Misra 			rc = ILB_STATUS_EINVAL;
336dbed73cbSSangeeta Misra 		}
337dbed73cbSSangeeta Misra 		break;
338dbed73cbSSangeeta Misra 	case ILBD_ENABLE_RULE:
339dbed73cbSSangeeta Misra 	case ILBD_DISABLE_RULE:
340dbed73cbSSangeeta Misra 	case ILBD_DESTROY_RULE:
341dbed73cbSSangeeta Misra 		nlist = (ilbd_namelist_t *)&ic->ic_data;
342dbed73cbSSangeeta Misra 
343dbed73cbSSangeeta Misra 		if (ic_sz < ilbd_cmd_size(ic) + nlist->ilbl_count *
344dbed73cbSSangeeta Misra 		    sizeof (ilbd_name_t)) {
345dbed73cbSSangeeta Misra 			rc = ILB_STATUS_EINVAL;
346dbed73cbSSangeeta Misra 		}
347dbed73cbSSangeeta Misra 		break;
348dbed73cbSSangeeta Misra 	}
349dbed73cbSSangeeta Misra 	return (rc);
350dbed73cbSSangeeta Misra }
351dbed73cbSSangeeta Misra 
352dbed73cbSSangeeta Misra /*
353dbed73cbSSangeeta Misra  * this function *relies* on a complete message/data struct
354dbed73cbSSangeeta Misra  * being passed in (currently via the SOCK_SEQPACKET socket type).
355dbed73cbSSangeeta Misra  *
356dbed73cbSSangeeta Misra  * Note that the size of ip is at most ILBD_MSG_SIZE.
357dbed73cbSSangeeta Misra  */
358dbed73cbSSangeeta Misra static ilb_status_t
consume_common_struct(ilb_comm_t * ic,size_t ic_sz,ilbd_client_t * cli,int ev_port)359dbed73cbSSangeeta Misra consume_common_struct(ilb_comm_t *ic, size_t ic_sz, ilbd_client_t *cli,
360dbed73cbSSangeeta Misra     int ev_port)
361dbed73cbSSangeeta Misra {
362dbed73cbSSangeeta Misra 	ilb_status_t	rc;
363dbed73cbSSangeeta Misra 	struct passwd	*ps;
364dbed73cbSSangeeta Misra 	size_t		rbufsz;
365dbed73cbSSangeeta Misra 	ssize_t		ret;
366dbed73cbSSangeeta Misra 	boolean_t	standard_reply = B_TRUE;
367dbed73cbSSangeeta Misra 	ilbd_name_t	name;
368dbed73cbSSangeeta Misra 
369dbed73cbSSangeeta Misra 	/*
370dbed73cbSSangeeta Misra 	 * cli_ev must be overridden during handling of individual commands,
371dbed73cbSSangeeta Misra 	 * if there's a special need; otherwise, leave this for
372dbed73cbSSangeeta Misra 	 * the "default" case
373dbed73cbSSangeeta Misra 	 */
374dbed73cbSSangeeta Misra 	cli->cli_ev = ILBD_EVENT_REQ;
375dbed73cbSSangeeta Misra 
376dbed73cbSSangeeta Misra 	ps = &cli->cli_pw;
377dbed73cbSSangeeta Misra 	rbufsz = ILBD_MSG_SIZE;
378dbed73cbSSangeeta Misra 
379dbed73cbSSangeeta Misra 	/* Sanity check on the size of the static part of a request. */
380dbed73cbSSangeeta Misra 	if (ic_sz < ilbd_cmd_size(ic)) {
381dbed73cbSSangeeta Misra 		rc = ILB_STATUS_EINVAL;
382dbed73cbSSangeeta Misra 		goto out;
383dbed73cbSSangeeta Misra 	}
384dbed73cbSSangeeta Misra 
385dbed73cbSSangeeta Misra 	switch (ic->ic_cmd) {
386dbed73cbSSangeeta Misra 	case ILBD_CREATE_SERVERGROUP: {
387dbed73cbSSangeeta Misra 		ilb_sg_info_t sg_info;
388dbed73cbSSangeeta Misra 
389dbed73cbSSangeeta Misra 		/*
390dbed73cbSSangeeta Misra 		 * ilbd_create_sg() only needs the sg_name field.  But it
391dbed73cbSSangeeta Misra 		 * takes in a ilb_sg_info_t because it is used as a callback
392dbed73cbSSangeeta Misra 		 * in ilbd_walk_sg_pgs().
393dbed73cbSSangeeta Misra 		 */
394dbed73cbSSangeeta Misra 		(void) strlcpy(sg_info.sg_name, (char *)&(ic->ic_data),
395dbed73cbSSangeeta Misra 		    sizeof (sg_info.sg_name));
396dbed73cbSSangeeta Misra 		rc = ilbd_create_sg(&sg_info, ev_port, ps,
397dbed73cbSSangeeta Misra 		    cli->cli_peer_ucredp);
398dbed73cbSSangeeta Misra 		break;
399dbed73cbSSangeeta Misra 	}
400dbed73cbSSangeeta Misra 
401dbed73cbSSangeeta Misra 	case ILBD_DESTROY_SERVERGROUP:
402dbed73cbSSangeeta Misra 		(void) strlcpy(name, (char *)&(ic->ic_data), sizeof (name));
403dbed73cbSSangeeta Misra 		rc = ilbd_destroy_sg(name, ps, cli->cli_peer_ucredp);
404dbed73cbSSangeeta Misra 		break;
405dbed73cbSSangeeta Misra 
406dbed73cbSSangeeta Misra 	case ILBD_ADD_SERVER_TO_GROUP:
407dbed73cbSSangeeta Misra 		if ((rc = ilbd_check_req_size(ic, ic_sz)) != ILB_STATUS_OK)
408dbed73cbSSangeeta Misra 			break;
409dbed73cbSSangeeta Misra 		rc = ilbd_add_server_to_group((ilb_sg_info_t *)&ic->ic_data,
410dbed73cbSSangeeta Misra 		    ev_port, ps, cli->cli_peer_ucredp);
411dbed73cbSSangeeta Misra 		break;
412dbed73cbSSangeeta Misra 
413dbed73cbSSangeeta Misra 	case ILBD_REM_SERVER_FROM_GROUP:
414dbed73cbSSangeeta Misra 		if ((rc = ilbd_check_req_size(ic, ic_sz)) != ILB_STATUS_OK)
415dbed73cbSSangeeta Misra 			break;
416dbed73cbSSangeeta Misra 		rc = ilbd_rem_server_from_group((ilb_sg_info_t *)&ic->ic_data,
417dbed73cbSSangeeta Misra 		    ev_port, ps, cli->cli_peer_ucredp);
418dbed73cbSSangeeta Misra 		break;
419dbed73cbSSangeeta Misra 
420dbed73cbSSangeeta Misra 	case ILBD_ENABLE_SERVER:
421dbed73cbSSangeeta Misra 		if ((rc = ilbd_check_req_size(ic, ic_sz)) != ILB_STATUS_OK)
422dbed73cbSSangeeta Misra 			break;
423dbed73cbSSangeeta Misra 		rc = ilbd_enable_server((ilb_sg_info_t *)&ic->ic_data, ps,
424dbed73cbSSangeeta Misra 		    cli->cli_peer_ucredp);
425dbed73cbSSangeeta Misra 		break;
426dbed73cbSSangeeta Misra 
427dbed73cbSSangeeta Misra 	case ILBD_DISABLE_SERVER:
428dbed73cbSSangeeta Misra 		if ((rc = ilbd_check_req_size(ic, ic_sz)) != ILB_STATUS_OK)
429dbed73cbSSangeeta Misra 			break;
430dbed73cbSSangeeta Misra 		rc = ilbd_disable_server((ilb_sg_info_t *)&ic->ic_data, ps,
431dbed73cbSSangeeta Misra 		    cli->cli_peer_ucredp);
432dbed73cbSSangeeta Misra 		break;
433dbed73cbSSangeeta Misra 
434dbed73cbSSangeeta Misra 	case ILBD_SRV_ADDR2ID:
435dbed73cbSSangeeta Misra 		rc = ilbd_address_to_srvID((ilb_sg_info_t *)&ic->ic_data,
436dbed73cbSSangeeta Misra 		    reply_buf, &rbufsz);
437dbed73cbSSangeeta Misra 		if (rc == ILB_STATUS_OK)
438dbed73cbSSangeeta Misra 			standard_reply = B_FALSE;
439dbed73cbSSangeeta Misra 		break;
440dbed73cbSSangeeta Misra 
441dbed73cbSSangeeta Misra 	case ILBD_SRV_ID2ADDR:
442dbed73cbSSangeeta Misra 		rc = ilbd_srvID_to_address((ilb_sg_info_t *)&ic->ic_data,
443dbed73cbSSangeeta Misra 		    reply_buf, &rbufsz);
444dbed73cbSSangeeta Misra 		if (rc == ILB_STATUS_OK)
445dbed73cbSSangeeta Misra 			standard_reply = B_FALSE;
446dbed73cbSSangeeta Misra 		break;
447dbed73cbSSangeeta Misra 
448dbed73cbSSangeeta Misra 	case ILBD_RETRIEVE_SG_HOSTS:
449dbed73cbSSangeeta Misra 		(void) strlcpy(name, (char *)&(ic->ic_data), sizeof (name));
450dbed73cbSSangeeta Misra 		rc = ilbd_retrieve_sg_hosts(name, reply_buf, &rbufsz);
451dbed73cbSSangeeta Misra 		if (rc == ILB_STATUS_OK)
452dbed73cbSSangeeta Misra 			standard_reply = B_FALSE;
453dbed73cbSSangeeta Misra 		break;
454dbed73cbSSangeeta Misra 
455dbed73cbSSangeeta Misra 	case ILBD_RETRIEVE_SG_NAMES:
456dbed73cbSSangeeta Misra 	case ILBD_RETRIEVE_RULE_NAMES:
457dbed73cbSSangeeta Misra 	case ILBD_RETRIEVE_HC_NAMES:
458dbed73cbSSangeeta Misra 		rc = ilbd_retrieve_names(ic->ic_cmd, reply_buf, &rbufsz);
459dbed73cbSSangeeta Misra 		if (rc == ILB_STATUS_OK)
460dbed73cbSSangeeta Misra 			standard_reply = B_FALSE;
461dbed73cbSSangeeta Misra 		break;
462dbed73cbSSangeeta Misra 
463dbed73cbSSangeeta Misra 	case ILBD_CREATE_RULE:
464dbed73cbSSangeeta Misra 		rc = ilbd_create_rule((ilb_rule_info_t *)&ic->ic_data, ev_port,
465dbed73cbSSangeeta Misra 		    ps, cli->cli_peer_ucredp);
466dbed73cbSSangeeta Misra 		break;
467dbed73cbSSangeeta Misra 
468dbed73cbSSangeeta Misra 	case ILBD_DESTROY_RULE:
469dbed73cbSSangeeta Misra 		/* Copy the name to ensure that name is NULL terminated. */
470dbed73cbSSangeeta Misra 		(void) strlcpy(name, (char *)&(ic->ic_data), sizeof (name));
471dbed73cbSSangeeta Misra 		rc = ilbd_destroy_rule(name, ps, cli->cli_peer_ucredp);
472dbed73cbSSangeeta Misra 		break;
473dbed73cbSSangeeta Misra 
474dbed73cbSSangeeta Misra 	case ILBD_ENABLE_RULE:
475dbed73cbSSangeeta Misra 		(void) strlcpy(name, (char *)&(ic->ic_data), sizeof (name));
476dbed73cbSSangeeta Misra 		rc = ilbd_enable_rule(name, ps, cli->cli_peer_ucredp);
477dbed73cbSSangeeta Misra 		break;
478dbed73cbSSangeeta Misra 
479dbed73cbSSangeeta Misra 	case ILBD_DISABLE_RULE:
480dbed73cbSSangeeta Misra 		(void) strlcpy(name, (char *)&(ic->ic_data), sizeof (name));
481dbed73cbSSangeeta Misra 		rc = ilbd_disable_rule(name, ps, cli->cli_peer_ucredp);
482dbed73cbSSangeeta Misra 		break;
483dbed73cbSSangeeta Misra 
484dbed73cbSSangeeta Misra 	case ILBD_RETRIEVE_RULE:
485dbed73cbSSangeeta Misra 		(void) strlcpy(name, (char *)&(ic->ic_data), sizeof (name));
486dbed73cbSSangeeta Misra 		rc = ilbd_retrieve_rule(name, reply_buf, &rbufsz);
487dbed73cbSSangeeta Misra 		if (rc == ILB_STATUS_OK)
488dbed73cbSSangeeta Misra 			standard_reply = B_FALSE;
489dbed73cbSSangeeta Misra 		break;
490dbed73cbSSangeeta Misra 
491dbed73cbSSangeeta Misra 	case ILBD_CREATE_HC:
492dbed73cbSSangeeta Misra 		rc = ilbd_create_hc((ilb_hc_info_t *)&ic->ic_data, ev_port, ps,
493dbed73cbSSangeeta Misra 		    cli->cli_peer_ucredp);
494dbed73cbSSangeeta Misra 		break;
495dbed73cbSSangeeta Misra 
496dbed73cbSSangeeta Misra 	case ILBD_DESTROY_HC:
497dbed73cbSSangeeta Misra 		(void) strlcpy(name, (char *)&(ic->ic_data), sizeof (name));
498dbed73cbSSangeeta Misra 		rc = ilbd_destroy_hc(name, ps, cli->cli_peer_ucredp);
499dbed73cbSSangeeta Misra 		break;
500dbed73cbSSangeeta Misra 
501dbed73cbSSangeeta Misra 	case ILBD_GET_HC_INFO:
502dbed73cbSSangeeta Misra 		(void) strlcpy(name, (char *)&(ic->ic_data), sizeof (name));
503dbed73cbSSangeeta Misra 		rc = ilbd_get_hc_info(name, reply_buf, &rbufsz);
504dbed73cbSSangeeta Misra 		if (rc == ILB_STATUS_OK)
505dbed73cbSSangeeta Misra 			standard_reply = B_FALSE;
506dbed73cbSSangeeta Misra 		break;
507dbed73cbSSangeeta Misra 
508dbed73cbSSangeeta Misra 	case ILBD_GET_HC_SRVS:
509dbed73cbSSangeeta Misra 		(void) strlcpy(name, (char *)&(ic->ic_data), sizeof (name));
510dbed73cbSSangeeta Misra 		rc = ilbd_get_hc_srvs(name, reply_buf, &rbufsz);
511dbed73cbSSangeeta Misra 		if (rc == ILB_STATUS_OK)
512dbed73cbSSangeeta Misra 			standard_reply = B_FALSE;
513dbed73cbSSangeeta Misra 		break;
514dbed73cbSSangeeta Misra 
515dbed73cbSSangeeta Misra 	case ILBD_SHOW_NAT:
516dbed73cbSSangeeta Misra 		rc = ilbd_show_nat(cli, ic, reply_buf, &rbufsz);
517dbed73cbSSangeeta Misra 		if (rc == ILB_STATUS_OK)
518dbed73cbSSangeeta Misra 			standard_reply = B_FALSE;
519dbed73cbSSangeeta Misra 		break;
520dbed73cbSSangeeta Misra 
521dbed73cbSSangeeta Misra 	case ILBD_SHOW_PERSIST:
522dbed73cbSSangeeta Misra 		rc = ilbd_show_sticky(cli, ic, reply_buf, &rbufsz);
523dbed73cbSSangeeta Misra 		if (rc == ILB_STATUS_OK)
524dbed73cbSSangeeta Misra 			standard_reply = B_FALSE;
525dbed73cbSSangeeta Misra 		break;
526dbed73cbSSangeeta Misra 
527dbed73cbSSangeeta Misra 	default:
528dbed73cbSSangeeta Misra 		logdebug("consume_common_struct: unknown command");
529dbed73cbSSangeeta Misra 		rc = ILB_STATUS_INVAL_CMD;
530dbed73cbSSangeeta Misra 		break;
531dbed73cbSSangeeta Misra 	}
532dbed73cbSSangeeta Misra 
533dbed73cbSSangeeta Misra out:
534dbed73cbSSangeeta Misra 	/*
535dbed73cbSSangeeta Misra 	 * The message exchange is always in pairs, request/response.  If
536dbed73cbSSangeeta Misra 	 * a transaction requires multiple exchanges, the client will send
537dbed73cbSSangeeta Misra 	 * in multiple requests to get multiple responses.  The show-nat and
538dbed73cbSSangeeta Misra 	 * show-persist request are examples of this.  The end of transaction
539dbed73cbSSangeeta Misra 	 * is marked with ic_flags set to ILB_COMM_END.
540dbed73cbSSangeeta Misra 	 */
541dbed73cbSSangeeta Misra 
542dbed73cbSSangeeta Misra 	/* This is the standard reply. */
543dbed73cbSSangeeta Misra 	if (standard_reply) {
544dbed73cbSSangeeta Misra 		if (rc == ILB_STATUS_OK)
545dbed73cbSSangeeta Misra 			ilbd_reply_ok(reply_buf, &rbufsz);
546dbed73cbSSangeeta Misra 		else
547dbed73cbSSangeeta Misra 			ilbd_reply_err(reply_buf, &rbufsz, rc);
548dbed73cbSSangeeta Misra 	}
549dbed73cbSSangeeta Misra 
550dbed73cbSSangeeta Misra 	if ((ret = send(cli->cli_sd, reply_buf, rbufsz, 0)) != rbufsz) {
551dbed73cbSSangeeta Misra 		if (ret == -1) {
552dbed73cbSSangeeta Misra 			if (errno != EWOULDBLOCK) {
553dbed73cbSSangeeta Misra 				logdebug("consume_common_struct: send: %s",
554dbed73cbSSangeeta Misra 				    strerror(errno));
555dbed73cbSSangeeta Misra 				rc = ILB_STATUS_SEND;
556dbed73cbSSangeeta Misra 				goto err_out;
557dbed73cbSSangeeta Misra 			}
558dbed73cbSSangeeta Misra 			/*
559dbed73cbSSangeeta Misra 			 * The reply is blocked, save the reply.  handle_req()
560dbed73cbSSangeeta Misra 			 * will associate the event port for the re-send.
561dbed73cbSSangeeta Misra 			 */
562dbed73cbSSangeeta Misra 			assert(cli->cli_saved_reply == NULL);
563dbed73cbSSangeeta Misra 			if ((cli->cli_saved_reply = malloc(rbufsz)) == NULL) {
564dbed73cbSSangeeta Misra 				/*
565dbed73cbSSangeeta Misra 				 * Set the error to ILB_STATUS_SEND so that
566dbed73cbSSangeeta Misra 				 * handle_req() will free the client.
567dbed73cbSSangeeta Misra 				 */
568dbed73cbSSangeeta Misra 				logdebug("consume_common_struct: failure to "
569dbed73cbSSangeeta Misra 				    "allocate memory to save reply");
570dbed73cbSSangeeta Misra 				rc = ILB_STATUS_SEND;
571dbed73cbSSangeeta Misra 				goto err_out;
572dbed73cbSSangeeta Misra 			}
573dbed73cbSSangeeta Misra 			bcopy(reply_buf, cli->cli_saved_reply, rbufsz);
574dbed73cbSSangeeta Misra 			cli->cli_saved_size = rbufsz;
575dbed73cbSSangeeta Misra 			return (ILB_STATUS_EWOULDBLOCK);
576dbed73cbSSangeeta Misra 		}
577dbed73cbSSangeeta Misra 	}
578dbed73cbSSangeeta Misra err_out:
579dbed73cbSSangeeta Misra 	return (rc);
580dbed73cbSSangeeta Misra }
581dbed73cbSSangeeta Misra 
582dbed73cbSSangeeta Misra /*
583dbed73cbSSangeeta Misra  * Accept a new client request.  A struct ilbd_client_t is allocated to
584dbed73cbSSangeeta Misra  * store the client info.  The accepted socket is port_associate() with
585dbed73cbSSangeeta Misra  * the given port.  And the allocated ilbd_client_t struct is passed as
586dbed73cbSSangeeta Misra  * the user pointer.
587dbed73cbSSangeeta Misra  */
588dbed73cbSSangeeta Misra static void
new_req(int ev_port,int listener,void * ev_obj)589dbed73cbSSangeeta Misra new_req(int ev_port, int listener, void *ev_obj)
590dbed73cbSSangeeta Misra {
591dbed73cbSSangeeta Misra 	struct sockaddr	sa;
592dbed73cbSSangeeta Misra 	int		sa_len;
593dbed73cbSSangeeta Misra 	int		new_sd;
594dbed73cbSSangeeta Misra 	int		sflags;
59556b8f71eSSerghei Samsi 	ilbd_client_t	*cli = NULL;
596dbed73cbSSangeeta Misra 	int		res;
597dbed73cbSSangeeta Misra 	uid_t		uid;
598dbed73cbSSangeeta Misra 
599dbed73cbSSangeeta Misra 	sa_len = sizeof (sa);
600dbed73cbSSangeeta Misra 	if ((new_sd = accept(listener, &sa, &sa_len)) == -1) {
601dbed73cbSSangeeta Misra 		/* don't log if we're out of file descriptors */
602dbed73cbSSangeeta Misra 		if (errno != EINTR && errno != EMFILE)
603dbed73cbSSangeeta Misra 			logperror("new_req: accept failed");
604dbed73cbSSangeeta Misra 		goto done;
605dbed73cbSSangeeta Misra 	}
606dbed73cbSSangeeta Misra 
607dbed73cbSSangeeta Misra 	/* Set the new socket to be non-blocking. */
608dbed73cbSSangeeta Misra 	if ((sflags = fcntl(new_sd, F_GETFL, 0)) == -1) {
609dbed73cbSSangeeta Misra 		logperror("new_req: fcntl(F_GETFL)");
610dbed73cbSSangeeta Misra 		goto clean_up;
611dbed73cbSSangeeta Misra 	}
612dbed73cbSSangeeta Misra 	if (fcntl(new_sd, F_SETFL, sflags | O_NONBLOCK) == -1) {
613dbed73cbSSangeeta Misra 		logperror("new_req: fcntl(F_SETFL)");
614dbed73cbSSangeeta Misra 		goto clean_up;
615dbed73cbSSangeeta Misra 	}
616dbed73cbSSangeeta Misra 	if (fcntl(new_sd, F_SETFD, FD_CLOEXEC) == -1) {
617dbed73cbSSangeeta Misra 		logperror("new_req: fcntl(FD_CLOEXEC)");
618dbed73cbSSangeeta Misra 		goto clean_up;
619dbed73cbSSangeeta Misra 	}
620dbed73cbSSangeeta Misra 	if ((cli = calloc(1, sizeof (ilbd_client_t))) == NULL) {
621dbed73cbSSangeeta Misra 		logerr("new_req: malloc(ilbd_client_t)");
622dbed73cbSSangeeta Misra 		goto clean_up;
623dbed73cbSSangeeta Misra 	}
624dbed73cbSSangeeta Misra 	res = getpeerucred(new_sd, &cli->cli_peer_ucredp);
625dbed73cbSSangeeta Misra 	if (res == -1) {
626dbed73cbSSangeeta Misra 		logperror("new_req: getpeerucred failed");
627dbed73cbSSangeeta Misra 		goto clean_up;
628dbed73cbSSangeeta Misra 	}
629dbed73cbSSangeeta Misra 	if ((uid = ucred_getruid(cli->cli_peer_ucredp)) == (uid_t)-1) {
630dbed73cbSSangeeta Misra 		logperror("new_req: ucred_getruid failed");
631dbed73cbSSangeeta Misra 		goto clean_up;
632dbed73cbSSangeeta Misra 	}
633dbed73cbSSangeeta Misra 	cli->cli_pw_bufsz = (size_t)sysconf(_SC_GETPW_R_SIZE_MAX);
634dbed73cbSSangeeta Misra 	if ((cli->cli_pw_buf = malloc(cli->cli_pw_bufsz)) == NULL) {
635dbed73cbSSangeeta Misra 		logerr("new_req: malloc(cli_pw_buf)");
636dbed73cbSSangeeta Misra 		goto clean_up;
637dbed73cbSSangeeta Misra 	}
638dbed73cbSSangeeta Misra 	if (getpwuid_r(uid, &cli->cli_pw, cli->cli_pw_buf,
639dbed73cbSSangeeta Misra 	    cli->cli_pw_bufsz) == NULL) {
640dbed73cbSSangeeta Misra 		logperror("new_req: invalid user");
641dbed73cbSSangeeta Misra 		goto clean_up;
642dbed73cbSSangeeta Misra 	}
643dbed73cbSSangeeta Misra 	cli->cli_ev = ILBD_EVENT_REQ;
644dbed73cbSSangeeta Misra 	cli->cli_sd = new_sd;
645dbed73cbSSangeeta Misra 	cli->cli_cmd = ILBD_BAD_CMD;
646dbed73cbSSangeeta Misra 	cli->cli_saved_reply = NULL;
647dbed73cbSSangeeta Misra 	cli->cli_saved_size = 0;
648dbed73cbSSangeeta Misra 	if (port_associate(ev_port, PORT_SOURCE_FD, new_sd, POLLRDNORM,
649dbed73cbSSangeeta Misra 	    cli) == -1) {
650dbed73cbSSangeeta Misra 		logperror("new_req: port_associate(cli) failed");
651dbed73cbSSangeeta Misra clean_up:
65256b8f71eSSerghei Samsi 		if (cli != NULL) {
65356b8f71eSSerghei Samsi 			if (cli->cli_peer_ucredp != NULL)
65456b8f71eSSerghei Samsi 				ucred_free(cli->cli_peer_ucredp);
65556b8f71eSSerghei Samsi 			free(cli->cli_pw_buf);
65656b8f71eSSerghei Samsi 			free(cli);
65756b8f71eSSerghei Samsi 		}
658dbed73cbSSangeeta Misra 		(void) close(new_sd);
659dbed73cbSSangeeta Misra 	}
660dbed73cbSSangeeta Misra 
661dbed73cbSSangeeta Misra done:
662dbed73cbSSangeeta Misra 	/* Re-associate the listener with the event port. */
663dbed73cbSSangeeta Misra 	if (port_associate(ev_port, PORT_SOURCE_FD, listener, POLLRDNORM,
664dbed73cbSSangeeta Misra 	    ev_obj) == -1) {
665dbed73cbSSangeeta Misra 		logperror("new_req: port_associate(listener) failed");
666dbed73cbSSangeeta Misra 		exit(1);
667dbed73cbSSangeeta Misra 	}
668dbed73cbSSangeeta Misra }
669dbed73cbSSangeeta Misra 
670dbed73cbSSangeeta Misra static void
handle_req(int ev_port,ilbd_event_t event,ilbd_client_t * cli)671dbed73cbSSangeeta Misra handle_req(int ev_port, ilbd_event_t event, ilbd_client_t *cli)
672dbed73cbSSangeeta Misra {
673dbed73cbSSangeeta Misra 	/* All request should be smaller than ILBD_MSG_SIZE */
674dbed73cbSSangeeta Misra 	union {
675dbed73cbSSangeeta Misra 		ilb_comm_t	ic;
676dbed73cbSSangeeta Misra 		uint32_t	buf[ILBD_MSG_SIZE / sizeof (uint32_t)];
677dbed73cbSSangeeta Misra 	} ic_u;
678dbed73cbSSangeeta Misra 	int	rc = ILB_STATUS_OK;
679dbed73cbSSangeeta Misra 	ssize_t	r;
680dbed73cbSSangeeta Misra 
681dbed73cbSSangeeta Misra 	if (event == ILBD_EVENT_REQ) {
682dbed73cbSSangeeta Misra 		/*
683dbed73cbSSangeeta Misra 		 * Something is wrong with the client since there is a
684dbed73cbSSangeeta Misra 		 * pending reply, the client should not send us another
685dbed73cbSSangeeta Misra 		 * request.  Kill this client.
686dbed73cbSSangeeta Misra 		 */
687dbed73cbSSangeeta Misra 		if (cli->cli_saved_reply != NULL) {
688dbed73cbSSangeeta Misra 			logerr("handle_req: misbehaving client, more than one "
689dbed73cbSSangeeta Misra 			    "outstanding request");
690dbed73cbSSangeeta Misra 			rc = ILB_STATUS_INTERNAL;
691dbed73cbSSangeeta Misra 			goto err_out;
692dbed73cbSSangeeta Misra 		}
693dbed73cbSSangeeta Misra 
694dbed73cbSSangeeta Misra 		/*
695dbed73cbSSangeeta Misra 		 * Our socket is message based so we should be able
696dbed73cbSSangeeta Misra 		 * to get the request in one single read.
697dbed73cbSSangeeta Misra 		 */
698dbed73cbSSangeeta Misra 		r = recv(cli->cli_sd, (void *)ic_u.buf, sizeof (ic_u.buf), 0);
699dbed73cbSSangeeta Misra 		if (r < 0) {
700dbed73cbSSangeeta Misra 			if (errno != EINTR) {
701dbed73cbSSangeeta Misra 				logperror("handle_req: read failed");
702dbed73cbSSangeeta Misra 				rc = ILB_STATUS_READ;
703dbed73cbSSangeeta Misra 				goto err_out;
704dbed73cbSSangeeta Misra 			}
705dbed73cbSSangeeta Misra 			/*
706dbed73cbSSangeeta Misra 			 * If interrupted, just re-associate the cli_sd
707dbed73cbSSangeeta Misra 			 * with the port.
708dbed73cbSSangeeta Misra 			 */
709dbed73cbSSangeeta Misra 			goto done;
710dbed73cbSSangeeta Misra 		}
711dbed73cbSSangeeta Misra 		cli->cli_cmd = ic_u.ic.ic_cmd;
712dbed73cbSSangeeta Misra 
713dbed73cbSSangeeta Misra 		rc = consume_common_struct(&ic_u.ic, r, cli, ev_port);
714dbed73cbSSangeeta Misra 		if (rc == ILB_STATUS_EWOULDBLOCK)
715dbed73cbSSangeeta Misra 			goto blocked;
716dbed73cbSSangeeta Misra 		/* Fatal error communicating with client, free it. */
717dbed73cbSSangeeta Misra 		if (rc == ILB_STATUS_SEND)
718dbed73cbSSangeeta Misra 			goto err_out;
719dbed73cbSSangeeta Misra 	} else {
720dbed73cbSSangeeta Misra 		assert(event == ILBD_EVENT_REP_OK);
721dbed73cbSSangeeta Misra 		assert(cli->cli_saved_reply != NULL);
722dbed73cbSSangeeta Misra 
723dbed73cbSSangeeta Misra 		/*
724dbed73cbSSangeeta Misra 		 * The reply to client was previously blocked, we will
725dbed73cbSSangeeta Misra 		 * send again.
726dbed73cbSSangeeta Misra 		 */
727dbed73cbSSangeeta Misra 		if (send(cli->cli_sd, cli->cli_saved_reply,
728dbed73cbSSangeeta Misra 		    cli->cli_saved_size, 0) != cli->cli_saved_size) {
729dbed73cbSSangeeta Misra 			if (errno != EWOULDBLOCK) {
730dbed73cbSSangeeta Misra 				logdebug("handle_req: send: %s",
731dbed73cbSSangeeta Misra 				    strerror(errno));
732dbed73cbSSangeeta Misra 				rc = ILB_STATUS_SEND;
733dbed73cbSSangeeta Misra 				goto err_out;
734dbed73cbSSangeeta Misra 			}
735dbed73cbSSangeeta Misra 			goto blocked;
736dbed73cbSSangeeta Misra 		}
737dbed73cbSSangeeta Misra 		free(cli->cli_saved_reply);
738dbed73cbSSangeeta Misra 		cli->cli_saved_reply = NULL;
739dbed73cbSSangeeta Misra 		cli->cli_saved_size = 0;
740dbed73cbSSangeeta Misra 	}
741dbed73cbSSangeeta Misra done:
742dbed73cbSSangeeta Misra 	/* Re-associate with the event port for more requests. */
743dbed73cbSSangeeta Misra 	cli->cli_ev = ILBD_EVENT_REQ;
744dbed73cbSSangeeta Misra 	if (port_associate(ev_port, PORT_SOURCE_FD, cli->cli_sd,
745dbed73cbSSangeeta Misra 	    POLLRDNORM, cli) == -1) {
746dbed73cbSSangeeta Misra 		logperror("handle_req: port_associate(POLLRDNORM)");
747dbed73cbSSangeeta Misra 		rc = ILB_STATUS_INTERNAL;
748dbed73cbSSangeeta Misra 		goto err_out;
749dbed73cbSSangeeta Misra 	}
750dbed73cbSSangeeta Misra 	return;
751dbed73cbSSangeeta Misra 
752dbed73cbSSangeeta Misra blocked:
753dbed73cbSSangeeta Misra 	/* Re-associate with the event port. */
754dbed73cbSSangeeta Misra 	cli->cli_ev = ILBD_EVENT_REP_OK;
755dbed73cbSSangeeta Misra 	if (port_associate(ev_port, PORT_SOURCE_FD, cli->cli_sd, POLLWRNORM,
756dbed73cbSSangeeta Misra 	    cli) == -1) {
757dbed73cbSSangeeta Misra 		logperror("handle_req: port_associate(POLLWRNORM)");
758dbed73cbSSangeeta Misra 		rc = ILB_STATUS_INTERNAL;
759dbed73cbSSangeeta Misra 		goto err_out;
760dbed73cbSSangeeta Misra 	}
761dbed73cbSSangeeta Misra 	return;
762dbed73cbSSangeeta Misra 
763dbed73cbSSangeeta Misra err_out:
764dbed73cbSSangeeta Misra 	ilbd_free_cli(cli);
765dbed73cbSSangeeta Misra }
766dbed73cbSSangeeta Misra 
767dbed73cbSSangeeta Misra static void
i_ilbd_read_config(int ev_port)768dbed73cbSSangeeta Misra i_ilbd_read_config(int ev_port)
769dbed73cbSSangeeta Misra {
770dbed73cbSSangeeta Misra 	logdebug("i_ilbd_read_config: port %d", ev_port);
771dbed73cbSSangeeta Misra 	(void) ilbd_walk_sg_pgs(ilbd_create_sg, &ev_port, NULL);
772dbed73cbSSangeeta Misra 	(void) ilbd_walk_hc_pgs(ilbd_create_hc, &ev_port, NULL);
773dbed73cbSSangeeta Misra 	(void) ilbd_walk_rule_pgs(ilbd_create_rule, &ev_port, NULL);
774dbed73cbSSangeeta Misra }
775dbed73cbSSangeeta Misra 
776dbed73cbSSangeeta Misra /*
777dbed73cbSSangeeta Misra  * main event loop for ilbd
778dbed73cbSSangeeta Misra  * asserts that argument 'listener' is a server socket ready to accept() on.
779dbed73cbSSangeeta Misra  */
780dbed73cbSSangeeta Misra static void
main_loop(int listener)781dbed73cbSSangeeta Misra main_loop(int listener)
782dbed73cbSSangeeta Misra {
783dbed73cbSSangeeta Misra 	port_event_t		p_ev;
784dbed73cbSSangeeta Misra 	int			ev_port, ev_port_obj;
785dbed73cbSSangeeta Misra 	ilbd_event_obj_t	ev_obj;
786dbed73cbSSangeeta Misra 	ilbd_timer_event_obj_t	timer_ev_obj;
787dbed73cbSSangeeta Misra 
788dbed73cbSSangeeta Misra 	ev_port = port_create();
789dbed73cbSSangeeta Misra 	if (ev_port == -1) {
790dbed73cbSSangeeta Misra 		logperror("main_loop: port_create failed");
791dbed73cbSSangeeta Misra 		exit(-1);
792dbed73cbSSangeeta Misra 	}
793dbed73cbSSangeeta Misra 	ilbd_hc_timer_init(ev_port, &timer_ev_obj);
794dbed73cbSSangeeta Misra 
795dbed73cbSSangeeta Misra 	ev_obj.ev = ILBD_EVENT_NEW_REQ;
796dbed73cbSSangeeta Misra 	if (port_associate(ev_port, PORT_SOURCE_FD, listener, POLLRDNORM,
797dbed73cbSSangeeta Misra 	    &ev_obj) == -1) {
798dbed73cbSSangeeta Misra 		logperror("main_loop: port_associate failed");
799dbed73cbSSangeeta Misra 		exit(1);
800dbed73cbSSangeeta Misra 	}
801dbed73cbSSangeeta Misra 
802dbed73cbSSangeeta Misra 	i_ilbd_read_config(ev_port);
803dbed73cbSSangeeta Misra 	ilbd_hc_timer_update(&timer_ev_obj);
804dbed73cbSSangeeta Misra 
805dbed73cbSSangeeta Misra 	_NOTE(CONSTCOND)
806dbed73cbSSangeeta Misra 	while (B_TRUE) {
807dbed73cbSSangeeta Misra 		int r;
808dbed73cbSSangeeta Misra 		ilbd_event_t event;
809dbed73cbSSangeeta Misra 		ilbd_client_t *cli;
810dbed73cbSSangeeta Misra 
811dbed73cbSSangeeta Misra 		r = port_get(ev_port, &p_ev, NULL);
812dbed73cbSSangeeta Misra 		if (r == -1) {
813dbed73cbSSangeeta Misra 			if (errno == EINTR)
814dbed73cbSSangeeta Misra 				continue;
815dbed73cbSSangeeta Misra 			logperror("main_loop: port_get failed");
816dbed73cbSSangeeta Misra 			break;
817dbed73cbSSangeeta Misra 		}
818dbed73cbSSangeeta Misra 
819dbed73cbSSangeeta Misra 		ev_port_obj = p_ev.portev_object;
820dbed73cbSSangeeta Misra 		event = ((ilbd_event_obj_t *)p_ev.portev_user)->ev;
821dbed73cbSSangeeta Misra 
822dbed73cbSSangeeta Misra 		switch (event) {
823dbed73cbSSangeeta Misra 		case ILBD_EVENT_TIMER:
824dbed73cbSSangeeta Misra 			ilbd_hc_timeout();
825dbed73cbSSangeeta Misra 			break;
826dbed73cbSSangeeta Misra 
827dbed73cbSSangeeta Misra 		case ILBD_EVENT_PROBE:
828dbed73cbSSangeeta Misra 			ilbd_hc_probe_return(ev_port, ev_port_obj,
829dbed73cbSSangeeta Misra 			    p_ev.portev_events,
830dbed73cbSSangeeta Misra 			    (ilbd_hc_probe_event_t *)p_ev.portev_user);
831dbed73cbSSangeeta Misra 			break;
832dbed73cbSSangeeta Misra 
833dbed73cbSSangeeta Misra 		case ILBD_EVENT_NEW_REQ:
834dbed73cbSSangeeta Misra 			assert(ev_port_obj == listener);
835dbed73cbSSangeeta Misra 			/*
836dbed73cbSSangeeta Misra 			 * An error happens in the listener.  Exit