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