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