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