xref: /illumos-gate/usr/src/cmd/cmd-inet/usr.lib/ilbd/ilbd_scf.c (revision e5803b76927480e8f9b67b22201c484ccf4c2bcf)
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 
46 typedef 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 
75 typedef 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  */
87 prop_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 
124 static ilb_status_t ilbd_scf_set_prop(scf_propertygroup_t *, const char *,
125     scf_type_t, scf_value_t *);
126 static ilb_status_t ilbd_scf_retrieve_pg(const char *, scf_propertygroup_t **,
127     boolean_t);
128 static ilb_status_t ilbd_scf_delete_pg(scf_propertygroup_t *);
129 static 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 
134 int
135 ilbd_scf_limit(int type)
136 {
137 	return (MIN(scf_limit(type), 120));
138 }
139 
140 /*
141  * Translate libscf error to libilb status
142  */
143 ilb_status_t
144 ilbd_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 
177 static void
178 ilbd_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 
204 static void
205 ilbd_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 
219 static ilb_status_t
220 ilbd_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  */
259 static ilb_status_t
260 ilbd_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 
315 struct 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 
328 void
329 ilbd_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 
343 static void
344 ilbd_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 
357 struct 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 
369 void
370 ilbd_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 
384 static void
385 ilbd_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 
398 static void
399 ilbd_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) == NULL) {
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  */
448 static void
449 ilbd_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 */
471 ilb_status_t
472 ilbd_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 */
497 ilb_status_t
498 ilbd_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  */
526 static ilb_status_t
527 ilbd_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  */
736 ilb_status_t
737 ilbd_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 
819 done:
820 	free(scfpgbuf);
821 	ilbd_scf_destroy(h, NULL, NULL, pg);
822 	return (ret);
823 }
824 
825 /*
826  * destroy a scf property group
827  */
828 static ilb_status_t
829 ilbd_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 */
837 ilb_status_t
838 ilbd_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  */
862 static ilb_status_t
863 ilbd_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 	}
939 out:
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  */
953 static ilb_status_t
954 ilbd_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;
986 out:
987 	if (prop != NULL)
988 		scf_property_destroy(prop);
989 
990 	return (ret);
991 }
992 
993 typedef 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 
1006 void
1007 ilbd_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  */
1027 static ilb_status_t
1028 ilbd_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 
1240 static ilbd_var_type_t
1241 ilbd_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 */
1255 static ilb_status_t
1256 ilbd_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 
1279 out:
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  */
1290 static ilb_status_t
1291 ilbd_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 	}
1329 out:
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 */
1343 static ilb_status_t
1344 ilbd_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 
1497 out:
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 
1507 typedef ilb_status_t (*ilbd_scf_walker_fn)(void *, int, struct passwd *,
1508     ucred_t *);
1509 
1510 ilb_status_t
1511 ilbd_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 
1530 ilb_status_t
1531 ilbd_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 
1549 ilb_status_t
1550 ilbd_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 
1568 ilb_status_t
1569 ilbd_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 
1610 done:
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  */
1624 ilb_status_t
1625 ilbd_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  */
1679 ilb_status_t
1680 ilbd_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 
1741 out:
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