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