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
ilbd_scf_limit(int type)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
ilbd_scf_err_to_ilb_err()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
ilbd_name_to_scfpgname(ilbd_scf_pg_type_t pg_type,const char * pgname,char * scf_pgname)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
ilbd_scf_destroy(scf_handle_t * h,scf_service_t * s,scf_instance_t * inst,scf_propertygroup_t * pg)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
ilbd_scf_get_inst(scf_handle_t ** h,scf_service_t ** svc,scf_instance_t ** inst)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
ilbd_scf_retrieve_pg(const char * pgname,scf_propertygroup_t ** pg,boolean_t create)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
ilbd_algo_to_str(ilb_algo_t algo_type,char * valstr)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
ilbd_scf_str_to_algo(ilb_algo_t * algo_type,char * valstr)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
ilbd_topo_to_str(ilb_topo_t topo_type,char * valstr)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
ilbd_scf_str_to_topo(ilb_topo_t * topo_type,char * valstr)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
ilbd_get_svr_field(char * valstr,struct in6_addr * sgs_addr,int32_t * min_port,int32_t * max_port,int32_t * sgs_flags)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) == 0) {
423 logerr("ilbd_get_svr_field: inet_pton failed");
424 return;
425 }
426
427 if (ip_ver == AF_INET) {
428 IN6_INADDR_TO_V4MAPPED(&(temp_ip.ia_v4), sgs_addr);
429 } else {
430 (void) memcpy(sgs_addr, &(temp_ip.ia_v6),
431 sizeof (struct in6_addr));
432 }
433
434 *sgs_flags = atoi(flagstr);
435 *min_port = atoi(strtok(portstr, "-"));
436 *min_port = ntohs(*min_port);
437 max_portstr = strtok(NULL, "-");
438 if (max_portstr != NULL) {
439 *max_port = atoi(max_portstr);
440 *max_port = ntohs(*max_port);
441 }
442 }
443
444 /*
445 * Convert the info of a server to its SCF string value representation.
446 * Argument value is assumed to be of size ILBD_MAX_VALUE_LEN.
447 */
448 static void
ilbd_srv_scf_val(ilbd_srv_t * srv,char * value)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
ilbd_get_svr_info(ilbd_sg_t * sg,int num,char * valstr,char * svrname)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
ilbd_scf_ip_to_str(uint16_t ipversion,struct in6_addr * addr,scf_type_t * scftype,char * valstr)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
ilbd_data_to_scfval(ilbd_scf_pg_type_t pg_type,ilbd_var_type_t type,scf_handle_t * h,void * data,scf_value_t *** val,scf_type_t * scftype,int * numval)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
ilbd_create_pg(ilbd_scf_pg_type_t pg_type,void * data)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
ilbd_scf_delete_pg(scf_propertygroup_t * pg)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
ilbd_destroy_pg(ilbd_scf_pg_type_t pg_t,const char * pgname)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
ilbd_scf_set_prop(scf_propertygroup_t * pg,const char * propname,scf_type_t proptype,scf_value_t * val)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
ilbd_scf_get_prop_val(scf_propertygroup_t * pg,const char * propname,scf_value_t ** val)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
ilbd_scf_str_to_ip(int ipversion,char * ipstr,struct in6_addr * addr)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
ilbd_scfval_to_data(const char * propname,ilbd_var_type_t ilb_type,scf_value_t * val,ilbd_data_t * ilb_data)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
ilbd_name_to_valtype(const char * prop_name)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
ilbd_scf_load_prop(scf_propertygroup_t * pg,const char * prop_name,void * arg)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
ilbd_scf_pg_walk_props(scf_propertygroup_t * pg,ilb_status_t (* cb)(scf_propertygroup_t *,const char *,void *),void * arg)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
ilbd_scf_instance_walk_pg(scf_instance_t * inst,ilbd_scf_pg_type_t pg_type,ilb_status_t (* cb)(void *,int,struct passwd *,ucred_t *),void * arg1,void * arg2)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
ilbd_walk_rule_pgs(ilb_status_t (* func)(ilb_rule_info_t *,int,const struct passwd *,ucred_t *),void * arg1,void * arg2)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
ilbd_walk_sg_pgs(ilb_status_t (* func)(ilb_sg_info_t *,int,const struct passwd *,ucred_t *),void * arg1,void * arg2)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
ilbd_walk_hc_pgs(ilb_status_t (* func)(const ilb_hc_info_t *,int,const struct passwd *,ucred_t *),void * arg1,void * arg2)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
ilbd_change_prop(ilbd_scf_pg_type_t pg_type,const char * pg_name,const char * prop_name,void * new_val)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
ilbd_scf_add_srv(ilbd_sg_t * sg,ilbd_srv_t * srv)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
ilbd_scf_del_srv(ilbd_sg_t * sg,ilbd_srv_t * srv)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