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