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
42typedef enum {
43	not_searched,
44	stop_found,
45	cont_search,
46	fail_search
47} srch_ind_t;
48
49static list_t	ilbd_sg_hlist;
50
51static ilb_status_t i_delete_srv(ilbd_sg_t *, ilbd_srv_t *, int);
52static 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
58static ilbd_srv_t *i_find_srv(list_t *, ilb_sg_srv_t *, int);
59
60void
61i_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 */
70static ilbd_sg_t *
71i_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);
87out:
88	return (d_sg);
89}
90
91static ilb_status_t
92i_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
111ilb_status_t
112i_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
119static void
120i_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
135ilbd_sg_t *
136i_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 */
153static void
154ilbd_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
287ilb_status_t
288ilbd_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 */
347ilb_status_t
348ilbd_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 */
411static srch_ind_t
412i_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
445static ilb_status_t
446i_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 */
509static int32_t
510i_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 */
540static void
541i_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 */
552static void
553fill_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 */
590ilb_status_t
591ilbd_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
782rollback:
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
809static srch_ind_t
810i_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 */
838ilb_status_t
839ilbd_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
901static srch_ind_t
902i_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
909static ilbd_srv_t *
910i_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
934static ilb_status_t
935i_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 */
954ilb_status_t
955ilbd_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
1031ilb_status_t
1032ilbd_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
1096ilb_status_t
1097ilbd_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 */
1139static ilb_cmd_t
1140i_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
1158ilb_status_t
1159ilbd_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
1192static ilb_status_t
1193ilbd_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
1475rollback_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
1501ilb_status_t
1502ilbd_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
1508ilb_status_t
1509ilbd_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 */
1518ilb_status_t
1519ilbd_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 */
1547ilb_status_t
1548ilbd_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 */
1577int
1578ilberror2auditerror(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