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 <stdio.h>
28#include <strings.h>
29#include <sys/types.h>
30#include <sys/socket.h>
31#include <netinet/in.h>
32#include <arpa/inet.h>
33#include <sys/list.h>
34#include <libilb.h>
35#include <assert.h>
36#include <libscf.h>
37#include "libilb_impl.h"
38#include "ilbd.h"
39
40#define	ILBD_PG_NAME_RULE "rule_"
41#define	ILBD_PG_NAME_SG "sg_"
42#define	ILBD_PG_NAME_HC "hc_"
43#define	ILBD_SVC_FMRI "svc:/network/loadbalancer/ilb"
44#define	ILBD_INST_NAME "default"
45
46typedef enum {
47	ILBD_RULE_STATUS,
48	ILBD_RULE_VIP,
49	ILBD_RULE_PROTO,
50	ILBD_RULE_PORT,
51	ILBD_RULE_ALGO,
52	ILBD_RULE_TOPO,
53	ILBD_RULE_NAT_STR,
54	ILBD_RULE_NAT_END,
55	ILBD_RULE_STI_MASK,
56	ILBD_RULE_SGNAME,
57	ILBD_RULE_HCNAME,
58	ILBD_RULE_HCPORT,
59	ILBD_RULE_HCPFLAG,
60	ILBD_RULE_DRAINTIME,
61	ILBD_RULE_NAT_TO,
62	ILBD_RULE_PERS_TO,
63
64	ILBD_SG_SERVER,
65
66	ILBD_HC_TEST,
67	ILBD_HC_TIMEOUT,
68	ILBD_HC_INTERVAL,
69	ILBD_HC_DEF_PING,
70	ILBD_HC_COUNT,
71
72	ILBD_VAR_INVALID
73} ilbd_var_type_t;
74
75typedef struct prop_tbl_entry {
76	ilbd_var_type_t val_type;
77	const char *scf_propname;
78	scf_type_t scf_proptype;
79} prop_tbl_entry_t;
80
81/*
82 * this table contains a map of all SCF properties, including rules,
83 * servergroups and health checks. The place to add new property needs to be
84 * watched carefully. When new properties are added, corresponding *VAR_NUM
85 * needs to be adjusted to reflect the correct index of the table
86 */
87prop_tbl_entry_t prop_tbl[] = {
88	/* entried for rule */
89	{ILBD_RULE_STATUS, "status", SCF_TYPE_BOOLEAN},
90	/* SCF_TYPE_NET_ADDR_V4 or SCF_TYPE_NET_ADDR_V6 */
91	{ILBD_RULE_VIP, "vip", SCF_TYPE_INVALID},
92	{ILBD_RULE_PROTO, "protocol", SCF_TYPE_ASTRING},
93	{ILBD_RULE_PORT, "port", SCF_TYPE_ASTRING},
94	{ILBD_RULE_ALGO, "ilb-algo", SCF_TYPE_ASTRING},
95	{ILBD_RULE_TOPO, "ilb-type", SCF_TYPE_ASTRING},
96	{ILBD_RULE_NAT_STR, "ilb-nat-start", SCF_TYPE_INVALID},
97	{ILBD_RULE_NAT_END, "ilb-nat-end", SCF_TYPE_INVALID},
98	{ILBD_RULE_STI_MASK, "ilb-sti-mask", SCF_TYPE_INVALID},
99	{ILBD_RULE_SGNAME, "servergroup", SCF_TYPE_ASTRING},
100	{ILBD_RULE_HCNAME, "healthcheck", SCF_TYPE_ASTRING},
101	{ILBD_RULE_HCPORT, "hc-port", SCF_TYPE_INTEGER},
102	{ILBD_RULE_HCPFLAG, "hcp-flag", SCF_TYPE_INTEGER},
103	{ILBD_RULE_DRAINTIME, "drain-time", SCF_TYPE_INTEGER},
104	{ILBD_RULE_NAT_TO, "nat-timeout", SCF_TYPE_INTEGER},
105	{ILBD_RULE_PERS_TO, "pers-timeout", SCF_TYPE_INTEGER},
106	/* add new rule related prop here */
107	/* entries for sg */
108	{ILBD_SG_SERVER, "server", SCF_TYPE_ASTRING},
109	/* add new sg related prop here */
110	/* entries for hc */
111	{ILBD_HC_TEST, "test", SCF_TYPE_ASTRING},
112	{ILBD_HC_TIMEOUT, "timeout", SCF_TYPE_INTEGER},
113	{ILBD_HC_INTERVAL, "interval", SCF_TYPE_INTEGER},
114	{ILBD_HC_DEF_PING, "ping", SCF_TYPE_BOOLEAN},
115	/* add new hc related prop here */
116	{ILBD_HC_COUNT, "count", SCF_TYPE_INTEGER}
117};
118
119#define	ILBD_PROP_VAR_NUM (ILBD_HC_COUNT + 1)
120#define	ILBD_RULE_VAR_NUM (ILBD_SG_SERVER)
121#define	ILBD_SG_VAR_NUM (ILBD_HC_TEST - ILBD_SG_SERVER)
122#define	ILBD_HC_VAR_NUM (ILBD_PROP_VAR_NUM - ILBD_HC_TEST)
123
124static ilb_status_t ilbd_scf_set_prop(scf_propertygroup_t *, const char *,
125    scf_type_t, scf_value_t *);
126static ilb_status_t ilbd_scf_retrieve_pg(const char *, scf_propertygroup_t **,
127    boolean_t);
128static ilb_status_t ilbd_scf_delete_pg(scf_propertygroup_t *);
129static ilb_status_t ilbd_scf_get_prop_val(scf_propertygroup_t *, const char *,
130    scf_value_t **);
131
132#define	MIN(a, b)	((a) < (b) ? (a) : (b))
133
134int
135ilbd_scf_limit(int type)
136{
137	return (MIN(scf_limit(type), 120));
138}
139
140/*
141 * Translate libscf error to libilb status
142 */
143ilb_status_t
144ilbd_scf_err_to_ilb_err()
145{
146	switch (scf_error()) {
147	case SCF_ERROR_NONE:
148		return (ILB_STATUS_OK);
149	case SCF_ERROR_HANDLE_MISMATCH:
150	case SCF_ERROR_HANDLE_DESTROYED:
151	case SCF_ERROR_VERSION_MISMATCH:
152	case SCF_ERROR_NOT_BOUND:
153	case SCF_ERROR_CONSTRAINT_VIOLATED:
154	case SCF_ERROR_NOT_SET:
155	case SCF_ERROR_TYPE_MISMATCH:
156	case SCF_ERROR_INVALID_ARGUMENT:
157		return (ILB_STATUS_EINVAL);
158	case SCF_ERROR_NO_MEMORY:
159	case SCF_ERROR_NO_RESOURCES:
160		return (ILB_STATUS_ENOMEM);
161	case SCF_ERROR_NOT_FOUND:
162	case SCF_ERROR_DELETED:
163		return (ILB_STATUS_ENOENT);
164	case SCF_ERROR_EXISTS:
165		return (ILB_STATUS_EEXIST);
166	case SCF_ERROR_PERMISSION_DENIED:
167		return (ILB_STATUS_PERMIT);
168	case SCF_ERROR_CALLBACK_FAILED:
169		return (ILB_STATUS_CALLBACK);
170	case SCF_ERROR_IN_USE:
171		return (ILB_STATUS_INUSE);
172	default:
173		return (ILB_STATUS_INTERNAL);
174	}
175}
176
177static void
178ilbd_name_to_scfpgname(ilbd_scf_pg_type_t pg_type, const char *pgname,
179    char *scf_pgname)
180{
181	switch (pg_type) {
182	case ILBD_SCF_RULE:
183		(void) snprintf(scf_pgname, ILBD_MAX_NAME_LEN,
184		    ILBD_PG_NAME_RULE "%s", pgname);
185		return;
186	case ILBD_SCF_SG:
187		(void) snprintf(scf_pgname, ILBD_MAX_NAME_LEN,
188		    ILBD_PG_NAME_SG "%s", pgname);
189		return;
190	case ILBD_SCF_HC:
191		(void) snprintf(scf_pgname, ILBD_MAX_NAME_LEN,
192		    ILBD_PG_NAME_HC "%s", pgname);
193		return;
194	/* Should not happen.  Log it and put ILB service in maintenance. */
195	default:
196		logerr("ilbd_name_to_scfpgname: invalid pg type %d for pg %s",
197		    pg_type, pgname);
198		(void) smf_maintain_instance(ILB_FMRI, SMF_IMMEDIATE);
199		exit(EXIT_FAILURE);
200		return;
201	}
202}
203
204static void
205ilbd_scf_destroy(scf_handle_t *h, scf_service_t *s, scf_instance_t *inst,
206    scf_propertygroup_t *pg)
207{
208	if (pg != NULL)
209		scf_pg_destroy(pg);
210	if (inst != NULL)
211		scf_instance_destroy(inst);
212	if (s != NULL)
213		scf_service_destroy(s);
214	if (h != NULL)
215		scf_handle_destroy(h);
216}
217
218
219static ilb_status_t
220ilbd_scf_get_inst(scf_handle_t **h, scf_service_t **svc, scf_instance_t **inst)
221{
222	if ((*h = scf_handle_create(SCF_VERSION)) == NULL)
223		return (ILB_STATUS_INTERNAL);
224
225	if (scf_handle_bind(*h) != 0) {
226		ilbd_scf_destroy(*h, NULL, NULL, NULL);
227		return (ilbd_scf_err_to_ilb_err());
228	}
229
230	if ((*svc = scf_service_create(*h)) == NULL) {
231		ilbd_scf_destroy(*h, NULL, NULL, NULL);
232		return (ilbd_scf_err_to_ilb_err());
233	}
234
235	if (scf_handle_decode_fmri(*h, ILBD_SVC_FMRI, NULL, *svc, NULL, NULL,
236	    NULL, SCF_DECODE_FMRI_EXACT) != 0) {
237		ilbd_scf_destroy(*h, *svc, NULL, NULL);
238		return (ilbd_scf_err_to_ilb_err());
239	}
240
241	if ((*inst = scf_instance_create(*h)) == NULL) {
242		ilbd_scf_destroy(*h, *svc, NULL, NULL);
243		return (ilbd_scf_err_to_ilb_err());
244	}
245
246	if (scf_service_get_instance(*svc, ILBD_INST_NAME, *inst) != 0) {
247		ilbd_scf_destroy(*h, *svc, *inst, NULL);
248		return (ilbd_scf_err_to_ilb_err());
249	}
250	return (ILB_STATUS_OK);
251}
252
253/*
254 * If create is set, create a new prop group, destroy the old one if exists.
255 * If create not set, try to find the prop group with given name.
256 * The created or found entry is returned as *pg.
257 * Caller frees *pg and its handle scf_pg_handle(pg)
258 */
259static ilb_status_t
260ilbd_scf_retrieve_pg(const char *pgname, scf_propertygroup_t **pg,
261    boolean_t create)
262{
263	scf_instance_t *inst;
264	scf_handle_t *h;
265	scf_service_t *svc;
266	ilb_status_t ret;
267
268	ret = ilbd_scf_get_inst(&h, &svc, &inst);
269	if (ret != ILB_STATUS_OK)
270		return (ret);
271
272	*pg = scf_pg_create(h);
273	if (*pg == NULL)
274		return (ILB_STATUS_INTERNAL);
275
276	if (scf_instance_get_pg(inst, pgname, *pg) != 0) {
277		if (scf_error() != SCF_ERROR_NOT_FOUND ||
278		    (scf_error() == SCF_ERROR_NOT_FOUND && (!create))) {
279			ilbd_scf_destroy(h, svc, inst, *pg);
280			*pg = NULL;
281			return (ilbd_scf_err_to_ilb_err());
282		}
283	} else {
284		/*
285		 * Found pg, don't want to create, return EEXIST.  Note that
286		 * h cannot be destroyed here since the caller needs to use it.
287		 * The caller gets it by calling scf_pg_handle().
288		 */
289		if (!create) {
290			ilbd_scf_destroy(NULL, svc, inst, NULL);
291			return (ILB_STATUS_EEXIST);
292		}
293		/* found pg, need to create, destroy the existing one */
294		else
295			(void) ilbd_scf_delete_pg(*pg);
296	}
297
298	if (create) {
299		if (scf_instance_add_pg(inst, pgname,
300		    SCF_GROUP_APPLICATION, 0, *pg) != 0) {
301			ilbd_scf_destroy(h, svc, inst, *pg);
302			*pg = NULL;
303			return (ilbd_scf_err_to_ilb_err());
304		}
305	}
306
307	/*
308	 * Note that handle cannot be destroyed here, caller sometimes needs
309	 * to use it.  It gets the handle by calling scf_pg_handle().
310	 */
311	ilbd_scf_destroy(NULL, svc, inst, NULL);
312	return (ILB_STATUS_OK);
313}
314
315struct algo_tbl_entry {
316	ilb_algo_t algo_type;
317	const char *algo_str;
318} algo_tbl[] = {
319	{ILB_ALG_ROUNDROBIN, "ROUNDROBIN"},
320	{ILB_ALG_HASH_IP, "HASH-IP"},
321	{ILB_ALG_HASH_IP_SPORT, "HASH-IP-PORT"},
322	{ILB_ALG_HASH_IP_VIP, "HASH-IP-VIP"}
323};
324
325#define	ILBD_ALGO_TBL_SIZE (sizeof (algo_tbl) / \
326	sizeof (*algo_tbl))
327
328void
329ilbd_algo_to_str(ilb_algo_t algo_type, char *valstr)
330{
331	int i;
332
333	for (i = 0; i < ILBD_ALGO_TBL_SIZE; i++) {
334		if (algo_type == algo_tbl[i].algo_type) {
335			(void) strlcpy(valstr, algo_tbl[i].algo_str,
336			    ILBD_MAX_VALUE_LEN);
337			return;
338		}
339	}
340	logerr("ilbd_algo_to_str: algo not found");
341}
342
343static void
344ilbd_scf_str_to_algo(ilb_algo_t *algo_type, char *valstr)
345{
346	int i;
347
348	for (i = 0; i < ILBD_ALGO_TBL_SIZE; i++) {
349		if (strcmp(valstr, algo_tbl[i].algo_str) == 0) {
350			*algo_type = algo_tbl[i].algo_type;
351			return;
352		}
353	}
354	logerr("ilbd_scf_str_to_algo: algo not found");
355}
356
357struct topo_tbl_entry {
358	ilb_topo_t topo_type;
359	const char *topo_str;
360} topo_tbl[] = {
361	{ILB_TOPO_DSR, "DSR"},
362	{ILB_TOPO_NAT, "NAT"},
363	{ILB_TOPO_HALF_NAT, "HALF-NAT"}
364};
365
366#define	ILBD_TOPO_TBL_SIZE (sizeof (topo_tbl) / \
367	sizeof (*topo_tbl))
368
369void
370ilbd_topo_to_str(ilb_topo_t topo_type, char *valstr)
371{
372	int i;
373
374	for (i = 0; i < ILBD_TOPO_TBL_SIZE; i++) {
375		if (topo_type == topo_tbl[i].topo_type) {
376			(void) strlcpy(valstr, topo_tbl[i].topo_str,
377			    ILBD_MAX_VALUE_LEN);
378			return;
379		}
380	}
381	logerr("ilbd_scf_topo_to_str: topo not found");
382}
383
384static void
385ilbd_scf_str_to_topo(ilb_topo_t *topo_type, char *valstr)
386{
387	int i;
388
389	for (i = 0; i < ILBD_TOPO_TBL_SIZE; i++) {
390		if (strcmp(valstr, topo_tbl[i].topo_str) == 0) {
391			*topo_type = topo_tbl[i].topo_type;
392			return;
393		}
394	}
395	logerr("ilbd_scf_str_to_topo: topo not found");
396}
397
398static void
399ilbd_get_svr_field(char *valstr, struct in6_addr *sgs_addr,
400    int32_t *min_port, int32_t *max_port, int32_t *sgs_flags)
401{
402	char *ipaddr, *ipverstr, *portstr, *flagstr;
403	int ip_ver;
404	ilb_ip_addr_t temp_ip;
405	void *addrptr;
406	char *max_portstr;
407
408	ipaddr = strtok(valstr, ";");
409	ipverstr = strtok(NULL, ";");
410	portstr = strtok(NULL, ";");
411	flagstr = strtok(NULL, ";");
412
413	if (ipaddr == NULL || ipverstr == NULL || portstr == NULL ||
414	    flagstr == NULL) {
415		logerr("%s: invalid server fields", __func__);
416		(void) smf_maintain_instance(ILB_FMRI, SMF_IMMEDIATE);
417		exit(EXIT_FAILURE);
418	}
419	ip_ver = atoi(ipverstr);
420	addrptr = (ip_ver == AF_INET) ? (void *)&temp_ip.ia_v4 :
421	    (void *)&temp_ip.ia_v6;
422	if (inet_pton(ip_ver, ipaddr, addrptr) == 0) {
423		logerr("ilbd_get_svr_field: inet_pton failed");
424		return;
425	}
426
427	if (ip_ver == AF_INET) {
428		IN6_INADDR_TO_V4MAPPED(&(temp_ip.ia_v4), sgs_addr);
429	} else {
430		(void) memcpy(sgs_addr, &(temp_ip.ia_v6),
431		    sizeof (struct in6_addr));
432	}
433
434	*sgs_flags = atoi(flagstr);
435	*min_port = atoi(strtok(portstr, "-"));
436	*min_port = ntohs(*min_port);
437	max_portstr = strtok(NULL, "-");
438	if (max_portstr != NULL) {
439		*max_port = atoi(max_portstr);
440		*max_port = ntohs(*max_port);
441	}
442}
443
444/*
445 * Convert the info of a server to its SCF string value representation.
446 * Argument value is assumed to be of size ILBD_MAX_VALUE_LEN.
447 */
448static void
449ilbd_srv_scf_val(ilbd_srv_t *srv, char *value)
450{
451	char ipstr[INET6_ADDRSTRLEN];
452	int ipver;
453
454	if (GET_AF(&srv->isv_addr) == AF_INET) {
455		struct in_addr v4_addr;
456
457		IN6_V4MAPPED_TO_INADDR(&srv->isv_addr, &v4_addr);
458		(void) inet_ntop(AF_INET, &v4_addr, ipstr, sizeof (ipstr));
459		ipver = AF_INET;
460	} else {
461		(void) inet_ntop(AF_INET6, &srv->isv_addr, ipstr,
462		    sizeof (ipstr));
463		ipver = AF_INET6;
464	}
465	(void) snprintf(value, ILBD_MAX_VALUE_LEN, "%s;%d;%d-%d;%d",
466	    ipstr, ipver, ntohs(srv->isv_minport), ntohs(srv->isv_maxport),
467	    srv->isv_flags);
468}
469
470/* get the "ip:port:status" str of the #num server in the servergroup */
471ilb_status_t
472ilbd_get_svr_info(ilbd_sg_t *sg, int num, char *valstr, char *svrname)
473{
474	int i;
475	ilbd_srv_t *tmp_srv = NULL;
476
477	tmp_srv = list_head(&sg->isg_srvlist);
478	if (tmp_srv == NULL)
479		return (ILB_STATUS_ENOENT);
480
481	for (i = 0; i < num; i++)
482		tmp_srv = list_next(&sg->isg_srvlist, tmp_srv);
483
484	assert(tmp_srv != NULL);
485	if (valstr != NULL)
486		ilbd_srv_scf_val(tmp_srv, valstr);
487
488	if (svrname != NULL) {
489		(void) snprintf(svrname, ILBD_MAX_NAME_LEN, "server%d",
490		    tmp_srv->isv_id);
491	}
492
493	return (ILB_STATUS_OK);
494}
495
496/* convert a struct in6_addr to valstr */
497ilb_status_t
498ilbd_scf_ip_to_str(uint16_t ipversion, struct in6_addr *addr,
499    scf_type_t *scftype, char *valstr)
500{
501	size_t vallen;
502	ilb_ip_addr_t ipaddr;
503	void *addrptr;
504
505	vallen = (ipversion == AF_INET) ? INET_ADDRSTRLEN :
506	    INET6_ADDRSTRLEN;
507	if (scftype != NULL)
508		*scftype = (ipversion == AF_INET) ? SCF_TYPE_NET_ADDR_V4 :
509		    SCF_TYPE_NET_ADDR_V6;
510
511	IP_COPY_IMPL_2_CLI(addr, &ipaddr);
512	addrptr = (ipversion == AF_INET) ?
513	    (void *)&ipaddr.ia_v4 : (void *)&ipaddr.ia_v6;
514	(void) inet_ntop(ipversion, (void *)addrptr, valstr, vallen);
515	return (ILB_STATUS_OK);
516}
517
518/*
519 * This function takes a ilbd internal data struct and translate its value to
520 * scf value. The data struct is passed in within "data".
521 * Upon successful return, the scf val will be stored in "val" and the scf type
522 * will be returned in "scftype" if scftype != NULL, the number of values
523 * translated will be in "numval"
524 * If it failed, no data will be written to SCF
525 */
526static ilb_status_t
527ilbd_data_to_scfval(ilbd_scf_pg_type_t pg_type, ilbd_var_type_t type,
528    scf_handle_t *h, void *data, scf_value_t ***val, scf_type_t *scftype,
529    int *numval)
530{
531	scf_value_t *v, **varray = NULL;
532	int ret = ILB_STATUS_OK;
533	int i;
534	int scf_val_len = ILBD_MAX_VALUE_LEN;
535	char *valstr = NULL;
536	int valint;
537	uint8_t valbool = 0;
538	ilbd_rule_t *r_ent = NULL;
539	ilbd_sg_t *s_ent = NULL;
540	ilbd_hc_t *h_ent = NULL;
541
542	switch (pg_type) {
543	case ILBD_SCF_RULE:
544		r_ent = (ilbd_rule_t *)data;
545		break;
546	case ILBD_SCF_SG:
547		s_ent = (ilbd_sg_t *)data;
548		break;
549	case ILBD_SCF_HC:
550		h_ent = (ilbd_hc_t *)data;
551		break;
552	}
553
554	v = scf_value_create(h);
555	if (v == NULL)
556		return (ILB_STATUS_INTERNAL);
557
558	if ((valstr = malloc(scf_val_len)) == NULL)
559			return (ILB_STATUS_ENOMEM);
560	switch (type) {
561	case ILBD_RULE_STATUS:
562		valbool = r_ent->irl_flags & ILB_FLAGS_RULE_ENABLED;
563		break;
564	case ILBD_RULE_VIP:
565		ret = ilbd_scf_ip_to_str(r_ent->irl_ipversion, &r_ent->irl_vip,
566		    scftype, valstr);
567		if (ret != ILB_STATUS_OK) {
568			free(valstr);
569			scf_value_destroy(v);
570			return (ret);
571		}
572		break;
573	case ILBD_RULE_PROTO: {
574		struct protoent *protoent;
575
576		protoent = getprotobynumber(r_ent->irl_proto);
577		(void) strlcpy(valstr, protoent->p_name, scf_val_len);
578		break;
579	}
580	case ILBD_RULE_PORT:
581		(void) snprintf(valstr, scf_val_len, "%d-%d",
582		    r_ent->irl_minport, r_ent->irl_maxport);
583		break;
584	case ILBD_RULE_ALGO:
585		ilbd_algo_to_str(r_ent->irl_algo, valstr);
586		break;
587	case ILBD_RULE_TOPO:
588		ilbd_topo_to_str(r_ent->irl_topo, valstr);
589		break;
590	case ILBD_RULE_NAT_STR:
591		ret = ilbd_scf_ip_to_str(r_ent->irl_ipversion,
592		    &r_ent->irl_nat_src_start, scftype, valstr);
593		if (ret != ILB_STATUS_OK) {
594			free(valstr);
595			scf_value_destroy(v);
596			return (ret);
597		}
598		break;
599	case ILBD_RULE_NAT_END:
600		ret = ilbd_scf_ip_to_str(r_ent->irl_ipversion,
601		    &r_ent->irl_nat_src_end, scftype, valstr);
602		if (ret != ILB_STATUS_OK) {
603			free(valstr);
604			scf_value_destroy(v);
605			return (ret);
606		}
607		break;
608	case ILBD_RULE_STI_MASK:
609		ret = ilbd_scf_ip_to_str(r_ent->irl_ipversion,
610		    &r_ent->irl_stickymask, scftype, valstr);
611		if (ret != ILB_STATUS_OK) {
612			free(valstr);
613			scf_value_destroy(v);
614			return (ret);
615		}
616		break;
617	case ILBD_RULE_SGNAME:
618		(void) strlcpy(valstr, r_ent->irl_sgname, scf_val_len);
619		break;
620	case ILBD_RULE_HCNAME:
621		if (r_ent->irl_hcname[0] != '\0')
622			(void) strlcpy(valstr, r_ent->irl_hcname,
623			    scf_val_len);
624		else
625			bzero(valstr, ILBD_MAX_VALUE_LEN);
626		break;
627	case ILBD_RULE_HCPORT:
628		valint = r_ent->irl_hcport;
629		break;
630	case ILBD_RULE_HCPFLAG:
631		valint = r_ent->irl_hcpflag;
632		break;
633	case ILBD_RULE_DRAINTIME:
634		valint = r_ent->irl_conndrain;
635		break;
636	case ILBD_RULE_NAT_TO:
637		valint = r_ent->irl_nat_timeout;
638		break;
639	case ILBD_RULE_PERS_TO:
640		valint = r_ent->irl_sticky_timeout;
641		break;
642
643	case ILBD_SG_SERVER:
644		if (s_ent->isg_srvcount == 0) {
645			(void) strlcpy(valstr, "EMPTY_SERVERGROUP",
646			    scf_val_len);
647			break;
648		}
649
650		varray = calloc(sizeof (*varray), s_ent->isg_srvcount);
651		if (varray == NULL) {
652			scf_value_destroy(v);
653			free(valstr);
654			return (ILB_STATUS_ENOMEM);
655		}
656
657		for (i = 0; i < s_ent->isg_srvcount; i++) {
658			if (v == NULL) {
659				for (i--; i >= 0; i--)
660					scf_value_destroy(varray[i]);
661				free(valstr);
662				return (ILB_STATUS_ENOMEM);
663			}
664
665			ret = ilbd_get_svr_info(s_ent, i, valstr, NULL);
666			if (ret != ILB_STATUS_OK) {
667				scf_value_destroy(v);
668				for (i--; i >= 0; i--)
669					scf_value_destroy(varray[i]);
670				free(valstr);
671				free(varray);
672				return (ret);
673			}
674			(void) scf_value_set_astring(v, valstr);
675			varray[i] = v;
676			v = scf_value_create(h);
677		}
678		/* the last 'v' we created will go unused, so drop it */
679		scf_value_destroy(v);
680		*numval = s_ent->isg_srvcount;
681		*val = varray;
682		free(valstr);
683		return (ret);
684	case ILBD_HC_TEST:
685		(void) strlcpy(valstr, h_ent->ihc_test, scf_val_len);
686		break;
687	case ILBD_HC_TIMEOUT:
688		valint = h_ent->ihc_timeout;
689		break;
690	case ILBD_HC_INTERVAL:
691		valint = h_ent->ihc_interval;
692		break;
693	case ILBD_HC_DEF_PING:
694		valbool = h_ent->ihc_def_ping;
695		break;
696	case ILBD_HC_COUNT:
697		valint = h_ent->ihc_count;
698		break;
699	}
700
701	switch (*scftype) {
702	case SCF_TYPE_BOOLEAN:
703		scf_value_set_boolean(v, valbool);
704		break;
705	case SCF_TYPE_ASTRING:
706		(void) scf_value_set_astring(v, valstr);
707		break;
708	case SCF_TYPE_INTEGER:
709		scf_value_set_integer(v, valint);
710		break;
711	case SCF_TYPE_NET_ADDR_V4:
712		(void) scf_value_set_from_string(v, SCF_TYPE_NET_ADDR_V4,
713		    valstr);
714		break;
715	case SCF_TYPE_NET_ADDR_V6:
716		(void) scf_value_set_from_string(v, SCF_TYPE_NET_ADDR_V6,
717		    valstr);
718		break;
719	}
720	free(valstr);
721
722	varray = calloc(1, sizeof (*varray));
723	if (varray == NULL) {
724		scf_value_destroy(v);
725		return (ILB_STATUS_ENOMEM);
726	}
727	varray[0] = v;
728	*val = varray;
729	*numval = 1;
730	return (ret);
731}
732
733/*
734 * create a scf property group
735 */
736ilb_status_t
737ilbd_create_pg(ilbd_scf_pg_type_t pg_type, void *data)
738{
739	ilb_status_t ret;
740	char *pgname;
741	scf_propertygroup_t *pg = NULL;
742	scf_value_t **val;
743	scf_handle_t *h;
744	int scf_name_len = ILBD_MAX_NAME_LEN;
745	char  *scfpgbuf; /* property group name or group type */
746	int i, i_st, i_end;
747
748	switch (pg_type) {
749	case ILBD_SCF_RULE: {
750		ilbd_rule_t *r_ent = (ilbd_rule_t *)data;
751
752		pgname = r_ent->irl_name;
753		i_st = 0;
754		i_end = ILBD_RULE_VAR_NUM;
755		break;
756	}
757	case ILBD_SCF_SG: {
758		ilbd_sg_t *s_ent = (ilbd_sg_t *)data;
759
760		pgname = s_ent->isg_name;
761		i_st = ILBD_RULE_VAR_NUM;
762		i_end = ILBD_RULE_VAR_NUM + ILBD_SG_VAR_NUM;
763		break;
764	}
765	case ILBD_SCF_HC: {
766		ilbd_hc_t *h_ent = (ilbd_hc_t *)data;
767
768		pgname = h_ent->ihc_name;
769		i_st = ILBD_RULE_VAR_NUM + ILBD_SG_VAR_NUM;
770		i_end = ILBD_PROP_VAR_NUM;
771		break;
772	}
773	default:
774		logdebug("ilbd_create_pg: invalid pg type %d for pg %s",
775		    pg_type, pgname);
776		return (ILB_STATUS_EINVAL);
777	}
778	if ((scfpgbuf = malloc(scf_name_len)) == NULL)
779		return (ILB_STATUS_ENOMEM);
780
781	ilbd_name_to_scfpgname(pg_type, pgname, scfpgbuf);
782
783	ret = ilbd_scf_retrieve_pg(scfpgbuf, &pg, B_TRUE);
784	if (ret != ILB_STATUS_OK) {
785		free(scfpgbuf);
786		return (ret);
787	}
788	h = scf_pg_handle(pg);
789
790	/* fill in props */
791	for (i = i_st; i < i_end; i++) {
792		int num, j;
793		scf_type_t scftype = prop_tbl[i].scf_proptype;
794
795		ret = ilbd_data_to_scfval(pg_type, prop_tbl[i].val_type, h,
796		    data, &val, &scftype, &num);
797		if (ret != ILB_STATUS_OK)
798			goto done;
799
800		for (j = 0; j < num; j++) {
801			if (pg_type == ILBD_SCF_SG) {
802				ret = ilbd_get_svr_info(data, j, NULL,
803				    scfpgbuf);
804				if (ret == ILB_STATUS_ENOENT) {
805					(void) strlcpy(scfpgbuf,
806					    "EMPTY_SERVER", scf_name_len);
807				}
808				ret = ilbd_scf_set_prop(pg, scfpgbuf,
809				    scftype, val[j]);
810			} else {
811				ret = ilbd_scf_set_prop(pg,
812				    prop_tbl[i].scf_propname, scftype, val[j]);
813			}
814			scf_value_destroy(val[j]);
815		}
816		free(val);
817	}
818
819done:
820	free(scfpgbuf);
821	ilbd_scf_destroy(h, NULL, NULL, pg);
822	return (ret);
823}
824
825/*
826 * destroy a scf property group
827 */
828static ilb_status_t
829ilbd_scf_delete_pg(scf_propertygroup_t *pg)
830{
831	if (scf_pg_delete(pg) != 0)
832		return (ilbd_scf_err_to_ilb_err());
833	return (ILB_STATUS_OK);
834}
835
836/* sg can have same name as rule */
837ilb_status_t
838ilbd_destroy_pg(ilbd_scf_pg_type_t pg_t, const char *pgname)
839{
840	ilb_status_t ret;
841	scf_propertygroup_t *pg;
842	int scf_name_len = ILBD_MAX_NAME_LEN;
843	char *scfname;
844
845	if ((scfname = malloc(scf_name_len)) == NULL)
846		return (ILB_STATUS_ENOMEM);
847	ilbd_name_to_scfpgname(pg_t, pgname, scfname);
848
849	ret = ilbd_scf_retrieve_pg(scfname, &pg, B_FALSE);
850	free(scfname);
851	if (ret != ILB_STATUS_EEXIST)
852		return (ret);
853	ret = ilbd_scf_delete_pg(pg);
854	ilbd_scf_destroy(scf_pg_handle(pg), NULL, NULL, pg);
855	return (ret);
856}
857
858/*
859 * Set named property to scf value specified.  If property is new,
860 * create it.
861 */
862static ilb_status_t
863ilbd_scf_set_prop(scf_propertygroup_t *pg, const char *propname,
864    scf_type_t proptype, scf_value_t *val)
865{
866	scf_handle_t *h = NULL;
867	scf_property_t *prop = NULL;
868	scf_value_t *oldval = NULL;
869	scf_transaction_t *tx = NULL;
870	scf_transaction_entry_t *ent = NULL;
871	boolean_t new = B_FALSE;
872	ilb_status_t ret = ILB_STATUS_OK;
873	int commit_ret;
874
875	h = scf_pg_handle(pg);
876	if (h == NULL || propname == NULL)
877		return (ILB_STATUS_EINVAL);
878
879	ret = ilbd_scf_get_prop_val(pg, propname, &oldval);
880	if (oldval != NULL)
881		scf_value_destroy(oldval);
882	if (ret == ILB_STATUS_ENOENT)
883		new = B_TRUE;
884	else if (ret != ILB_STATUS_OK)
885		return (ret);
886
887	if ((prop = scf_property_create(h)) == NULL)
888		return (ilbd_scf_err_to_ilb_err());
889	if ((tx = scf_transaction_create(h)) == NULL ||
890	    (ent = scf_entry_create(h)) == NULL) {
891		ret = ilbd_scf_err_to_ilb_err();
892		logdebug("ilbd_scf_set_prop: create scf transaction failed\n");
893		goto out;
894	}
895
896	if (scf_transaction_start(tx, pg) == -1) {
897		ret = ilbd_scf_err_to_ilb_err();
898		logdebug("ilbd_scf_set_prop: start scf transaction failed\n");
899		goto out;
900	}
901
902	if (new) {
903		if (scf_transaction_property_new(tx, ent, propname,
904		    proptype) == -1) {
905			ret = ilbd_scf_err_to_ilb_err();
906			logdebug("ilbd_scf_set_prop: create scf prop failed\n");
907			goto out;
908		}
909	} else {
910		if (scf_transaction_property_change(tx, ent, propname, proptype)
911		    == -1) {
912			ret = ilbd_scf_err_to_ilb_err();
913			logdebug("ilbd_scf_set_prop: change scf prop failed\n");
914			goto out;
915		}
916	}
917
918	if (scf_entry_add_value(ent, val) != 0) {
919		logdebug("ilbd_scf_set_prop: add scf entry failed\n");
920		ret = ilbd_scf_err_to_ilb_err();
921		goto out;
922	}
923
924	commit_ret = scf_transaction_commit(tx);
925	switch (commit_ret) {
926	case 1:
927		ret = ILB_STATUS_OK;
928		/* update pg here, so subsequent property setting  succeeds */
929		(void) scf_pg_update(pg);
930		break;
931	case 0:
932		/* transaction failed due to not having most recent pg */
933		ret = ILB_STATUS_INUSE;
934		break;
935	default:
936		ret = ilbd_scf_err_to_ilb_err();
937		break;
938	}
939out:
940	if (tx != NULL)
941		scf_transaction_destroy(tx);
942	if (ent != NULL)
943		scf_entry_destroy(ent);
944	if (prop != NULL)
945		scf_property_destroy(prop);
946
947	return (ret);
948}
949
950/*
951 * get a prop's scf val
952 */
953static ilb_status_t
954ilbd_scf_get_prop_val(scf_propertygroup_t *pg, const char *propname,
955    scf_value_t **val)
956{
957	scf_handle_t *h = NULL;
958	scf_property_t *prop = NULL;
959	scf_value_t *value = NULL;
960	ilb_status_t ret = ILB_STATUS_OK;
961
962	h = scf_pg_handle(pg);
963	if (h == NULL || propname == NULL)
964		return (ILB_STATUS_EINVAL);
965
966	if ((prop = scf_property_create(h)) == NULL)
967		return (ilbd_scf_err_to_ilb_err());
968
969	if (scf_pg_get_property(pg, propname, prop) != 0) {
970		ret = ilbd_scf_err_to_ilb_err();
971		goto out;
972	}
973
974	if ((value = scf_value_create(h)) == NULL) {
975		ret = ilbd_scf_err_to_ilb_err();
976		goto out;
977	}
978
979	if (scf_property_get_value(prop, value) != 0) {
980		scf_value_destroy(value);
981		ret = ilbd_scf_err_to_ilb_err();
982		goto out;
983	}
984
985	*val = value;
986out:
987	if (prop != NULL)
988		scf_property_destroy(prop);
989
990	return (ret);
991}
992
993typedef struct ilbd_data
994{
995	union {
996		ilb_sg_info_t *sg_info;
997		ilb_hc_info_t *hc_info;
998		ilb_rule_info_t *rule_info;
999	} data;
1000	ilbd_scf_pg_type_t pg_type;	/* type of data */
1001#define	sg_data data.sg_info
1002#define	hc_data data.hc_info
1003#define	rule_data data.rule_info
1004} ilbd_data_t;
1005
1006void
1007ilbd_scf_str_to_ip(int ipversion, char *ipstr, struct in6_addr *addr)
1008{
1009	ilb_ip_addr_t ipaddr;
1010	void *addrptr;
1011
1012	addrptr = (ipversion == AF_INET) ?
1013	    (void *)&ipaddr.ia_v4 : (void *)&ipaddr.ia_v6;
1014	(void) inet_pton(ipversion, ipstr, addrptr);
1015	if (ipversion == AF_INET) {
1016		IN6_INADDR_TO_V4MAPPED(&(ipaddr.ia_v4), addr);
1017	} else {
1018		(void) memcpy(addr, &(ipaddr.ia_v6),
1019		    sizeof (struct in6_addr));
1020	}
1021}
1022
1023/*
1024 * This function takes a scf value and writes it to the correct field of the
1025 * corresponding data struct.
1026 */
1027static ilb_status_t
1028ilbd_scfval_to_data(const char *propname, ilbd_var_type_t ilb_type,
1029    scf_value_t *val, ilbd_data_t *ilb_data)
1030{
1031
1032	scf_type_t scf_type = scf_value_type(val);
1033	ilbd_scf_pg_type_t pg_type = ilb_data->pg_type;
1034	int ret = 0;
1035	ilb_rule_info_t *r_ent = NULL;
1036	ilb_sg_info_t *s_ent = NULL;
1037	ilb_hc_info_t *h_ent = NULL;
1038	char ipstr[INET6_ADDRSTRLEN];
1039	char *valstr;
1040	int64_t valint;
1041	uint8_t valbool;
1042	int ipversion;
1043
1044	switch (pg_type) {
1045	case ILBD_SCF_RULE:
1046		r_ent = ilb_data->rule_data;
1047		break;
1048	case ILBD_SCF_HC:
1049		h_ent = ilb_data->hc_data;
1050		break;
1051	case ILBD_SCF_SG:
1052		s_ent = ilb_data->sg_data;
1053		break;
1054	}
1055
1056	/* get scf value out */
1057	if ((valstr = malloc(ILBD_MAX_VALUE_LEN)) == NULL)
1058		return (ILB_STATUS_ENOMEM);
1059	switch (scf_type) {
1060		case SCF_TYPE_NET_ADDR_V4:
1061			if (scf_value_get_as_string_typed(val,
1062			    SCF_TYPE_NET_ADDR_V4, ipstr, INET_ADDRSTRLEN) < 0) {
1063				free(valstr);
1064				return (ILB_STATUS_INTERNAL);
1065			}
1066			ipversion = AF_INET;
1067			break;
1068		case SCF_TYPE_NET_ADDR_V6:
1069			if (scf_value_get_as_string_typed(val,
1070			    SCF_TYPE_NET_ADDR_V6, ipstr,
1071			    INET6_ADDRSTRLEN) < 0) {
1072				free(valstr);
1073				return (ILB_STATUS_INTERNAL);
1074			}
1075			ipversion = AF_INET6;
1076			break;
1077		case SCF_TYPE_BOOLEAN:
1078			if (scf_value_get_boolean(val, &valbool) < 0) {
1079				free(valstr);
1080				return (ILB_STATUS_INTERNAL);
1081			}
1082			break;
1083		case SCF_TYPE_ASTRING:
1084			if (scf_value_get_astring(val, valstr,
1085			    ILBD_MAX_VALUE_LEN) < 0) {
1086				free(valstr);
1087				return (ILB_STATUS_INTERNAL);
1088			}
1089			break;
1090		case SCF_TYPE_INTEGER:
1091			if (scf_value_get_integer(val, &valint) < 0) {
1092				free(valstr);
1093				return (ILB_STATUS_INTERNAL);
1094			}
1095			break;
1096		default:
1097			free(valstr);
1098			return (ILB_STATUS_INTERNAL);
1099	}
1100
1101	ret = ILB_STATUS_OK;
1102	switch (ilb_type) {
1103	case ILBD_RULE_STATUS:
1104		if (valbool)
1105			r_ent->rl_flags |= ILB_FLAGS_RULE_ENABLED;
1106		break;
1107	case ILBD_RULE_VIP:
1108		r_ent->rl_ipversion = ipversion;
1109		ilbd_scf_str_to_ip(ipversion, ipstr, &r_ent->rl_vip);
1110		break;
1111	case ILBD_RULE_PROTO: {
1112		struct protoent *protoent;
1113
1114		protoent = getprotobyname(valstr);
1115		r_ent->rl_proto = protoent->p_proto;
1116		break;
1117	}
1118	case ILBD_RULE_PORT: {
1119		char *token1, *token2;
1120
1121		token1 = strtok(valstr, "-");
1122		token2 = strtok(NULL, "-");
1123		r_ent->rl_minport = atoi(token1);
1124		r_ent->rl_maxport = atoi(token2);
1125		break;
1126	}
1127	case ILBD_RULE_ALGO:
1128		ilbd_scf_str_to_algo(&(r_ent->rl_algo), valstr);
1129		break;
1130	case ILBD_RULE_TOPO:
1131		ilbd_scf_str_to_topo(&(r_ent->rl_topo), valstr);
1132		break;
1133	case ILBD_RULE_NAT_STR:
1134		ilbd_scf_str_to_ip(ipversion, ipstr, &r_ent->rl_nat_src_start);
1135		break;
1136	case ILBD_RULE_NAT_END:
1137		ilbd_scf_str_to_ip(ipversion, ipstr, &r_ent->rl_nat_src_end);
1138		break;
1139	case ILBD_RULE_STI_MASK:
1140		ilbd_scf_str_to_ip(ipversion, ipstr, &r_ent->rl_stickymask);
1141		if (ipversion == AF_INET) {
1142			if (!IN6_IS_ADDR_V4MAPPED_ANY(&r_ent->rl_stickymask))
1143				r_ent->rl_flags |= ILB_FLAGS_RULE_STICKY;
1144		} else {
1145			if (!IN6_IS_ADDR_UNSPECIFIED(&r_ent->rl_stickymask))
1146				r_ent->rl_flags |= ILB_FLAGS_RULE_STICKY;
1147		}
1148		break;
1149	case ILBD_RULE_SGNAME:
1150		(void) strlcpy(r_ent->rl_sgname, valstr,
1151		    sizeof (r_ent->rl_sgname));
1152		break;
1153	case ILBD_RULE_HCNAME:
1154		(void) strlcpy(r_ent->rl_hcname, valstr,
1155		    sizeof (r_ent->rl_hcname));
1156		break;
1157	case ILBD_RULE_HCPORT:
1158		r_ent->rl_hcport = valint;
1159		break;
1160	case ILBD_RULE_HCPFLAG:
1161		r_ent->rl_hcpflag = valint;
1162		break;
1163	case ILBD_RULE_DRAINTIME:
1164		r_ent->rl_conndrain = valint;
1165		break;
1166	case ILBD_RULE_NAT_TO:
1167		r_ent->rl_nat_timeout = valint;
1168		break;
1169	case ILBD_RULE_PERS_TO:
1170		r_ent->rl_sticky_timeout = valint;
1171		break;
1172
1173	case ILBD_SG_SERVER: {
1174		int svr_cnt = s_ent->sg_srvcount;
1175
1176		/* found a new server, increase the svr count of this sg */
1177		s_ent->sg_srvcount++;
1178
1179		/*
1180		 * valstr contains information of one server in the servergroup
1181		 * valstr is in the format of "ip:minport-maxport:enable"
1182		 */
1183		s_ent = realloc(s_ent, sizeof (ilb_sg_info_t) +
1184		    s_ent->sg_srvcount * sizeof (ilb_sg_srv_t));
1185
1186		/* sgs_srvID is the sg name, leave it blank */
1187		/*
1188		 * sgs_id is the digit in propname, propname is in a format of
1189		 * "server" + the digital serverID. We get the serverID by
1190		 * reading from the 7th char of propname.
1191		 */
1192		s_ent->sg_servers[svr_cnt].sgs_id = atoi(&propname[6]);
1193
1194		ilbd_get_svr_field(valstr,
1195		    &s_ent->sg_servers[svr_cnt].sgs_addr,
1196		    &s_ent->sg_servers[svr_cnt].sgs_minport,
1197		    &s_ent->sg_servers[svr_cnt].sgs_maxport,
1198		    &s_ent->sg_servers[svr_cnt].sgs_flags);
1199		ilb_data->sg_data = s_ent;
1200
1201		break;
1202	}
1203	case ILBD_HC_TEST:
1204		(void) strlcpy(h_ent->hci_test, valstr,
1205		    sizeof (h_ent->hci_test));
1206		break;
1207	case ILBD_HC_TIMEOUT:
1208		h_ent->hci_timeout = valint;
1209		break;
1210	case ILBD_HC_INTERVAL:
1211		h_ent->hci_interval = valint;
1212		break;
1213	case ILBD_HC_DEF_PING:
1214		h_ent->hci_def_ping = valbool;
1215		break;
1216	case ILBD_HC_COUNT:
1217		h_ent->hci_count = valint;
1218		break;
1219	case ILBD_VAR_INVALID:
1220		/*
1221		 * An empty server group is represented by an invalid
1222		 * SCF property.  So when loading a server group, this
1223		 * case can be hit.  But it should happen only for this
1224		 * single case.  So if it happens in another case, move
1225		 * the service into maintenance mode.
1226		 */
1227		if (pg_type != ILBD_SCF_SG || scf_type != SCF_TYPE_ASTRING) {
1228			logerr("%s: invalid ilb type", __func__);
1229			(void) smf_maintain_instance(ILB_FMRI, SMF_IMMEDIATE);
1230		} else {
1231			logdebug("%s: invalid ilb type", __func__);
1232		}
1233		break;
1234	}
1235
1236	free(valstr);
1237	return (ret);
1238}
1239
1240static ilbd_var_type_t
1241ilbd_name_to_valtype(const char *prop_name)
1242{
1243	int i;
1244
1245	for (i = 0; i < ILBD_PROP_VAR_NUM; i++)
1246		if (strncmp(prop_name, prop_tbl[i].scf_propname,
1247		    strlen(prop_tbl[i].scf_propname)) == 0)
1248			return (prop_tbl[i].val_type);
1249
1250	logdebug("ilbd_name_to_valtype: couldn't find prop %s", prop_name);
1251	return (ILBD_VAR_INVALID);
1252}
1253
1254/* callback for pg_walk_prop, arg is ilbd_data_t */
1255static ilb_status_t
1256ilbd_scf_load_prop(scf_propertygroup_t *pg, const char *prop_name, void *arg)
1257{
1258	scf_handle_t *h;
1259	scf_value_t *val;
1260	ilb_status_t ret;
1261	ilbd_data_t *ilb_data = (ilbd_data_t *)arg;
1262	ilbd_var_type_t val_type = ilbd_name_to_valtype(prop_name);
1263
1264	h = scf_pg_handle(pg);
1265	if (h == NULL)
1266		return (ILB_STATUS_EINVAL);
1267
1268	ret = ilbd_scf_get_prop_val(pg, prop_name, &val);
1269	if (ret == ILB_STATUS_ENOENT)
1270		return (ILB_STATUS_OK);
1271	else if (ret != ILB_STATUS_OK)
1272		return (ret);
1273
1274	/*
1275	 * Load value to ilb_data.
1276	 */
1277	ret = ilbd_scfval_to_data(prop_name, val_type, val, ilb_data);
1278
1279out:
1280	if (val != NULL)
1281		scf_value_destroy(val);
1282
1283	return (ret);
1284}
1285
1286/*
1287 * walk properties in one prop group, arg is ilbd_data
1288 * cb is ilbd_scf_load_prop()
1289 */
1290static ilb_status_t
1291ilbd_scf_pg_walk_props(scf_propertygroup_t *pg,
1292    ilb_status_t (*cb)(scf_propertygroup_t *, const char *, void *),
1293    void *arg)
1294{
1295	scf_handle_t *h;
1296	scf_iter_t *propiter;
1297	scf_property_t *prop;
1298	int scf_name_len = ILBD_MAX_NAME_LEN;
1299	char *prop_name = NULL;
1300	ilb_status_t ret = ILB_STATUS_OK;
1301	int scf_ret = -1;
1302
1303	h = scf_pg_handle(pg);
1304	if (h == NULL)
1305		return (ILB_STATUS_EINVAL);
1306
1307	prop = scf_property_create(h);
1308	propiter = scf_iter_create(h);
1309	if (prop == NULL || propiter == NULL)
1310		goto out;
1311
1312	if (scf_iter_pg_properties(propiter, pg) != 0)
1313		goto out;
1314
1315	if ((prop_name = malloc(scf_name_len)) == NULL) {
1316		ret = ILB_STATUS_ENOMEM;
1317		goto out;
1318	}
1319	while ((scf_ret = scf_iter_next_property(propiter, prop)) == 1) {
1320		if (scf_property_get_name(prop, prop_name, scf_name_len)
1321		    < 0) {
1322			ret = ilbd_scf_err_to_ilb_err();
1323			goto out;
1324		}
1325		ret = cb(pg, prop_name, arg);
1326		if (ret != ILB_STATUS_OK)
1327			break;
1328	}
1329out:
1330	if (prop_name != NULL)
1331		free(prop_name);
1332	if (scf_ret == -1)
1333		ret = ilbd_scf_err_to_ilb_err();
1334	if (prop != NULL)
1335		scf_property_destroy(prop);
1336	if (propiter != NULL)
1337		scf_iter_destroy(propiter);
1338
1339	return (ret);
1340}
1341
1342/* cbs are libd_create_X */
1343static ilb_status_t
1344ilbd_scf_instance_walk_pg(scf_instance_t *inst,
1345    ilbd_scf_pg_type_t pg_type,
1346    ilb_status_t (*cb)(void *, int, struct passwd *, ucred_t *),
1347    void *arg1, void *arg2)
1348{
1349	int			scf_ret;
1350	ilb_status_t		ret;
1351	scf_handle_t		*h;
1352	scf_iter_t		*pgiter;
1353	scf_propertygroup_t	*newpg;
1354	int			port = *((int *)arg1);
1355	int scf_name_len = ILBD_MAX_NAME_LEN;
1356	char *pg_name = NULL;
1357
1358	if (inst == NULL)
1359		return (ILB_STATUS_EINVAL);
1360
1361	h = scf_instance_handle(inst);
1362	if (h == NULL)
1363		return (ILB_STATUS_EINVAL);
1364
1365	if ((newpg = scf_pg_create(h)) == NULL)
1366		return (ilbd_scf_err_to_ilb_err());
1367
1368	if ((pgiter = scf_iter_create(h)) == NULL) {
1369		scf_pg_destroy(newpg);
1370		return (ilbd_scf_err_to_ilb_err());
1371	}
1372
1373	if ((scf_ret = scf_iter_instance_pgs(pgiter, inst)) < 0)
1374		goto out;
1375
1376	if ((pg_name = malloc(scf_name_len)) == NULL) {
1377		ret = ILB_STATUS_ENOMEM;
1378		goto out;
1379	}
1380	while ((scf_ret = scf_iter_next_pg(pgiter, newpg)) > 0) {
1381		ilbd_data_t data;
1382
1383		if (scf_pg_get_name(newpg, pg_name, scf_name_len) < 0) {
1384			ret = ilbd_scf_err_to_ilb_err();
1385			goto out;
1386		}
1387
1388		/*
1389		 * if pg name indicates it's a ilb configuration, walk its prop
1390		 */
1391		data.pg_type = pg_type;
1392		data.hc_data = NULL;
1393		data.sg_data = NULL;
1394		data.rule_data = NULL;
1395
1396		switch (pg_type) {
1397		case ILBD_SCF_RULE:
1398			if (strncmp(ILBD_PG_NAME_RULE, pg_name,
1399			    strlen(ILBD_PG_NAME_RULE)) == 0) {
1400				data.rule_data = calloc(1,
1401				    sizeof (ilb_rule_info_t));
1402				if (data.rule_data == NULL) {
1403					ret = ILB_STATUS_ENOMEM;
1404					goto out;
1405				}
1406				ret = ilbd_scf_pg_walk_props(newpg,
1407				    ilbd_scf_load_prop, &data);
1408				if (ret != ILB_STATUS_OK)
1409					goto out;
1410				assert(data.rule_data != NULL);
1411				/* set rule name */
1412				(void) strlcpy(data.rule_data->rl_name,
1413				    &pg_name[strlen(ILBD_PG_NAME_RULE)],
1414				    sizeof (data.rule_data->rl_name));
1415
1416				ret = cb(data.rule_data, port, arg2, NULL);
1417				free(data.rule_data);
1418				if (ret != ILB_STATUS_OK)
1419					goto out;
1420			}
1421			break;
1422		case ILBD_SCF_SG:
1423			if (strncmp(ILBD_PG_NAME_SG, pg_name,
1424			    strlen(ILBD_PG_NAME_SG)) == 0) {
1425				data.sg_data = calloc(1,
1426				    sizeof (ilb_sg_info_t));
1427				if (data.sg_data == NULL) {
1428					ret = ILB_STATUS_ENOMEM;
1429					goto out;
1430				}
1431				ret = ilbd_scf_pg_walk_props(newpg,
1432				    ilbd_scf_load_prop, &data);
1433				if (ret != ILB_STATUS_OK) {
1434					free(data.sg_data);
1435					goto out;
1436				}
1437				assert(data.sg_data != NULL);
1438				/* set sg name */
1439				(void) strlcpy(data.sg_data->sg_name,
1440				    &pg_name[strlen(ILBD_PG_NAME_SG)],
1441				    sizeof (data.sg_data->sg_name));
1442				ret = cb(data.sg_data, port, arg2, NULL);
1443				if (ret != ILB_STATUS_OK) {
1444					free(data.sg_data);
1445					goto out;
1446				}
1447				/*
1448				 * create a servergroup is two-step operation.
1449				 * 1. create an empty servergroup.
1450				 * 2. add server(s) to the group.
1451				 *
1452				 * since we are here from:
1453				 * main_loop()->ilbd_read_config()->
1454				 * ilbd_walk_sg_pgs()
1455				 * there is no cli to send. So in this
1456				 * path auditing will skip the
1457				 * adt_set_from_ucred() check
1458				 */
1459				if (data.sg_data->sg_srvcount > 0) {
1460					ret = ilbd_add_server_to_group(
1461					    data.sg_data, port, NULL, NULL);
1462					if (ret != ILB_STATUS_OK) {
1463						free(data.sg_data);
1464						goto out;
1465					}
1466					free(data.sg_data);
1467				}
1468			}
1469			break;
1470		case ILBD_SCF_HC:
1471			if (strncmp(ILBD_PG_NAME_HC, pg_name,
1472			    strlen(ILBD_PG_NAME_HC)) == 0) {
1473				data.hc_data = calloc(1,
1474				    sizeof (ilb_hc_info_t));
1475				if (data.hc_data == NULL) {
1476					ret = ILB_STATUS_ENOMEM;
1477					goto out;
1478				}
1479				ret = ilbd_scf_pg_walk_props(newpg,
1480				    ilbd_scf_load_prop, &data);
1481				if (ret != ILB_STATUS_OK)
1482					goto out;
1483				assert(data.hc_data != NULL);
1484				/* set hc name */
1485				(void) strlcpy(data.hc_data->hci_name,
1486				    &pg_name[strlen(ILBD_PG_NAME_HC)],
1487				    sizeof (data.hc_data->hci_name));
1488				ret = cb(data.hc_data, port, arg2, NULL);
1489				free(data.hc_data);
1490				if (ret != ILB_STATUS_OK)
1491					goto out;
1492			}
1493			break;
1494		}
1495	}
1496
1497out:
1498	if (pg_name != NULL)
1499		free(pg_name);
1500	if (scf_ret < 0)
1501		ret = ilbd_scf_err_to_ilb_err();
1502	scf_pg_destroy(newpg);
1503	scf_iter_destroy(pgiter);
1504	return (ret);
1505}
1506
1507typedef ilb_status_t (*ilbd_scf_walker_fn)(void *, int, struct passwd *,
1508    ucred_t *);
1509
1510ilb_status_t
1511ilbd_walk_rule_pgs(ilb_status_t (*func)(ilb_rule_info_t *, int,
1512    const struct passwd *, ucred_t *), void *arg1, void *arg2)
1513{
1514	scf_instance_t *inst;
1515	scf_handle_t *h;
1516	scf_service_t *svc;
1517	ilb_status_t ret;
1518
1519	ret = ilbd_scf_get_inst(&h, &svc, &inst);
1520	if (ret != ILB_STATUS_OK)
1521		return (ret);
1522
1523	/* get rule prop group, transfer it to ilb_lrule_info_t */
1524	ret = ilbd_scf_instance_walk_pg(inst, ILBD_SCF_RULE,
1525	    (ilbd_scf_walker_fn)func, arg1, arg2);
1526	ilbd_scf_destroy(h, svc, inst, NULL);
1527	return (ret);
1528}
1529
1530ilb_status_t
1531ilbd_walk_sg_pgs(ilb_status_t (*func)(ilb_sg_info_t *, int,
1532    const struct passwd *, ucred_t *), void *arg1, void *arg2)
1533{
1534	scf_instance_t *inst;
1535	scf_handle_t *h;
1536	scf_service_t *svc;
1537	ilb_status_t ret;
1538
1539	ret = ilbd_scf_get_inst(&h, &svc, &inst);
1540	if (ret != ILB_STATUS_OK)
1541		return (ret);
1542
1543	ret = ilbd_scf_instance_walk_pg(inst, ILBD_SCF_SG,
1544	    (ilbd_scf_walker_fn)func, arg1, arg2);
1545	ilbd_scf_destroy(h, svc, inst, NULL);
1546	return (ret);
1547}
1548
1549ilb_status_t
1550ilbd_walk_hc_pgs(ilb_status_t (*func)(const ilb_hc_info_t *, int,
1551    const struct passwd *, ucred_t *), void *arg1, void *arg2)
1552{
1553	scf_instance_t *inst;
1554	scf_handle_t *h;
1555	scf_service_t *svc;
1556	ilb_status_t ret;
1557
1558	ret = ilbd_scf_get_inst(&h, &svc, &inst);
1559	if (ret != ILB_STATUS_OK)
1560		return (ret);
1561
1562	ret = ilbd_scf_instance_walk_pg(inst, ILBD_SCF_HC,
1563	    (ilbd_scf_walker_fn)func, arg1, arg2);
1564	ilbd_scf_destroy(h, svc, inst, NULL);
1565	return (ret);
1566}
1567
1568ilb_status_t
1569ilbd_change_prop(ilbd_scf_pg_type_t pg_type, const char *pg_name,
1570    const char *prop_name, void *new_val)
1571{
1572	int ret;
1573	scf_propertygroup_t *scfpg = NULL;
1574	char *scf_pgname = NULL;
1575	scf_type_t scftype;
1576	scf_value_t *scfval;
1577	scf_handle_t *h;
1578
1579	if ((scf_pgname = malloc(ILBD_MAX_NAME_LEN)) == NULL)
1580		return (ILB_STATUS_ENOMEM);
1581	ilbd_name_to_scfpgname(pg_type, pg_name, scf_pgname);
1582	ret = ilbd_scf_retrieve_pg(scf_pgname, &scfpg, B_FALSE);
1583	free(scf_pgname);
1584
1585	if (ret != ILB_STATUS_EEXIST)
1586		return (ret);
1587
1588	assert(scfpg != NULL);
1589
1590	h = scf_pg_handle(scfpg);
1591	if (h == NULL) {
1592		ret = ILB_STATUS_EINVAL;
1593		goto done;
1594	}
1595
1596	if ((scfval = scf_value_create(h)) == NULL) {
1597		ret = ILB_STATUS_ENOMEM;
1598		goto done;
1599	}
1600
1601	if (pg_type == ILBD_SCF_RULE) {
1602		scftype = SCF_TYPE_BOOLEAN;
1603		scf_value_set_boolean(scfval, *(boolean_t *)new_val);
1604	} else if (pg_type == ILBD_SCF_SG) {
1605		scftype = SCF_TYPE_ASTRING;
1606		(void) scf_value_set_astring(scfval, (char *)new_val);
1607	}
1608	ret = ilbd_scf_set_prop(scfpg, prop_name, scftype, scfval);
1609
1610done:
1611	if (scf_pg_handle(scfpg) != NULL)
1612		scf_handle_destroy(scf_pg_handle(scfpg));
1613	if (scfpg != NULL)
1614		scf_pg_destroy(scfpg);
1615	if (scfval != NULL)
1616		scf_value_destroy(scfval);
1617	return (ret);
1618}
1619
1620/*
1621 * Update the persistent configuration with a new server, srv, added to a
1622 * server group, sg.
1623 */
1624ilb_status_t
1625ilbd_scf_add_srv(ilbd_sg_t *sg, ilbd_srv_t *srv)
1626{
1627	scf_propertygroup_t *pg;
1628	scf_handle_t *h;
1629	scf_value_t *val;
1630	ilb_status_t ret;
1631	int scf_name_len = ILBD_MAX_NAME_LEN;
1632	char *buf = NULL;
1633
1634	if ((buf = malloc(scf_name_len)) == NULL)
1635		return (ILB_STATUS_ENOMEM);
1636
1637	ilbd_name_to_scfpgname(ILBD_SCF_SG, sg->isg_name, buf);
1638	ret = ilbd_scf_retrieve_pg(buf, &pg, B_FALSE);
1639	/*
1640	 * The server group does not exist in persistent storage.  This
1641	 * cannot happen.  Should probably transition the service to
1642	 * maintenance since it should be there.
1643	 */
1644	if (ret != ILB_STATUS_EEXIST) {
1645		logerr("ilbd_scf_add_srv: SCF update failed - entering"
1646		    " maintenance mode");
1647		(void) smf_maintain_instance(ILB_FMRI, SMF_IMMEDIATE);
1648		free(buf);
1649		return (ILB_STATUS_INTERNAL);
1650	}
1651
1652	if ((h = scf_pg_handle(pg)) == NULL) {
1653		ilbd_scf_destroy(NULL, NULL, NULL, pg);
1654		free(buf);
1655		return (ilbd_scf_err_to_ilb_err());
1656	}
1657
1658	if ((val = scf_value_create(h)) == NULL) {
1659		ilbd_scf_destroy(h, NULL, NULL, pg);
1660		free(buf);
1661		return (ILB_STATUS_ENOMEM);
1662	}
1663	ilbd_srv_scf_val(srv, buf);
1664	(void) scf_value_set_astring(val, buf);
1665
1666	(void) snprintf(buf, scf_name_len, "server%d", srv->isv_id);
1667	ret = ilbd_scf_set_prop(pg, buf, SCF_TYPE_ASTRING, val);
1668	free(buf);
1669	ilbd_scf_destroy(h, NULL, NULL, pg);
1670	scf_value_destroy(val);
1671
1672	return (ret);
1673}
1674
1675/*
1676 * Delete a server, srv, of a server group, sg, from the persistent
1677 * configuration.
1678 */
1679ilb_status_t
1680ilbd_scf_del_srv(ilbd_sg_t *sg, ilbd_srv_t *srv)
1681{
1682	ilb_status_t ret;
1683	scf_propertygroup_t *pg;
1684	scf_handle_t *h;
1685	int scf_name_len = ILBD_MAX_NAME_LEN;
1686	char *buf;
1687	scf_transaction_t *tx = NULL;
1688	scf_transaction_entry_t *entry = NULL;
1689
1690	if ((buf = malloc(scf_name_len)) == NULL)
1691		return (ILB_STATUS_ENOMEM);
1692	ilbd_name_to_scfpgname(ILBD_SCF_SG, sg->isg_name, buf);
1693	ret = ilbd_scf_retrieve_pg(buf, &pg, B_FALSE);
1694	/*
1695	 * The server group does not exist in persistent storage.  This
1696	 * cannot happen. THe caller of this function puts service in
1697	 * maintenance mode.
1698	 */
1699	if (ret != ILB_STATUS_EEXIST) {
1700		free(buf);
1701		return (ILB_STATUS_INTERNAL);
1702	}
1703	ret = ILB_STATUS_OK;
1704
1705	if ((h = scf_pg_handle(pg)) == NULL) {
1706		logdebug("ilbd_scf_del_srv: scf_pg_handle: %s\n",
1707		    scf_strerror(scf_error()));
1708		ilbd_scf_destroy(NULL, NULL, NULL, pg);
1709		free(buf);
1710		return (ilbd_scf_err_to_ilb_err());
1711	}
1712
1713	if ((tx = scf_transaction_create(h)) == NULL ||
1714	    (entry = scf_entry_create(h)) == NULL) {
1715		logdebug("ilbd_scf_del_srv: create scf transaction failed: "
1716		    "%s\n", scf_strerror(scf_error()));
1717		ret = ilbd_scf_err_to_ilb_err();
1718		goto out;
1719	}
1720
1721	(void) snprintf(buf, scf_name_len, "server%d", srv->isv_id);
1722
1723	if (scf_transaction_start(tx, pg) == -1) {
1724		logdebug("ilbd_scf_set_prop: start scf transaction failed: "
1725		    "%s\n", scf_strerror(scf_error()));
1726		ret = ilbd_scf_err_to_ilb_err();
1727		goto out;
1728	}
1729	if (scf_transaction_property_delete(tx, entry, buf) == -1) {
1730		logdebug("ilbd_scf_set_prop: delete property failed: %s\n",
1731		    scf_strerror(scf_error()));
1732		ret = ilbd_scf_err_to_ilb_err();
1733		goto out;
1734	}
1735	if (scf_transaction_commit(tx) != 1) {
1736		logdebug("ilbd_scf_set_prop: commit transaction failed: %s\n",
1737		    scf_strerror(scf_error()));
1738		ret = ilbd_scf_err_to_ilb_err();
1739	}
1740
1741out:
1742	free(buf);
1743	if (entry != NULL)
1744		scf_entry_destroy(entry);
1745	if (tx != NULL)
1746		scf_transaction_destroy(tx);
1747	ilbd_scf_destroy(h, NULL, NULL, pg);
1748
1749	return (ret);
1750}
1751