xref: /illumos-gate/usr/src/cmd/cmd-inet/usr.lib/ilbd/ilbd_sg.c (revision 8119dad84d6416f13557b0ba8e2aaf9064cbcfd3)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
24  * Copyright (c) 2016 by Delphix. All rights reserved.
25  */
26 
27 #include <stdlib.h>
28 #include <strings.h>
29 #include <stddef.h>
30 #include <sys/types.h>
31 #include <sys/socket.h>
32 #include <sys/list.h>
33 #include <assert.h>
34 #include <errno.h>
35 #include <libilb.h>
36 #include <net/if.h>
37 #include <inet/ilb.h>
38 #include <netinet/in.h>
39 #include <arpa/inet.h>
40 #include "libilb_impl.h"
41 #include "ilbd.h"
42 
43 typedef enum {
44 	not_searched,
45 	stop_found,
46 	cont_search,
47 	fail_search
48 } srch_ind_t;
49 
50 static list_t	ilbd_sg_hlist;
51 
52 static ilb_status_t i_delete_srv(ilbd_sg_t *, ilbd_srv_t *, int);
53 static void i_ilbd_free_srvID(ilbd_sg_t *, int32_t);
54 
55 /* Last parameter to pass to i_find_srv(), specifying the matching mode */
56 #define	MODE_ADDR	1
57 #define	MODE_SRVID	2
58 
59 static ilbd_srv_t *i_find_srv(list_t *, ilb_sg_srv_t *, int);
60 
61 void
62 i_setup_sg_hlist(void)
63 {
64 	list_create(&ilbd_sg_hlist, sizeof (ilbd_sg_t),
65 	    offsetof(ilbd_sg_t, isg_link));
66 }
67 
68 /*
69  * allocate storage for a daemon-internal server group, init counters
70  */
71 static ilbd_sg_t *
72 i_ilbd_alloc_sg(char *name)
73 {
74 	ilbd_sg_t	*d_sg;
75 
76 	d_sg = calloc(sizeof (*d_sg), 1);
77 	if (d_sg == NULL)
78 		goto out;
79 
80 	(void) strlcpy(d_sg->isg_name, name, sizeof (d_sg->isg_name));
81 
82 	list_create(&d_sg->isg_srvlist, sizeof (ilbd_srv_t),
83 	    offsetof(ilbd_srv_t, isv_srv_link));
84 	list_create(&d_sg->isg_rulelist, sizeof (ilbd_rule_t),
85 	    offsetof(ilbd_rule_t, irl_sglink));
86 
87 	list_insert_tail(&ilbd_sg_hlist, d_sg);
88 out:
89 	return (d_sg);
90 }
91 
92 static ilb_status_t
93 i_ilbd_save_sg(ilbd_sg_t *d_sg, ilbd_scf_cmd_t scf_cmd, const char *prop_name,
94     char *valstr)
95 {
96 	switch (scf_cmd) {
97 	case ILBD_SCF_CREATE:
98 		return (ilbd_create_pg(ILBD_SCF_SG, (void *)d_sg));
99 	case ILBD_SCF_DESTROY:
100 		return (ilbd_destroy_pg(ILBD_SCF_SG, d_sg->isg_name));
101 	case ILBD_SCF_ENABLE_DISABLE:
102 		if (prop_name == NULL)
103 			return (ILB_STATUS_EINVAL);
104 		return (ilbd_change_prop(ILBD_SCF_SG, d_sg->isg_name,
105 		    prop_name, valstr));
106 	default:
107 		logdebug("i_ilbd_save_sg: invalid scf cmd %d", scf_cmd);
108 		return (ILB_STATUS_EINVAL);
109 	}
110 }
111 
112 ilb_status_t
113 i_attach_rule2sg(ilbd_sg_t *sg, ilbd_rule_t *irl)
114 {
115 	/* assert: the same rule is attached to any sg only once */
116 	list_insert_tail(&sg->isg_rulelist, irl);
117 	return (ILB_STATUS_OK);
118 }
119 
120 static void
121 i_ilbd_free_sg(ilbd_sg_t *sg)
122 {
123 	ilbd_srv_t *tmp_srv;
124 
125 	if (sg == NULL)
126 		return;
127 	list_remove(&ilbd_sg_hlist, sg);
128 	while ((tmp_srv = list_remove_tail(&sg->isg_srvlist)) != NULL) {
129 		i_ilbd_free_srvID(sg, tmp_srv->isv_id);
130 		free(tmp_srv);
131 		sg->isg_srvcount--;
132 	}
133 	free(sg);
134 }
135 
136 ilbd_sg_t *
137 i_find_sg_byname(const char *name)
138 {
139 	ilbd_sg_t *sg;
140 
141 	/* find position of sg in list */
142 	for (sg = list_head(&ilbd_sg_hlist); sg != NULL;
143 	    sg = list_next(&ilbd_sg_hlist, sg)) {
144 		if (strncmp(sg->isg_name, name, sizeof (sg->isg_name)) == 0)
145 			return (sg);
146 	}
147 	return (sg);
148 }
149 
150 /*
151  * Generates an audit record for enable-server, disable-server, remove-server
152  * delete-servergroup, create-servergroup and add-server subcommands.
153  */
154 static void
155 ilbd_audit_server_event(audit_sg_event_data_t *data,
156     ilbd_cmd_t cmd, ilb_status_t rc, ucred_t *ucredp)
157 {
158 	adt_session_data_t	*ah;
159 	adt_event_data_t	*event;
160 	au_event_t	flag;
161 	int	audit_error;
162 
163 	if ((ucredp == NULL) && ((cmd == ILBD_ADD_SERVER_TO_GROUP) ||
164 	    (cmd == ILBD_CREATE_SERVERGROUP)))  {
165 		/*
166 		 * We came here from the path where ilbd is
167 		 * incorporating the ILB configuration from
168 		 * SCF. In that case, we skip auditing
169 		 */
170 		return;
171 	}
172 
173 	if (adt_start_session(&ah, NULL, 0) != 0) {
174 		logerr("ilbd_audit_server_event: adt_start_session failed");
175 		exit(EXIT_FAILURE);
176 	}
177 
178 	if (adt_set_from_ucred(ah, ucredp, ADT_NEW) != 0) {
179 		(void) adt_end_session(ah);
180 		logerr("ilbd_audit_server_event: adt_set_from_ucred failed");
181 		exit(EXIT_FAILURE);
182 	}
183 
184 	if (cmd == ILBD_ENABLE_SERVER)
185 		flag = ADT_ilb_enable_server;
186 	else if (cmd == ILBD_DISABLE_SERVER)
187 		flag = ADT_ilb_disable_server;
188 	else if (cmd == ILBD_REM_SERVER_FROM_GROUP)
189 		flag = ADT_ilb_remove_server;
190 	else if (cmd == ILBD_ADD_SERVER_TO_GROUP)
191 		flag = ADT_ilb_add_server;
192 	else if (cmd == ILBD_CREATE_SERVERGROUP)
193 		flag = ADT_ilb_create_servergroup;
194 	else if (cmd == ILBD_DESTROY_SERVERGROUP)
195 		flag = ADT_ilb_delete_servergroup;
196 
197 	if ((event = adt_alloc_event(ah, flag)) == NULL) {
198 		logerr("ilbd_audit_server_event: adt_alloc_event failed");
199 		exit(EXIT_FAILURE);
200 	}
201 	(void) memset((char *)event, 0, sizeof (adt_event_data_t));
202 
203 	switch (cmd) {
204 	case ILBD_ENABLE_SERVER:
205 		event->adt_ilb_enable_server.auth_used =
206 		    NET_ILB_ENABLE_AUTH;
207 		event->adt_ilb_enable_server.server_id =
208 		    data->ed_serverid;
209 		event->adt_ilb_enable_server.server_ipaddress_type =
210 		    data->ed_ipaddr_type;
211 		(void) memcpy(event->adt_ilb_enable_server.server_ipaddress,
212 		    data->ed_server_address,
213 		    (sizeof (data->ed_server_address)));
214 		break;
215 	case ILBD_DISABLE_SERVER:
216 		event->adt_ilb_disable_server.auth_used =
217 		    NET_ILB_ENABLE_AUTH;
218 		event->adt_ilb_disable_server.server_id =
219 		    data->ed_serverid;
220 		event->adt_ilb_disable_server.server_ipaddress_type =
221 		    data->ed_ipaddr_type;
222 		(void) memcpy(event->adt_ilb_disable_server.server_ipaddress,
223 		    data->ed_server_address,
224 		    (sizeof (data->ed_server_address)));
225 		break;
226 	case ILBD_REM_SERVER_FROM_GROUP:
227 		event->adt_ilb_remove_server.auth_used =
228 		    NET_ILB_CONFIG_AUTH;
229 		event->adt_ilb_remove_server.server_id =
230 		    data->ed_serverid;
231 		event->adt_ilb_remove_server.server_group = data->ed_sgroup;
232 		event->adt_ilb_remove_server.server_ipaddress_type =
233 		    data->ed_ipaddr_type;
234 		(void) memcpy(event->adt_ilb_remove_server.server_ipaddress,
235 		    data->ed_server_address,
236 		    (sizeof (data->ed_server_address)));
237 		break;
238 	case ILBD_CREATE_SERVERGROUP:
239 		event->adt_ilb_create_servergroup.auth_used =
240 		    NET_ILB_CONFIG_AUTH;
241 		event->adt_ilb_create_servergroup.server_group =
242 		    data->ed_sgroup;
243 		break;
244 	case ILBD_ADD_SERVER_TO_GROUP:
245 		event->adt_ilb_add_server.auth_used =
246 		    NET_ILB_CONFIG_AUTH;
247 		event->adt_ilb_add_server.server_ipaddress_type =
248 		    data->ed_ipaddr_type;
249 		(void) memcpy(event->adt_ilb_add_server.server_ipaddress,
250 		    data->ed_server_address,
251 		    (sizeof (data->ed_server_address)));
252 		event->adt_ilb_add_server.server_id =
253 		    data->ed_serverid;
254 		event->adt_ilb_add_server.server_group =
255 		    data->ed_sgroup;
256 		event->adt_ilb_add_server.server_minport =
257 		    ntohs(data->ed_minport);
258 		event->adt_ilb_add_server.server_maxport =
259 		    ntohs(data->ed_maxport);
260 		break;
261 	case ILBD_DESTROY_SERVERGROUP:
262 		event->adt_ilb_delete_servergroup.auth_used =
263 		    NET_ILB_CONFIG_AUTH;
264 		event->adt_ilb_delete_servergroup.server_group =
265 		    data->ed_sgroup;
266 		break;
267 	}
268 
269 	/* Fill in success/failure */
270 	if (rc == ILB_STATUS_OK) {
271 		if (adt_put_event(event, ADT_SUCCESS, ADT_SUCCESS) != 0) {
272 			logerr("ilbd_audit_server_event:"
273 			    " adt_put_event failed");
274 			exit(EXIT_FAILURE);
275 		}
276 	} else {
277 		audit_error = ilberror2auditerror(rc);
278 		if (adt_put_event(event, ADT_FAILURE, audit_error) != 0) {
279 			logerr("ilbd_audit_server_event:"
280 			    " adt_put_event failed");
281 			exit(EXIT_FAILURE);
282 		}
283 	}
284 	adt_free_event(event);
285 	(void) adt_end_session(ah);
286 }
287 
288 ilb_status_t
289 ilbd_destroy_sg(const char *sg_name, const struct passwd *ps,
290     ucred_t *ucredp)
291 {
292 	ilb_status_t	rc;
293 	ilbd_sg_t	*tmp_sg;
294 	audit_sg_event_data_t   audit_sg_data;
295 
296 	(void) memset(&audit_sg_data, 0, sizeof (audit_sg_event_data_t));
297 	audit_sg_data.ed_sgroup = (char *)sg_name;
298 
299 	rc = ilbd_check_client_config_auth(ps);
300 	if (rc != ILB_STATUS_OK) {
301 		ilbd_audit_server_event(&audit_sg_data,
302 		    ILBD_DESTROY_SERVERGROUP, rc, ucredp);
303 		return (rc);
304 	}
305 
306 	tmp_sg = i_find_sg_byname(sg_name);
307 	if (tmp_sg == NULL) {
308 		logdebug("ilbd_destroy_sg: cannot find specified server"
309 		    " group %s", sg_name);
310 		ilbd_audit_server_event(&audit_sg_data,
311 		    ILBD_DESTROY_SERVERGROUP, ILB_STATUS_SGUNAVAIL, ucredp);
312 		return (ILB_STATUS_SGUNAVAIL);
313 	}
314 
315 	/*
316 	 * we only destroy SGs that don't have any rules associated with
317 	 * them anymore.
318 	 */
319 	if (list_head(&tmp_sg->isg_rulelist) != NULL) {
320 		logdebug("ilbd_destroy_sg: server group %s has rules"
321 		" associated with it and thus cannot be"
322 		    " removed", tmp_sg->isg_name);
323 		ilbd_audit_server_event(&audit_sg_data,
324 		    ILBD_DESTROY_SERVERGROUP, ILB_STATUS_SGINUSE, ucredp);
325 		return (ILB_STATUS_SGINUSE);
326 	}
327 
328 	if (ps != NULL) {
329 		rc = i_ilbd_save_sg(tmp_sg, ILBD_SCF_DESTROY, NULL, NULL);
330 		if (rc != ILB_STATUS_OK) {
331 		ilbd_audit_server_event(&audit_sg_data,
332 		    ILBD_DESTROY_SERVERGROUP, rc, ucredp);
333 			return (rc);
334 		}
335 	}
336 	i_ilbd_free_sg(tmp_sg);
337 	ilbd_audit_server_event(&audit_sg_data, ILBD_DESTROY_SERVERGROUP,
338 	    rc, ucredp);
339 	return (rc);
340 }
341 
342 /* ARGSUSED */
343 /*
344  * Parameter ev_port is not used but has to have for read persistent configure
345  * ilbd_create_sg(), ilbd_create_hc() and ilbd_create_rule() are callbacks
346  * for ilbd_scf_instance_walk_pg() which requires the same signature.
347  */
348 ilb_status_t
349 ilbd_create_sg(ilb_sg_info_t *sg, int ev_port, const struct passwd *ps,
350     ucred_t *ucredp)
351 {
352 	ilb_status_t	rc = ILB_STATUS_OK;
353 	ilbd_sg_t	*d_sg;
354 	audit_sg_event_data_t   audit_sg_data;
355 
356 	(void) memset(&audit_sg_data, 0, sizeof (audit_sg_event_data_t));
357 	audit_sg_data.ed_sgroup = sg->sg_name;
358 
359 	if (ps != NULL) {
360 		rc = ilbd_check_client_config_auth(ps);
361 		if (rc != ILB_STATUS_OK) {
362 			ilbd_audit_server_event(&audit_sg_data,
363 			    ILBD_CREATE_SERVERGROUP, rc, ucredp);
364 			return (rc);
365 		}
366 	}
367 
368 	if (i_find_sg_byname(sg->sg_name) != NULL) {
369 		logdebug("ilbd_create_sg: server group %s already exists",
370 		    sg->sg_name);
371 		ilbd_audit_server_event(&audit_sg_data,
372 		    ILBD_CREATE_SERVERGROUP, ILB_STATUS_SGEXISTS, ucredp);
373 		return (ILB_STATUS_SGEXISTS);
374 	}
375 
376 	d_sg = i_ilbd_alloc_sg(sg->sg_name);
377 	if (d_sg == NULL) {
378 		ilbd_audit_server_event(&audit_sg_data,
379 		    ILBD_CREATE_SERVERGROUP, ILB_STATUS_ENOMEM, ucredp);
380 		return (ILB_STATUS_ENOMEM);
381 	}
382 
383 	/*
384 	 * we've successfully created the sg in memory. Before we can
385 	 * return "success", we need to reflect this in persistent
386 	 * storage
387 	 */
388 	if (ps != NULL) {
389 		rc = i_ilbd_save_sg(d_sg, ILBD_SCF_CREATE, NULL, NULL);
390 		if (rc != ILB_STATUS_OK) {
391 			i_ilbd_free_sg(d_sg);
392 			ilbd_audit_server_event(&audit_sg_data,
393 			    ILBD_CREATE_SERVERGROUP, rc, ucredp);
394 			return (rc);
395 		}
396 	}
397 	ilbd_audit_server_event(&audit_sg_data,
398 	    ILBD_CREATE_SERVERGROUP, rc, ucredp);
399 	return (rc);
400 }
401 
402 /*
403  * This function checks whether tsrv should/can be inserted before lsrv
404  * and does so if possible.
405  * We keep the list in sorted order so we don't have to search it
406  * in its entirety for overlap every time we insert a new server.
407  * Return code:
408  *	stop_found: don't continue searching because we found a place
409  *	cont_search: continue with next element in the list
410  *	fail_search: search failed (caller translates to ILB_STATUS_EEXIST)
411  */
412 static srch_ind_t
413 i_test_and_insert(ilbd_srv_t *tsrv, ilbd_srv_t *lsrv, list_t *srvlist)
414 {
415 	struct in6_addr	*t1, *l1;
416 	int		fnd;
417 
418 	t1 = &tsrv->isv_addr;
419 	l1 = &lsrv->isv_addr;
420 
421 	if ((fnd = ilb_cmp_in6_addr(t1, l1, NULL)) == 1)
422 		return (cont_search);	/* search can continue */
423 
424 	if (fnd == 0) {
425 		logdebug("i_test_and_insert: specified server already exists");
426 		return (fail_search);
427 	}
428 	/* the list is kept in ascending order */
429 	list_insert_before(srvlist, lsrv, tsrv);
430 	return (stop_found);
431 }
432 
433 
434 /*
435  * copy a server description [ip1,ip2,port1,port2,srvID,flags]
436  */
437 #define	COPY_SERVER(src, dest)					\
438 	(dest)->sgs_addr = (src)->sgs_addr;			\
439 	(dest)->sgs_minport = (src)->sgs_minport;		\
440 	(dest)->sgs_maxport = (src)->sgs_maxport;		\
441 	(dest)->sgs_id = (src)->sgs_id;				\
442 	(void) strlcpy((dest)->sgs_srvID, (src)->sgs_srvID,	\
443 	    sizeof ((dest)->sgs_srvID));			\
444 	(dest)->sgs_flags = (src)->sgs_flags
445 
446 static ilb_status_t
447 i_add_srv2sg(ilbd_sg_t *dsg, ilb_sg_srv_t *srv, ilbd_srv_t **ret_srv)
448 {
449 	ilb_sg_srv_t	*n_sg_srv;
450 	list_t		*srvlist;
451 	srch_ind_t	search = not_searched;
452 	ilb_status_t	rc = ILB_STATUS_OK;
453 	ilbd_srv_t	*nsrv, *lsrv;
454 	in_port_t	h_minport, h_maxport;
455 
456 	nsrv = calloc(sizeof (*nsrv), 1);
457 	if (nsrv == NULL)
458 		return (ILB_STATUS_ENOMEM);
459 	n_sg_srv = &nsrv->isv_srv;
460 	COPY_SERVER(srv, n_sg_srv);
461 
462 	/*
463 	 * port info is in network byte order - we need host byte order
464 	 * for comparisons purposes
465 	 */
466 	h_minport = ntohs(n_sg_srv->sgs_minport);
467 	h_maxport = ntohs(n_sg_srv->sgs_maxport);
468 	if (h_minport != 0 && h_minport > h_maxport)
469 		n_sg_srv->sgs_maxport = n_sg_srv->sgs_minport;
470 
471 	srvlist = &dsg->isg_srvlist;
472 
473 	lsrv = list_head(srvlist);
474 	if (lsrv == NULL) {
475 		list_insert_head(srvlist, nsrv);
476 	} else {
477 		while (lsrv != NULL) {
478 			search = i_test_and_insert(nsrv, lsrv,
479 			    srvlist);
480 
481 			if (search != cont_search)
482 				break;
483 			lsrv = list_next(srvlist, lsrv);
484 
485 			/* if reaches the end of list, insert to the tail */
486 			if (search == cont_search && lsrv == NULL)
487 				list_insert_tail(srvlist, nsrv);
488 		}
489 		if (search == fail_search)
490 			rc = ILB_STATUS_EEXIST;
491 	}
492 
493 	if (rc == ILB_STATUS_OK) {
494 		dsg->isg_srvcount++;
495 		*ret_srv = nsrv;
496 	} else {
497 		free(nsrv);
498 	}
499 
500 	return (rc);
501 }
502 
503 /*
504  * Allocate a server ID.  The algorithm is simple.  Just check the ID array
505  * of the server group and find an unused ID.  If *set_id is given, it
506  * means that the ID is already allocated and the ID array needs to be
507  * updated.  This is the case when ilbd reads from the persistent
508  * configuration.
509  */
510 static int32_t
511 i_ilbd_alloc_srvID(ilbd_sg_t *sg, int32_t *set_id)
512 {
513 	int32_t		id;
514 	int32_t		i;
515 
516 	/* The server ID is already allocated, just update the ID array. */
517 	if (set_id != NULL) {
518 		assert(sg->isg_id_arr[*set_id] == 0);
519 		sg->isg_id_arr[*set_id] = 1;
520 		return (*set_id);
521 	}
522 
523 	/* if we're "full up", give back something invalid */
524 	if (sg->isg_srvcount == MAX_SRVCOUNT)
525 		return (BAD_SRVID);
526 
527 	i = sg->isg_max_id;
528 	for (id = 0; id < MAX_SRVCOUNT; id++) {
529 		if (sg->isg_id_arr[(id + i) % MAX_SRVCOUNT] == 0)
530 			break;
531 	}
532 
533 	sg->isg_max_id = (id + i) % MAX_SRVCOUNT;
534 	sg->isg_id_arr[sg->isg_max_id] = 1;
535 	return (sg->isg_max_id);
536 }
537 
538 /*
539  * Free a server ID by updating the server group's ID array.
540  */
541 static void
542 i_ilbd_free_srvID(ilbd_sg_t *sg, int32_t id)
543 {
544 	assert(sg->isg_id_arr[id] == 1);
545 	sg->isg_id_arr[id] = 0;
546 }
547 
548 /*
549  * This function is called by ilbd_add_server_to_group() and
550  * ilb_remove_server_group() to create a audit record for a
551  * failed servicing of add-server/remove-server command
552  */
553 static void
554 fill_audit_record(ilb_sg_info_t *sg, audit_sg_event_data_t *audit_sg_data,
555     ilbd_cmd_t cmd, ilb_status_t rc, ucred_t *ucredp)
556 {
557 	ilb_sg_srv_t	*tsrv;
558 	int	i;
559 
560 	for (i = 0; i < sg->sg_srvcount; i++) {
561 		tsrv = &sg->sg_servers[i];
562 		if (cmd == ILBD_ADD_SERVER_TO_GROUP)  {
563 
564 			audit_sg_data->ed_serverid = NULL;
565 			if (IN6_IS_ADDR_V4MAPPED(&tsrv->sgs_addr)) {
566 				audit_sg_data->ed_ipaddr_type = ADT_IPv4;
567 				cvt_addr(audit_sg_data->ed_server_address,
568 				    ADT_IPv4, tsrv->sgs_addr);
569 			} else {
570 				audit_sg_data->ed_ipaddr_type = ADT_IPv6;
571 				cvt_addr(audit_sg_data->ed_server_address,
572 				    ADT_IPv6, tsrv->sgs_addr);
573 			}
574 			audit_sg_data->ed_minport = tsrv->sgs_minport;
575 			audit_sg_data->ed_maxport = tsrv->sgs_maxport;
576 			audit_sg_data->ed_sgroup = sg->sg_name;
577 		} else if (cmd == ILBD_REM_SERVER_FROM_GROUP) {
578 			audit_sg_data->ed_serverid = tsrv->sgs_srvID;
579 			audit_sg_data->ed_sgroup = sg->sg_name;
580 
581 			audit_sg_data->ed_minport = 0;
582 			audit_sg_data->ed_maxport = 0;
583 		}
584 		ilbd_audit_server_event(audit_sg_data, cmd, rc, ucredp);
585 	}
586 }
587 
588 /*
589  * the name(s) of the server(s) are encoded in the sg.
590  */
591 ilb_status_t
592 ilbd_add_server_to_group(ilb_sg_info_t *sg_info, int ev_port,
593     const struct passwd *ps, ucred_t *ucredp)
594 {
595 	ilb_status_t	rc = ILB_STATUS_OK;
596 	ilbd_sg_t	*tmp_sg;
597 	int		i, j;
598 	int32_t		new_id = BAD_SRVID;
599 	int32_t		af = AF_UNSPEC;
600 	ilbd_srv_t	*nsrv;
601 	ilb_sg_srv_t	*srv;
602 	audit_sg_event_data_t   audit_sg_data;
603 
604 	if (ps != NULL) {
605 		rc = ilbd_check_client_config_auth(ps);
606 		if (rc != ILB_STATUS_OK) {
607 			fill_audit_record(sg_info, &audit_sg_data,
608 			    ILBD_ADD_SERVER_TO_GROUP, rc, ucredp);
609 			return (rc);
610 		}
611 	}
612 
613 	tmp_sg = i_find_sg_byname(sg_info->sg_name);
614 	if (tmp_sg == NULL) {
615 		logdebug("ilbd_add_server_to_group: server"
616 		    " group %s does not exist", sg_info->sg_name);
617 		fill_audit_record(sg_info, &audit_sg_data,
618 		    ILBD_ADD_SERVER_TO_GROUP, ILB_STATUS_ENOENT, ucredp);
619 		return (ILB_STATUS_ENOENT);
620 	}
621 
622 	/*
623 	 * we do the dance with address family below to make sure only
624 	 * IP addresses in the same AF get into an SG; the first one to get
625 	 * in sets the "tone"
626 	 * if this is the first server to join a group, check whether
627 	 * there's no mismatch with any *rules* already attached
628 	 */
629 	if (tmp_sg->isg_srvcount > 0) {
630 		ilbd_srv_t *tsrv = list_head(&tmp_sg->isg_srvlist);
631 
632 		af = GET_AF(&tsrv->isv_addr);
633 	} else {
634 		ilbd_rule_t	*irl = list_head(&tmp_sg->isg_rulelist);
635 
636 		if (irl != NULL)
637 			af = GET_AF(&irl->irl_vip);
638 	}
639 
640 	for (i = 0; i < sg_info->sg_srvcount; i++) {
641 		srv = &sg_info->sg_servers[i];
642 
643 		(void) memset(&audit_sg_data, 0, sizeof (audit_sg_data));
644 		if (IN6_IS_ADDR_V4MAPPED(&srv->sgs_addr)) {
645 			audit_sg_data.ed_ipaddr_type = ADT_IPv4;
646 			cvt_addr(audit_sg_data.ed_server_address, ADT_IPv4,
647 			    srv->sgs_addr);
648 		} else {
649 			audit_sg_data.ed_ipaddr_type = ADT_IPv6;
650 			cvt_addr(audit_sg_data.ed_server_address, ADT_IPv6,
651 			    srv->sgs_addr);
652 		}
653 		audit_sg_data.ed_minport = srv->sgs_minport;
654 		audit_sg_data.ed_maxport = srv->sgs_maxport;
655 		audit_sg_data.ed_sgroup = sg_info->sg_name;
656 
657 		/* only test if we have sth to test against */
658 		if (af != AF_UNSPEC) {
659 			int32_t	sgs_af = GET_AF(&srv->sgs_addr);
660 
661 			if (af != sgs_af) {
662 				logdebug("address family mismatch with previous"
663 				    " hosts in servergroup or with rule");
664 				rc = ILB_STATUS_MISMATCHH;
665 				ilbd_audit_server_event(&audit_sg_data,
666 				    ILBD_ADD_SERVER_TO_GROUP, rc, ucredp);
667 				goto rollback;
668 			}
669 		}
670 
671 		/*
672 		 * PS: NULL means daemon is loading configure from scf.
673 		 * ServerID is already assigned, just update the ID array.
674 		 */
675 		if (ps != NULL) {
676 			new_id = i_ilbd_alloc_srvID(tmp_sg, NULL);
677 			if (new_id == BAD_SRVID) {
678 				logdebug("ilbd_add_server_to_group: server"
679 				    "group %s is full, no more servers"
680 				    " can be added", sg_info->sg_name);
681 				rc = ILB_STATUS_SGFULL;
682 				ilbd_audit_server_event(&audit_sg_data,
683 				    ILBD_ADD_SERVER_TO_GROUP, rc, ucredp);
684 				goto rollback;
685 			}
686 			srv->sgs_id = new_id;
687 		} else {
688 			new_id = i_ilbd_alloc_srvID(tmp_sg, &srv->sgs_id);
689 		}
690 
691 		/*
692 		 * here we implement the requirement that server IDs start
693 		 * with a character that is not legal in hostnames - in our
694 		 * case, a "_" (underscore).
695 		 */
696 		(void) snprintf(srv->sgs_srvID,
697 		    sizeof (srv->sgs_srvID), "%c%s.%d", ILB_SRVID_PREFIX,
698 		    tmp_sg->isg_name, srv->sgs_id);
699 		audit_sg_data.ed_serverid = srv->sgs_srvID;
700 
701 		/*
702 		 * Before we update the kernel rules by adding the server,
703 		 * we need to make checks and fail if any of the
704 		 * following is true:
705 		 *
706 		 * o if the server has single port and the servergroup
707 		 *   is associated to a DSR rule with a port range
708 		 * o if the server has a port range and the servergroup
709 		 *   is associated to a DSR rule with a port range and
710 		 *   the rule's min and max port does not exactly
711 		 *   match that of the server's.
712 		 * o if the the server has a port range and the servergroup
713 		 *   is associated to a NAT/Half-NAT rule with a port range
714 		 *   and the rule's port range size does not match that
715 		 *   of the server's.
716 		 * o if the rule has a fixed hc port, check that this port
717 		 *   is valid in the server's port specification.
718 		 */
719 		rc = i_check_srv2rules(&tmp_sg->isg_rulelist, srv);
720 		if (rc != ILB_STATUS_OK) {
721 			ilbd_audit_server_event(&audit_sg_data,
722 			    ILBD_ADD_SERVER_TO_GROUP, rc, ucredp);
723 			goto rollback;
724 		}
725 
726 		if ((rc = i_add_srv2sg(tmp_sg, srv, &nsrv)) != ILB_STATUS_OK) {
727 			ilbd_audit_server_event(&audit_sg_data,
728 			    ILBD_ADD_SERVER_TO_GROUP, rc, ucredp);
729 			goto rollback;
730 		}
731 
732 		rc = i_add_srv2krules(&tmp_sg->isg_rulelist, &nsrv->isv_srv,
733 		    ev_port);
734 		if (rc != ILB_STATUS_OK) {
735 			ilbd_audit_server_event(&audit_sg_data,
736 			    ILBD_ADD_SERVER_TO_GROUP, rc, ucredp);
737 			/*
738 			 * The failure may be due to the serverid being on
739 			 * hold in kernel for connection draining. But ilbd
740 			 * has no way of knowing that. So we are freeing up
741 			 * the serverid, and may run into the risk of
742 			 * having this failure again, if we choose this
743 			 * serverid  when processing the next add-server
744 			 * command for this servergroup, while connection
745 			 * draining is underway. We assume that the user
746 			 * will read the man page after they encounter
747 			 * this failure, and learn to not add any server
748 			 * to the servergroup until connection draining of
749 			 * all servers in the  servergroup is complete.
750 			 * XXX Need to revisit this when connection draining
751 			 * is reworked
752 			 */
753 			list_remove(&tmp_sg->isg_srvlist, nsrv);
754 			i_ilbd_free_srvID(tmp_sg, nsrv->isv_id);
755 			free(nsrv);
756 			tmp_sg->isg_srvcount--;
757 			goto rollback;
758 		}
759 		if (ps != NULL) {
760 			rc = ilbd_scf_add_srv(tmp_sg, nsrv);
761 			if (rc != ILB_STATUS_OK) {
762 				/*
763 				 * The following should not fail since the
764 				 * server is just added.  Just in case, we
765 				 * pass in -1 as the event port to avoid
766 				 * roll back in i_rem_srv_frm_krules() called
767 				 * by i_delete_srv().
768 				 */
769 				ilbd_audit_server_event(&audit_sg_data,
770 				    ILBD_ADD_SERVER_TO_GROUP, rc, ucredp);
771 				(void) i_delete_srv(tmp_sg, nsrv, -1);
772 				break;
773 			}
774 		}
775 	}
776 
777 	if (rc == ILB_STATUS_OK) {
778 		ilbd_audit_server_event(&audit_sg_data,
779 		    ILBD_ADD_SERVER_TO_GROUP, rc, ucredp);
780 		return (rc);
781 	}
782 
783 rollback:
784 	/*
785 	 * If ilbd is initializing based on the SCF data and something fails,
786 	 * the only choice is to transition the service to maintanence mode...
787 	 */
788 	if (ps == NULL) {
789 		logerr("%s: failure during initialization -"
790 		    " entering maintenance mode", __func__);
791 		(void) smf_maintain_instance(ILB_FMRI, SMF_IMMEDIATE);
792 		return (rc);
793 	}
794 
795 	/*
796 	 * we need to roll back all servers previous to the one
797 	 * that just caused the failure
798 	 */
799 	for (j = i-1; j >= 0; j--) {
800 		srv = &sg_info->sg_servers[j];
801 
802 		/* We should be able to find those servers just added. */
803 		nsrv = i_find_srv(&tmp_sg->isg_srvlist, srv, MODE_SRVID);
804 		assert(nsrv != NULL);
805 		(void) i_delete_srv(tmp_sg, nsrv, -1);
806 	}
807 	return (rc);
808 }
809 
810 static srch_ind_t
811 i_match_srvID(ilb_sg_srv_t *sg_srv, ilbd_srv_t *lsrv)
812 {
813 	if (strncmp(sg_srv->sgs_srvID, lsrv->isv_srvID,
814 	    sizeof (sg_srv->sgs_srvID)) == 0) {
815 		return (stop_found);
816 	}
817 	return (cont_search);
818 }
819 
820 /*
821  * Sanity check on a rule's port specification against all the servers'
822  * specification in its associated server group.
823  *
824  * 1. If the health check's probe port (hcport) is specified.
825  *    - if server port range is specified, check if hcport is inside
826  *      the range
827  *    - if no server port is specified (meaning the port range is the same as
828  *      the rule's port range), check if hcport is inside the rule's range.
829  *
830  * 2. If a server has no port specification, there is no conflict.
831  *
832  * 3. If the rule's load balance mode is DSR, a server port specification must
833  *    be exactly the same as the rule's.
834  *
835  * 4. In other modes (NAT and half-NAT), the server's port range must be
836  *    the same as the rule's, unless it is doing port collapsing (the server's
837  *    port range is only 1).
838  */
839 ilb_status_t
840 ilbd_sg_check_rule_port(ilbd_sg_t *sg, ilb_rule_info_t *rl)
841 {
842 	ilbd_srv_t	*srv;
843 	in_port_t	r_minport, r_maxport;
844 
845 	/* Don't allow adding a rule to a sg with no server, for now... */
846 	if (sg->isg_srvcount == 0)
847 		return (ILB_STATUS_SGEMPTY);
848 
849 	r_minport = ntohs(rl->rl_minport);
850 	r_maxport = ntohs(rl->rl_maxport);
851 
852 	for (srv = list_head(&sg->isg_srvlist); srv != NULL;
853 	    srv = list_next(&sg->isg_srvlist, srv)) {
854 		in_port_t srv_minport, srv_maxport;
855 		int range;
856 
857 		srv_minport = ntohs(srv->isv_minport);
858 		srv_maxport = ntohs(srv->isv_maxport);
859 		range = srv_maxport - srv_minport;
860 
861 		/*
862 		 * If the rule has a specific probe port, check if that port is
863 		 * valid in all the servers' port specification.
864 		 */
865 		if (rl->rl_hcpflag == ILB_HCI_PROBE_FIX) {
866 			in_port_t hcport = ntohs(rl->rl_hcport);
867 
868 			/* No server port specified. */
869 			if (srv_minport == 0) {
870 				if (hcport > r_maxport || hcport < r_minport) {
871 					return (ILB_STATUS_BADSG);
872 				}
873 			} else {
874 				if (hcport > srv_maxport ||
875 				    hcport < srv_minport) {
876 					return (ILB_STATUS_BADSG);
877 				}
878 			}
879 		}
880 
881 		/*
882 		 * There is no server port specification, so there cannot be
883 		 * any conflict.
884 		 */
885 		if (srv_minport == 0)
886 			continue;
887 
888 		if (rl->rl_topo == ILB_TOPO_DSR) {
889 			if (r_minport != srv_minport ||
890 			    r_maxport != srv_maxport) {
891 				return (ILB_STATUS_BADSG);
892 			}
893 		} else {
894 			if ((range != r_maxport - r_minport) && range != 0)
895 				return (ILB_STATUS_BADSG);
896 		}
897 	}
898 
899 	return (ILB_STATUS_OK);
900 }
901 
902 static srch_ind_t
903 i_match_srvIP(ilb_sg_srv_t *sg_srv, ilbd_srv_t *lsrv)
904 {
905 	if (IN6_ARE_ADDR_EQUAL(&sg_srv->sgs_addr, &lsrv->isv_addr))
906 		return (stop_found);
907 	return (cont_search);
908 }
909 
910 static ilbd_srv_t *
911 i_find_srv(list_t *srvlist, ilb_sg_srv_t *sg_srv, int cmpmode)
912 {
913 	ilbd_srv_t	*tmp_srv;
914 	srch_ind_t	srch_res = cont_search;
915 
916 	for (tmp_srv = list_head(srvlist); tmp_srv != NULL;
917 	    tmp_srv = list_next(srvlist, tmp_srv)) {
918 		switch (cmpmode) {
919 		case MODE_ADDR:
920 			srch_res = i_match_srvIP(sg_srv, tmp_srv);
921 			break;
922 		case MODE_SRVID:
923 			srch_res = i_match_srvID(sg_srv, tmp_srv);
924 			break;
925 		}
926 		if (srch_res == stop_found)
927 			break;
928 	}
929 
930 	if (srch_res == stop_found)
931 		return (tmp_srv);
932 	return (NULL);
933 }
934 
935 static ilb_status_t
936 i_delete_srv(ilbd_sg_t *sg, ilbd_srv_t *srv, int ev_port)
937 {
938 	ilb_status_t	rc;
939 
940 	rc = i_rem_srv_frm_krules(&sg->isg_rulelist, &srv->isv_srv, ev_port);
941 	if (rc != ILB_STATUS_OK)
942 		return (rc);
943 	list_remove(&sg->isg_srvlist, srv);
944 	i_ilbd_free_srvID(sg, srv->isv_id);
945 	free(srv);
946 	sg->isg_srvcount--;
947 	return (ILB_STATUS_OK);
948 }
949 
950 /*
951  * some people argue that returning anything here is
952  * useless - what *do* you do if you can't remove/destroy
953  * something anyway?
954  */
955 ilb_status_t
956 ilbd_rem_server_from_group(ilb_sg_info_t *sg_info, int ev_port,
957     const struct passwd *ps, ucred_t *ucredp)
958 {
959 	ilb_status_t	rc = ILB_STATUS_OK;
960 	ilbd_sg_t	*tmp_sg;
961 	ilbd_srv_t	*srv, tmp_srv;
962 	ilb_sg_srv_t    *tsrv;
963 	audit_sg_event_data_t   audit_sg_data;
964 
965 	rc = ilbd_check_client_config_auth(ps);
966 	if (rc != ILB_STATUS_OK) {
967 		fill_audit_record(sg_info, &audit_sg_data,
968 		    ILBD_REM_SERVER_FROM_GROUP, rc, ucredp);
969 		return (rc);
970 	}
971 
972 	tmp_sg = i_find_sg_byname(sg_info->sg_name);
973 	if (tmp_sg == NULL) {
974 		logdebug("%s: server group %s\n does not exist", __func__,
975 		    sg_info->sg_name);
976 		fill_audit_record(sg_info, &audit_sg_data,
977 		    ILBD_REM_SERVER_FROM_GROUP, ILB_STATUS_SGUNAVAIL, ucredp);
978 		return (ILB_STATUS_SGUNAVAIL);
979 	}
980 	tsrv = &sg_info->sg_servers[0];
981 	audit_sg_data.ed_serverid = tsrv->sgs_srvID;
982 	audit_sg_data.ed_sgroup = sg_info->sg_name;
983 
984 	assert(sg_info->sg_srvcount == 1);
985 	srv = i_find_srv(&tmp_sg->isg_srvlist, &sg_info->sg_servers[0],
986 	    MODE_SRVID);
987 	if (srv == NULL) {
988 		logdebug("%s: cannot find server in server group %s", __func__,
989 		    sg_info->sg_name);
990 		ilbd_audit_server_event(&audit_sg_data,
991 		    ILBD_REM_SERVER_FROM_GROUP, ILB_STATUS_SRVUNAVAIL, ucredp);
992 		return (ILB_STATUS_SRVUNAVAIL);
993 	}
994 	tsrv = &srv->isv_srv;
995 	if (IN6_IS_ADDR_V4MAPPED(&tsrv->sgs_addr)) {
996 		audit_sg_data.ed_ipaddr_type = ADT_IPv4;
997 		cvt_addr(audit_sg_data.ed_server_address, ADT_IPv4,
998 		    tsrv->sgs_addr);
999 	} else {
1000 		audit_sg_data.ed_ipaddr_type = ADT_IPv6;
1001 		cvt_addr(audit_sg_data.ed_server_address, ADT_IPv6,
1002 		    tsrv->sgs_addr);
1003 	}
1004 	/*
1005 	 * i_delete_srv frees srv, therefore we need to save
1006 	 * this information for ilbd_scf_del_srv
1007 	 */
1008 	(void) memcpy(&tmp_srv, srv, sizeof (tmp_srv));
1009 
1010 	rc = i_delete_srv(tmp_sg, srv, ev_port);
1011 	if (rc != ILB_STATUS_OK) {
1012 		ilbd_audit_server_event(&audit_sg_data,
1013 		    ILBD_REM_SERVER_FROM_GROUP, rc, ucredp);
1014 		return (rc);
1015 	}
1016 
1017 	if (ps != NULL) {
1018 		if ((rc = ilbd_scf_del_srv(tmp_sg, &tmp_srv)) !=
1019 		    ILB_STATUS_OK) {
1020 			ilbd_audit_server_event(&audit_sg_data,
1021 			    ILBD_REM_SERVER_FROM_GROUP, rc, ucredp);
1022 			logerr("%s: SCF update failed - entering maintenance"
1023 			    " mode", __func__);
1024 			(void) smf_maintain_instance(ILB_FMRI, SMF_IMMEDIATE);
1025 		}
1026 	}
1027 	ilbd_audit_server_event(&audit_sg_data,
1028 	    ILBD_REM_SERVER_FROM_GROUP, rc, ucredp);
1029 	return (rc);
1030 }
1031 
1032 ilb_status_t
1033 ilbd_retrieve_names(ilbd_cmd_t cmd, uint32_t *rbuf, size_t *rbufsz)
1034 {
1035 	ilb_status_t	rc = ILB_STATUS_OK;
1036 	ilbd_namelist_t	*nlist;
1037 	size_t		tmp_rbufsz;
1038 
1039 	tmp_rbufsz = *rbufsz;
1040 	/* Set up the reply buffer.  rbufsz will be set to the new size. */
1041 	ilbd_reply_ok(rbuf, rbufsz);
1042 
1043 	/* Calculate how much space is left for holding name info. */
1044 	*rbufsz += sizeof (ilbd_namelist_t);
1045 	tmp_rbufsz -= *rbufsz;
1046 
1047 	nlist = (ilbd_namelist_t *)&((ilb_comm_t *)rbuf)->ic_data;
1048 	nlist->ilbl_count = 0;
1049 
1050 	switch (cmd) {
1051 	case ILBD_RETRIEVE_SG_NAMES: {
1052 		ilbd_sg_t	*sg;
1053 
1054 		for (sg = list_head(&ilbd_sg_hlist);
1055 		    sg != NULL && tmp_rbufsz >= sizeof (ilbd_name_t);
1056 		    sg = list_next(&ilbd_sg_hlist, sg),
1057 		    tmp_rbufsz -= sizeof (ilbd_name_t)) {
1058 			(void) strlcpy(nlist->ilbl_name[nlist->ilbl_count++],
1059 			    sg->isg_name, sizeof (ilbd_name_t));
1060 		}
1061 		break;
1062 	}
1063 	case ILBD_RETRIEVE_RULE_NAMES: {
1064 		ilbd_rule_t	*irl;
1065 		extern list_t	ilbd_rule_hlist;
1066 
1067 		for (irl = list_head(&ilbd_rule_hlist);
1068 		    irl != NULL && tmp_rbufsz >= sizeof (ilbd_name_t);
1069 		    irl = list_next(&ilbd_rule_hlist, irl),
1070 		    tmp_rbufsz -= sizeof (ilbd_name_t)) {
1071 			(void) strlcpy(nlist->ilbl_name[nlist->ilbl_count++],
1072 			    irl->irl_name, sizeof (ilbd_name_t));
1073 		}
1074 		break;
1075 	}
1076 	case ILBD_RETRIEVE_HC_NAMES: {
1077 		extern list_t	ilbd_hc_list;
1078 		ilbd_hc_t	*hc;
1079 
1080 		for (hc = list_head(&ilbd_hc_list);
1081 		    hc != NULL && tmp_rbufsz >= sizeof (ilbd_name_t);
1082 		    hc = list_next(&ilbd_hc_list, hc)) {
1083 			(void) strlcpy(nlist->ilbl_name[nlist->ilbl_count++],
1084 			    hc->ihc_name, sizeof (ilbd_name_t));
1085 		}
1086 		break;
1087 	}
1088 	default:
1089 		logdebug("ilbd_retrieve_names: unknown command");
1090 		return (ILB_STATUS_INVAL_CMD);
1091 	}
1092 
1093 	*rbufsz += nlist->ilbl_count * sizeof (ilbd_name_t);
1094 	return (rc);
1095 }
1096 
1097 ilb_status_t
1098 ilbd_retrieve_sg_hosts(const char *sg_name, uint32_t *rbuf, size_t *rbufsz)
1099 {
1100 	ilbd_sg_t	*dsg;
1101 	ilbd_srv_t	*dsrv;
1102 	list_t		*srvlist;
1103 	ilb_sg_info_t	*sg_info;
1104 	size_t		tmp_rbufsz;
1105 
1106 	dsg = i_find_sg_byname(sg_name);
1107 	if (dsg == NULL) {
1108 		logdebug("ilbd_retrieve_sg_hosts: server group"
1109 		    " %s not found", sg_name);
1110 		return (ILB_STATUS_SGUNAVAIL);
1111 	}
1112 
1113 	srvlist = &dsg->isg_srvlist;
1114 	dsrv = list_head(srvlist);
1115 
1116 	tmp_rbufsz = *rbufsz;
1117 	ilbd_reply_ok(rbuf, rbufsz);
1118 
1119 	/* Calculate the size to hold all the hosts info. */
1120 	*rbufsz += sizeof (ilb_sg_info_t);
1121 	tmp_rbufsz -= *rbufsz;
1122 
1123 	sg_info = (ilb_sg_info_t *)&((ilb_comm_t *)rbuf)->ic_data;
1124 	(void) strlcpy(sg_info->sg_name, sg_name, sizeof (sg_info->sg_name));
1125 	sg_info->sg_srvcount = 0;
1126 
1127 	while (dsrv != NULL && tmp_rbufsz >= sizeof (ilb_sg_srv_t)) {
1128 		sg_info->sg_servers[sg_info->sg_srvcount++] = dsrv->isv_srv;
1129 		dsrv = list_next(srvlist, dsrv);
1130 		tmp_rbufsz -= sizeof (ilb_sg_srv_t);
1131 	}
1132 	*rbufsz += sg_info->sg_srvcount * sizeof (ilb_sg_srv_t);
1133 	return (ILB_STATUS_OK);
1134 }
1135 
1136 /*
1137  * this mapping function works on the assumption that HC only is
1138  * active when a server is enabled.
1139  */
1140 static ilb_cmd_t
1141 i_srvcmd_d2k(ilbd_srv_status_ind_t dcmd)
1142 {
1143 	ilb_cmd_t	cmd;
1144 
1145 	switch (dcmd) {
1146 	case stat_enable_server:
1147 	case stat_declare_srv_alive:
1148 		cmd = ILB_ENABLE_SERVERS;
1149 		break;
1150 	case stat_disable_server:
1151 	case stat_declare_srv_dead:
1152 		cmd = ILB_DISABLE_SERVERS;
1153 		break;
1154 	}
1155 
1156 	return (cmd);
1157 }
1158 
1159 ilb_status_t
1160 ilbd_k_Xable_server(const struct in6_addr *addr, const char *rlname,
1161     ilbd_srv_status_ind_t cmd)
1162 {
1163 	ilb_status_t		rc;
1164 	ilb_servers_cmd_t	kcmd;
1165 	int			e;
1166 
1167 	kcmd.cmd = i_srvcmd_d2k(cmd);
1168 	(void) strlcpy(kcmd.name, rlname, sizeof (kcmd.name));
1169 	kcmd.num_servers = 1;
1170 
1171 	kcmd.servers[0].addr = *addr;
1172 	kcmd.servers[0].err = 0;
1173 
1174 	rc = do_ioctl(&kcmd, 0);
1175 	if (rc != ILB_STATUS_OK)
1176 		return (rc);
1177 
1178 	if ((e = kcmd.servers[0].err) != 0) {
1179 		logdebug("ilbd_k_Xable_server: error %s occurred",
1180 		    strerror(e));
1181 		return (ilb_map_errno2ilbstat(e));
1182 	}
1183 
1184 	return (rc);
1185 }
1186 
1187 #define	IS_SRV_ENABLED(s)	ILB_IS_SRV_ENABLED((s)->sgs_flags)
1188 #define	IS_SRV_DISABLED(s)	(!(IS_SRV_ENABLED(s)))
1189 
1190 #define	SET_SRV_ENABLED(s)	ILB_SET_ENABLED((s)->sgs_flags)
1191 #define	SET_SRV_DISABLED(s)	ILB_SET_DISABLED((s)->sgs_flags)
1192 
1193 static ilb_status_t
1194 ilbd_Xable_server(ilb_sg_info_t *sg, const struct passwd *ps,
1195     ilbd_srv_status_ind_t cmd, ucred_t *ucredp)
1196 {
1197 	ilb_status_t	rc = ILB_STATUS_OK;
1198 	ilbd_sg_t	*isg;
1199 	ilbd_srv_t	*tmp_srv;
1200 	ilb_sg_srv_t 	*srv;
1201 	ilbd_rule_t	*irl;
1202 	char		*dot;
1203 	int		scf_name_len = ILBD_MAX_NAME_LEN;
1204 	int		scf_val_len = ILBD_MAX_VALUE_LEN;
1205 	char		*prop_name = NULL;
1206 	ilb_ip_addr_t	ipaddr;
1207 	void		*addrptr;
1208 	char		ipstr[INET6_ADDRSTRLEN], *valstr = NULL;
1209 	int		ipver, vallen;
1210 	char		sgname[ILB_NAMESZ];
1211 	uint32_t	nflags;
1212 	ilbd_srv_status_ind_t u_cmd;
1213 	audit_sg_event_data_t   audit_sg_data;
1214 
1215 	(void) memset(&audit_sg_data, 0, sizeof (audit_sg_data));
1216 
1217 	/* we currently only implement a "list" of one */
1218 	assert(sg->sg_srvcount == 1);
1219 
1220 	srv = &sg->sg_servers[0];
1221 	audit_sg_data.ed_serverid = srv->sgs_srvID;
1222 
1223 	rc = ilbd_check_client_enable_auth(ps);
1224 	if (rc != ILB_STATUS_OK) {
1225 		ilbd_audit_server_event(&audit_sg_data,
1226 		    ILBD_ENABLE_SERVER, rc, ucredp);
1227 		return (rc);
1228 	}
1229 
1230 	if (srv->sgs_srvID[0] != ILB_SRVID_PREFIX) {
1231 		switch (cmd) {
1232 		case stat_disable_server:
1233 			ilbd_audit_server_event(&audit_sg_data,
1234 			    ILBD_DISABLE_SERVER,
1235 			    ILB_STATUS_EINVAL, ucredp);
1236 			break;
1237 		case stat_enable_server:
1238 			ilbd_audit_server_event(&audit_sg_data,
1239 			    ILBD_ENABLE_SERVER,
1240 			    ILB_STATUS_EINVAL, ucredp);
1241 			break;
1242 		}
1243 		return (ILB_STATUS_EINVAL);
1244 	}
1245 
1246 	/*
1247 	 * the following asserts that serverIDs are constructed
1248 	 * along the pattern "_"<SG name>"."<number>
1249 	 * so we look for the final "." to recreate the SG name.
1250 	 */
1251 	(void) strlcpy(sgname, srv->sgs_srvID + 1, sizeof (sgname));
1252 	dot = strrchr(sgname, (int)'.');
1253 	if (dot == NULL) {
1254 		switch (cmd) {
1255 		case stat_disable_server:
1256 			ilbd_audit_server_event(&audit_sg_data,
1257 			    ILBD_DISABLE_SERVER,
1258 			    ILB_STATUS_EINVAL, ucredp);
1259 			break;
1260 		case stat_enable_server:
1261 			ilbd_audit_server_event(&audit_sg_data,
1262 			    ILBD_ENABLE_SERVER,
1263 			    ILB_STATUS_EINVAL, ucredp);
1264 			break;
1265 		}
1266 		return (ILB_STATUS_EINVAL);
1267 	}
1268 
1269 	/* make the non-sg_name part "invisible" */
1270 	*dot = '\0';
1271 	isg = i_find_sg_byname(sgname);
1272 	if (isg == NULL) {
1273 		switch (cmd) {
1274 		case stat_disable_server:
1275 			ilbd_audit_server_event(&audit_sg_data,
1276 			    ILBD_DISABLE_SERVER,
1277 			    ILB_STATUS_ENOENT, ucredp);
1278 			break;
1279 		case stat_enable_server:
1280 			ilbd_audit_server_event(&audit_sg_data,
1281 			    ILBD_ENABLE_SERVER,
1282 			    ILB_STATUS_ENOENT, ucredp);
1283 			break;
1284 		}
1285 		return (ILB_STATUS_ENOENT);
1286 	}
1287 
1288 	tmp_srv = i_find_srv(&isg->isg_srvlist, srv, MODE_SRVID);
1289 	if (tmp_srv == NULL) {
1290 		switch (cmd) {
1291 		case stat_disable_server:
1292 			ilbd_audit_server_event(&audit_sg_data,
1293 			    ILBD_DISABLE_SERVER,
1294 			    ILB_STATUS_ENOENT, ucredp);
1295 			break;
1296 		case stat_enable_server:
1297 			ilbd_audit_server_event(&audit_sg_data,
1298 			    ILBD_ENABLE_SERVER,
1299 			    ILB_STATUS_ENOENT, ucredp);
1300 			break;
1301 		}
1302 		return (ILB_STATUS_ENOENT);
1303 	}
1304 
1305 	/*
1306 	 * if server's servergroup is not associated with
1307 	 * a rule, do not enable it.
1308 	 */
1309 	irl = list_head(&isg->isg_rulelist);
1310 	if (irl == NULL) {
1311 		switch (cmd) {
1312 		case stat_disable_server:
1313 			ilbd_audit_server_event(&audit_sg_data,
1314 			    ILBD_DISABLE_SERVER,
1315 			    ILB_STATUS_INVAL_ENBSRVR, ucredp);
1316 			break;
1317 		case stat_enable_server:
1318 			ilbd_audit_server_event(&audit_sg_data,
1319 			    ILBD_ENABLE_SERVER,
1320 			    ILB_STATUS_INVAL_ENBSRVR, ucredp);
1321 			break;
1322 		}
1323 		return (ILB_STATUS_INVAL_ENBSRVR);
1324 	}
1325 	/* Fill in the server IP address for audit record */
1326 	if (IN6_IS_ADDR_V4MAPPED(&tmp_srv->isv_addr)) {
1327 		audit_sg_data.ed_ipaddr_type = ADT_IPv4;
1328 		cvt_addr(audit_sg_data.ed_server_address, ADT_IPv4,
1329 		    tmp_srv->isv_addr);
1330 	} else {
1331 		audit_sg_data.ed_ipaddr_type = ADT_IPv6;
1332 		cvt_addr(audit_sg_data.ed_server_address, ADT_IPv6,
1333 		    tmp_srv->isv_addr);
1334 	}
1335 
1336 	/*
1337 	 * We have found the server in memory, perform the following
1338 	 * tasks.
1339 	 *
1340 	 * 1. For every rule associated with this SG,
1341 	 *    - tell the kernel
1342 	 *    - tell the hc
1343 	 * 2. Update our internal state and persistent configuration
1344 	 *    if the new state is not the same as the old one.
1345 	 */
1346 	/* 1. */
1347 	for (; irl != NULL; irl = list_next(&isg->isg_rulelist, irl)) {
1348 		rc = ilbd_k_Xable_server(&tmp_srv->isv_addr,
1349 		    irl->irl_name, cmd);
1350 		if (rc != ILB_STATUS_OK) {
1351 			switch (cmd) {
1352 			case stat_disable_server:
1353 				ilbd_audit_server_event(&audit_sg_data,
1354 				    ILBD_DISABLE_SERVER, rc, ucredp);
1355 				break;
1356 			case stat_enable_server:
1357 			ilbd_audit_server_event(&audit_sg_data,
1358 			    ILBD_ENABLE_SERVER, rc, ucredp);
1359 			break;
1360 			}
1361 			goto rollback_rules;
1362 		}
1363 		if (!RULE_HAS_HC(irl))
1364 			continue;
1365 
1366 		if (cmd == stat_disable_server) {
1367 			rc = ilbd_hc_disable_server(irl,
1368 			    &tmp_srv->isv_srv);
1369 		} else {
1370 			assert(cmd == stat_enable_server);
1371 			rc = ilbd_hc_enable_server(irl,
1372 			    &tmp_srv->isv_srv);
1373 		}
1374 		if (rc != ILB_STATUS_OK) {
1375 			logdebug("ilbd_Xable_server: cannot toggle srv "
1376 			    "timer, rc =%d, srv =%s%d\n", rc,
1377 			    tmp_srv->isv_srvID,
1378 			    tmp_srv->isv_id);
1379 		}
1380 	}
1381 
1382 	/* 2. */
1383 	if ((cmd == stat_disable_server &&
1384 	    IS_SRV_DISABLED(&tmp_srv->isv_srv)) ||
1385 	    (cmd == stat_enable_server &&
1386 	    IS_SRV_ENABLED(&tmp_srv->isv_srv))) {
1387 		switch (cmd) {
1388 		case stat_disable_server:
1389 			ilbd_audit_server_event(&audit_sg_data,
1390 			    ILBD_DISABLE_SERVER, ILB_STATUS_OK, ucredp);
1391 			break;
1392 		case stat_enable_server:
1393 			ilbd_audit_server_event(&audit_sg_data,
1394 			    ILBD_ENABLE_SERVER, ILB_STATUS_OK, ucredp);
1395 			break;
1396 		}
1397 		return (ILB_STATUS_OK);
1398 	}
1399 
1400 	nflags = tmp_srv->isv_flags;
1401 	if (cmd == stat_enable_server)
1402 		ILB_SET_ENABLED(nflags);
1403 	else
1404 		ILB_SET_DISABLED(nflags);
1405 
1406 	IP_COPY_IMPL_2_CLI(&tmp_srv->isv_addr, &ipaddr);
1407 	ipver = GET_AF(&tmp_srv->isv_addr);
1408 	vallen = (ipver == AF_INET) ? INET_ADDRSTRLEN :
1409 	    INET6_ADDRSTRLEN;
1410 	addrptr = (ipver == AF_INET) ? (void *)&ipaddr.ia_v4 :
1411 	    (void *)&ipaddr.ia_v6;
1412 	if (inet_ntop(ipver, addrptr, ipstr, vallen) == NULL) {
1413 		logerr("ilbd_Xable_server: failed transfer ip addr to"
1414 		    " str");
1415 		if (errno == ENOSPC)
1416 			rc = ILB_STATUS_ENOMEM;
1417 		else
1418 			rc = ILB_STATUS_GENERIC;
1419 		switch (cmd) {
1420 		case stat_disable_server:
1421 			ilbd_audit_server_event(&audit_sg_data,
1422 			    ILBD_DISABLE_SERVER, rc, ucredp);
1423 			break;
1424 		case stat_enable_server:
1425 			ilbd_audit_server_event(&audit_sg_data,
1426 			    ILBD_ENABLE_SERVER, rc, ucredp);
1427 			break;
1428 		}
1429 		goto rollback_rules;
1430 	}
1431 
1432 	if ((prop_name = malloc(scf_name_len)) == NULL)
1433 		return (ILB_STATUS_ENOMEM);
1434 	if ((valstr = malloc(scf_val_len)) == NULL) {
1435 		free(prop_name);
1436 		return (ILB_STATUS_ENOMEM);
1437 	}
1438 
1439 	(void) snprintf(valstr, scf_val_len, "%s;%d;%d-%d;%d",
1440 	    ipstr, ipver,
1441 	    ntohs(tmp_srv->isv_minport),
1442 	    ntohs(tmp_srv->isv_maxport), nflags);
1443 	(void) snprintf(prop_name, scf_name_len, "server%d",
1444 	    tmp_srv->isv_id);
1445 
1446 	switch (cmd) {
1447 	case stat_disable_server:
1448 		rc = i_ilbd_save_sg(isg, ILBD_SCF_ENABLE_DISABLE,
1449 		    prop_name, valstr);
1450 		if (rc == ILB_STATUS_OK)
1451 			SET_SRV_DISABLED(&tmp_srv->isv_srv);
1452 		break;
1453 	case stat_enable_server:
1454 		rc = i_ilbd_save_sg(isg, ILBD_SCF_ENABLE_DISABLE,
1455 		    prop_name, valstr);
1456 		if (rc == ILB_STATUS_OK)
1457 			SET_SRV_ENABLED(&tmp_srv->isv_srv);
1458 		break;
1459 	}
1460 	free(prop_name);
1461 	free(valstr);
1462 	if (rc == ILB_STATUS_OK) {
1463 		switch (cmd) {
1464 		case stat_disable_server:
1465 			ilbd_audit_server_event(&audit_sg_data,
1466 			    ILBD_DISABLE_SERVER, ILB_STATUS_OK, ucredp);
1467 			break;
1468 		case stat_enable_server:
1469 			ilbd_audit_server_event(&audit_sg_data,
1470 			    ILBD_ENABLE_SERVER, ILB_STATUS_OK, ucredp);
1471 			break;
1472 		}
1473 		return (ILB_STATUS_OK);
1474 	}
1475 
1476 rollback_rules:
1477 	if (cmd == stat_disable_server)
1478 		u_cmd = stat_enable_server;
1479 	else
1480 		u_cmd = stat_disable_server;
1481 
1482 	if (irl == NULL)
1483 		irl = list_tail(&isg->isg_rulelist);
1484 	else
1485 		irl = list_prev(&isg->isg_rulelist, irl);
1486 
1487 	for (; irl != NULL; irl = list_prev(&isg->isg_rulelist, irl)) {
1488 		(void) ilbd_k_Xable_server(&tmp_srv->isv_addr,
1489 		    irl->irl_name, u_cmd);
1490 		if (!RULE_HAS_HC(irl))
1491 			continue;
1492 
1493 		if (u_cmd == stat_disable_server)
1494 			(void) ilbd_hc_disable_server(irl, &tmp_srv->isv_srv);
1495 		else
1496 			(void) ilbd_hc_enable_server(irl, &tmp_srv->isv_srv);
1497 	}
1498 
1499 	return (rc);
1500 }
1501 
1502 ilb_status_t
1503 ilbd_disable_server(ilb_sg_info_t *sg, const struct passwd *ps,
1504     ucred_t *ucredp)
1505 {
1506 	return (ilbd_Xable_server(sg, ps, stat_disable_server, ucredp));
1507 }
1508 
1509 ilb_status_t
1510 ilbd_enable_server(ilb_sg_info_t *sg, const struct passwd *ps,
1511     ucred_t *ucredp)
1512 {
1513 	return (ilbd_Xable_server(sg, ps, stat_enable_server, ucredp));
1514 }
1515 
1516 /*
1517  * fill in the srvID for the given IP address in the 0th server
1518  */
1519 ilb_status_t
1520 ilbd_address_to_srvID(ilb_sg_info_t *sg, uint32_t *rbuf, size_t *rbufsz)
1521 {
1522 	ilbd_srv_t 	*tmp_srv;
1523 	ilb_sg_srv_t 	*tsrv;
1524 	ilbd_sg_t	*tmp_sg;
1525 
1526 	ilbd_reply_ok(rbuf, rbufsz);
1527 	tsrv = (ilb_sg_srv_t *)&((ilb_comm_t *)rbuf)->ic_data;
1528 	*rbufsz += sizeof (ilb_sg_srv_t);
1529 
1530 	tmp_sg = i_find_sg_byname(sg->sg_name);
1531 	if (tmp_sg == NULL)
1532 		return (ILB_STATUS_SGUNAVAIL);
1533 	tsrv->sgs_addr = sg->sg_servers[0].sgs_addr;
1534 
1535 	tmp_srv = i_find_srv(&tmp_sg->isg_srvlist, tsrv, MODE_ADDR);
1536 	if (tmp_srv == NULL)
1537 		return (ILB_STATUS_ENOENT);
1538 
1539 	(void) strlcpy(tsrv->sgs_srvID, tmp_srv->isv_srvID,
1540 	    sizeof (tsrv->sgs_srvID));
1541 
1542 	return (ILB_STATUS_OK);
1543 }
1544 
1545 /*
1546  * fill in the address for the given serverID in the 0th server
1547  */
1548 ilb_status_t
1549 ilbd_srvID_to_address(ilb_sg_info_t *sg, uint32_t *rbuf, size_t *rbufsz)
1550 {
1551 	ilbd_srv_t 	*tmp_srv;
1552 	ilb_sg_srv_t 	*tsrv;
1553 	ilbd_sg_t	*tmp_sg;
1554 
1555 	ilbd_reply_ok(rbuf, rbufsz);
1556 	tsrv = (ilb_sg_srv_t *)&((ilb_comm_t *)rbuf)->ic_data;
1557 
1558 	tmp_sg = i_find_sg_byname(sg->sg_name);
1559 	if (tmp_sg == NULL)
1560 		return (ILB_STATUS_SGUNAVAIL);
1561 	(void) strlcpy(tsrv->sgs_srvID, sg->sg_servers[0].sgs_srvID,
1562 	    sizeof (tsrv->sgs_srvID));
1563 
1564 	tmp_srv = i_find_srv(&tmp_sg->isg_srvlist, tsrv, MODE_SRVID);
1565 	if (tmp_srv == NULL)
1566 		return (ILB_STATUS_ENOENT);
1567 
1568 	tsrv->sgs_addr = tmp_srv->isv_addr;
1569 	*rbufsz += sizeof (ilb_sg_srv_t);
1570 
1571 	return (ILB_STATUS_OK);
1572 }
1573 
1574 /*
1575  * Map ilb_status errors to similar errno values from errno.h or
1576  * adt_event.h to be used for audit record
1577  */
1578 int
1579 ilberror2auditerror(ilb_status_t rc)
1580 {
1581 	int audit_error;
1582 
1583 	switch (rc) {
1584 	case ILB_STATUS_CFGAUTH:
1585 		audit_error = ADT_FAIL_VALUE_AUTH;
1586 		break;
1587 	case ILB_STATUS_ENOMEM:
1588 		audit_error = ENOMEM;
1589 		break;
1590 	case ILB_STATUS_ENOENT:
1591 	case ILB_STATUS_ENOHCINFO:
1592 	case ILB_STATUS_INVAL_HCTESTTYPE:
1593 	case ILB_STATUS_INVAL_CMD:
1594 	case ILB_STATUS_DUP_RULE:
1595 	case ILB_STATUS_ENORULE:
1596 	case ILB_STATUS_SGUNAVAIL:
1597 		audit_error = ENOENT;
1598 		break;
1599 	case ILB_STATUS_EINVAL:
1600 	case ILB_STATUS_MISMATCHSG:
1601 	case ILB_STATUS_MISMATCHH:
1602 	case ILB_STATUS_BADSG:
1603 	case ILB_STATUS_INVAL_SRVR:
1604 	case ILB_STATUS_INVAL_ENBSRVR:
1605 	case ILB_STATUS_BADPORT:
1606 		audit_error = EINVAL;
1607 		break;
1608 	case ILB_STATUS_EEXIST:
1609 	case ILB_STATUS_SGEXISTS:
1610 		audit_error = EEXIST;
1611 		break;
1612 	case ILB_STATUS_EWOULDBLOCK:
1613 		audit_error = EWOULDBLOCK;
1614 		break;
1615 	case ILB_STATUS_INPROGRESS:
1616 		audit_error = EINPROGRESS;
1617 		break;
1618 	case ILB_STATUS_INTERNAL:
1619 	case ILB_STATUS_CALLBACK:
1620 	case ILB_STATUS_PERMIT:
1621 	case ILB_STATUS_RULE_NO_HC:
1622 		audit_error = ADT_FAIL_VALUE_PROGRAM;
1623 		break;
1624 	case ILB_STATUS_SOCKET:
1625 		audit_error = ENOTSOCK;
1626 		break;
1627 	case ILB_STATUS_READ:
1628 	case ILB_STATUS_WRITE:
1629 		audit_error = ENOTCONN;
1630 		break;
1631 	case ILB_STATUS_SGINUSE:
1632 		audit_error = EADDRINUSE;
1633 		break;
1634 	case ILB_STATUS_SEND:
1635 		audit_error = ECOMM;
1636 		break;
1637 	case ILB_STATUS_SGFULL:
1638 		audit_error = EOVERFLOW;
1639 		break;
1640 	case ILB_STATUS_NAMETOOLONG:
1641 		audit_error = ENAMETOOLONG;
1642 		break;
1643 	case ILB_STATUS_SRVUNAVAIL:
1644 		audit_error = EHOSTUNREACH;
1645 		break;
1646 	default:
1647 		audit_error = ADT_FAIL_VALUE_UNKNOWN;
1648 		break;
1649 	}
1650 	return (audit_error);
1651 }
1652