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