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