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) == 0) { 423 logerr("ilbd_get_svr_field: inet_pton failed"); 424 return; 425 } 426 427 if (ip_ver == AF_INET) { 428 IN6_INADDR_TO_V4MAPPED(&(temp_ip.ia_v4), sgs_addr); 429 } else { 430 (void) memcpy(sgs_addr, &(temp_ip.ia_v6), 431 sizeof (struct in6_addr)); 432 } 433 434 *sgs_flags = atoi(flagstr); 435 *min_port = atoi(strtok(portstr, "-")); 436 *min_port = ntohs(*min_port); 437 max_portstr = strtok(NULL, "-"); 438 if (max_portstr != NULL) { 439 *max_port = atoi(max_portstr); 440 *max_port = ntohs(*max_port); 441 } 442 } 443 444 /* 445 * Convert the info of a server to its SCF string value representation. 446 * Argument value is assumed to be of size ILBD_MAX_VALUE_LEN. 447 */ 448 static void 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 if (val != NULL) 1280 scf_value_destroy(val); 1281 1282 return (ret); 1283 } 1284 1285 /* 1286 * walk properties in one prop group, arg is ilbd_data 1287 * cb is ilbd_scf_load_prop() 1288 */ 1289 static ilb_status_t 1290 ilbd_scf_pg_walk_props(scf_propertygroup_t *pg, 1291 ilb_status_t (*cb)(scf_propertygroup_t *, const char *, void *), 1292 void *arg) 1293 { 1294 scf_handle_t *h; 1295 scf_iter_t *propiter; 1296 scf_property_t *prop; 1297 int scf_name_len = ILBD_MAX_NAME_LEN; 1298 char *prop_name = NULL; 1299 ilb_status_t ret = ILB_STATUS_OK; 1300 int scf_ret = -1; 1301 1302 h = scf_pg_handle(pg); 1303 if (h == NULL) 1304 return (ILB_STATUS_EINVAL); 1305 1306 prop = scf_property_create(h); 1307 propiter = scf_iter_create(h); 1308 if (prop == NULL || propiter == NULL) 1309 goto out; 1310 1311 if (scf_iter_pg_properties(propiter, pg) != 0) 1312 goto out; 1313 1314 if ((prop_name = malloc(scf_name_len)) == NULL) { 1315 ret = ILB_STATUS_ENOMEM; 1316 goto out; 1317 } 1318 while ((scf_ret = scf_iter_next_property(propiter, prop)) == 1) { 1319 if (scf_property_get_name(prop, prop_name, scf_name_len) 1320 < 0) { 1321 ret = ilbd_scf_err_to_ilb_err(); 1322 goto out; 1323 } 1324 ret = cb(pg, prop_name, arg); 1325 if (ret != ILB_STATUS_OK) 1326 break; 1327 } 1328 out: 1329 if (prop_name != NULL) 1330 free(prop_name); 1331 if (scf_ret == -1) 1332 ret = ilbd_scf_err_to_ilb_err(); 1333 if (prop != NULL) 1334 scf_property_destroy(prop); 1335 if (propiter != NULL) 1336 scf_iter_destroy(propiter); 1337 1338 return (ret); 1339 } 1340 1341 /* cbs are libd_create_X */ 1342 static ilb_status_t 1343 ilbd_scf_instance_walk_pg(scf_instance_t *inst, 1344 ilbd_scf_pg_type_t pg_type, 1345 ilb_status_t (*cb)(void *, int, struct passwd *, ucred_t *), 1346 void *arg1, void *arg2) 1347 { 1348 int scf_ret; 1349 ilb_status_t ret; 1350 scf_handle_t *h; 1351 scf_iter_t *pgiter; 1352 scf_propertygroup_t *newpg; 1353 int port = *((int *)arg1); 1354 int scf_name_len = ILBD_MAX_NAME_LEN; 1355 char *pg_name = NULL; 1356 1357 if (inst == NULL) 1358 return (ILB_STATUS_EINVAL); 1359 1360 h = scf_instance_handle(inst); 1361 if (h == NULL) 1362 return (ILB_STATUS_EINVAL); 1363 1364 if ((newpg = scf_pg_create(h)) == NULL) 1365 return (ilbd_scf_err_to_ilb_err()); 1366 1367 if ((pgiter = scf_iter_create(h)) == NULL) { 1368 scf_pg_destroy(newpg); 1369 return (ilbd_scf_err_to_ilb_err()); 1370 } 1371 1372 if ((scf_ret = scf_iter_instance_pgs(pgiter, inst)) < 0) 1373 goto out; 1374 1375 if ((pg_name = malloc(scf_name_len)) == NULL) { 1376 ret = ILB_STATUS_ENOMEM; 1377 goto out; 1378 } 1379 while ((scf_ret = scf_iter_next_pg(pgiter, newpg)) > 0) { 1380 ilbd_data_t data; 1381 1382 if (scf_pg_get_name(newpg, pg_name, scf_name_len) < 0) { 1383 ret = ilbd_scf_err_to_ilb_err(); 1384 goto out; 1385 } 1386 1387 /* 1388 * if pg name indicates it's a ilb configuration, walk its prop 1389 */ 1390 data.pg_type = pg_type; 1391 data.hc_data = NULL; 1392 data.sg_data = NULL; 1393 data.rule_data = NULL; 1394 1395 switch (pg_type) { 1396 case ILBD_SCF_RULE: 1397 if (strncmp(ILBD_PG_NAME_RULE, pg_name, 1398 strlen(ILBD_PG_NAME_RULE)) == 0) { 1399 data.rule_data = calloc(1, 1400 sizeof (ilb_rule_info_t)); 1401 if (data.rule_data == NULL) { 1402 ret = ILB_STATUS_ENOMEM; 1403 goto out; 1404 } 1405 ret = ilbd_scf_pg_walk_props(newpg, 1406 ilbd_scf_load_prop, &data); 1407 if (ret != ILB_STATUS_OK) 1408 goto out; 1409 assert(data.rule_data != NULL); 1410 /* set rule name */ 1411 (void) strlcpy(data.rule_data->rl_name, 1412 &pg_name[strlen(ILBD_PG_NAME_RULE)], 1413 sizeof (data.rule_data->rl_name)); 1414 1415 ret = cb(data.rule_data, port, arg2, NULL); 1416 free(data.rule_data); 1417 if (ret != ILB_STATUS_OK) 1418 goto out; 1419 } 1420 break; 1421 case ILBD_SCF_SG: 1422 if (strncmp(ILBD_PG_NAME_SG, pg_name, 1423 strlen(ILBD_PG_NAME_SG)) == 0) { 1424 data.sg_data = calloc(1, 1425 sizeof (ilb_sg_info_t)); 1426 if (data.sg_data == NULL) { 1427 ret = ILB_STATUS_ENOMEM; 1428 goto out; 1429 } 1430 ret = ilbd_scf_pg_walk_props(newpg, 1431 ilbd_scf_load_prop, &data); 1432 if (ret != ILB_STATUS_OK) { 1433 free(data.sg_data); 1434 goto out; 1435 } 1436 assert(data.sg_data != NULL); 1437 /* set sg name */ 1438 (void) strlcpy(data.sg_data->sg_name, 1439 &pg_name[strlen(ILBD_PG_NAME_SG)], 1440 sizeof (data.sg_data->sg_name)); 1441 ret = cb(data.sg_data, port, arg2, NULL); 1442 if (ret != ILB_STATUS_OK) { 1443 free(data.sg_data); 1444 goto out; 1445 } 1446 /* 1447 * create a servergroup is two-step operation. 1448 * 1. create an empty servergroup. 1449 * 2. add server(s) to the group. 1450 * 1451 * since we are here from: 1452 * main_loop()->ilbd_read_config()-> 1453 * ilbd_walk_sg_pgs() 1454 * there is no cli to send. So in this 1455 * path auditing will skip the 1456 * adt_set_from_ucred() check 1457 */ 1458 if (data.sg_data->sg_srvcount > 0) { 1459 ret = ilbd_add_server_to_group( 1460 data.sg_data, port, NULL, NULL); 1461 if (ret != ILB_STATUS_OK) { 1462 free(data.sg_data); 1463 goto out; 1464 } 1465 free(data.sg_data); 1466 } 1467 } 1468 break; 1469 case ILBD_SCF_HC: 1470 if (strncmp(ILBD_PG_NAME_HC, pg_name, 1471 strlen(ILBD_PG_NAME_HC)) == 0) { 1472 data.hc_data = calloc(1, 1473 sizeof (ilb_hc_info_t)); 1474 if (data.hc_data == NULL) { 1475 ret = ILB_STATUS_ENOMEM; 1476 goto out; 1477 } 1478 ret = ilbd_scf_pg_walk_props(newpg, 1479 ilbd_scf_load_prop, &data); 1480 if (ret != ILB_STATUS_OK) 1481 goto out; 1482 assert(data.hc_data != NULL); 1483 /* set hc name */ 1484 (void) strlcpy(data.hc_data->hci_name, 1485 &pg_name[strlen(ILBD_PG_NAME_HC)], 1486 sizeof (data.hc_data->hci_name)); 1487 ret = cb(data.hc_data, port, arg2, NULL); 1488 free(data.hc_data); 1489 if (ret != ILB_STATUS_OK) 1490 goto out; 1491 } 1492 break; 1493 } 1494 } 1495 1496 out: 1497 if (pg_name != NULL) 1498 free(pg_name); 1499 if (scf_ret < 0) 1500 ret = ilbd_scf_err_to_ilb_err(); 1501 scf_pg_destroy(newpg); 1502 scf_iter_destroy(pgiter); 1503 return (ret); 1504 } 1505 1506 typedef ilb_status_t (*ilbd_scf_walker_fn)(void *, int, struct passwd *, 1507 ucred_t *); 1508 1509 ilb_status_t 1510 ilbd_walk_rule_pgs(ilb_status_t (*func)(ilb_rule_info_t *, int, 1511 const struct passwd *, ucred_t *), void *arg1, void *arg2) 1512 { 1513 scf_instance_t *inst; 1514 scf_handle_t *h; 1515 scf_service_t *svc; 1516 ilb_status_t ret; 1517 1518 ret = ilbd_scf_get_inst(&h, &svc, &inst); 1519 if (ret != ILB_STATUS_OK) 1520 return (ret); 1521 1522 /* get rule prop group, transfer it to ilb_lrule_info_t */ 1523 ret = ilbd_scf_instance_walk_pg(inst, ILBD_SCF_RULE, 1524 (ilbd_scf_walker_fn)func, arg1, arg2); 1525 ilbd_scf_destroy(h, svc, inst, NULL); 1526 return (ret); 1527 } 1528 1529 ilb_status_t 1530 ilbd_walk_sg_pgs(ilb_status_t (*func)(ilb_sg_info_t *, int, 1531 const struct passwd *, ucred_t *), void *arg1, void *arg2) 1532 { 1533 scf_instance_t *inst; 1534 scf_handle_t *h; 1535 scf_service_t *svc; 1536 ilb_status_t ret; 1537 1538 ret = ilbd_scf_get_inst(&h, &svc, &inst); 1539 if (ret != ILB_STATUS_OK) 1540 return (ret); 1541 1542 ret = ilbd_scf_instance_walk_pg(inst, ILBD_SCF_SG, 1543 (ilbd_scf_walker_fn)func, arg1, arg2); 1544 ilbd_scf_destroy(h, svc, inst, NULL); 1545 return (ret); 1546 } 1547 1548 ilb_status_t 1549 ilbd_walk_hc_pgs(ilb_status_t (*func)(const ilb_hc_info_t *, int, 1550 const struct passwd *, ucred_t *), void *arg1, void *arg2) 1551 { 1552 scf_instance_t *inst; 1553 scf_handle_t *h; 1554 scf_service_t *svc; 1555 ilb_status_t ret; 1556 1557 ret = ilbd_scf_get_inst(&h, &svc, &inst); 1558 if (ret != ILB_STATUS_OK) 1559 return (ret); 1560 1561 ret = ilbd_scf_instance_walk_pg(inst, ILBD_SCF_HC, 1562 (ilbd_scf_walker_fn)func, arg1, arg2); 1563 ilbd_scf_destroy(h, svc, inst, NULL); 1564 return (ret); 1565 } 1566 1567 ilb_status_t 1568 ilbd_change_prop(ilbd_scf_pg_type_t pg_type, const char *pg_name, 1569 const char *prop_name, void *new_val) 1570 { 1571 int ret; 1572 scf_propertygroup_t *scfpg = NULL; 1573 char *scf_pgname = NULL; 1574 scf_type_t scftype; 1575 scf_value_t *scfval; 1576 scf_handle_t *h; 1577 1578 if ((scf_pgname = malloc(ILBD_MAX_NAME_LEN)) == NULL) 1579 return (ILB_STATUS_ENOMEM); 1580 ilbd_name_to_scfpgname(pg_type, pg_name, scf_pgname); 1581 ret = ilbd_scf_retrieve_pg(scf_pgname, &scfpg, B_FALSE); 1582 free(scf_pgname); 1583 1584 if (ret != ILB_STATUS_EEXIST) 1585 return (ret); 1586 1587 assert(scfpg != NULL); 1588 1589 h = scf_pg_handle(scfpg); 1590 if (h == NULL) { 1591 ret = ILB_STATUS_EINVAL; 1592 goto done; 1593 } 1594 1595 if ((scfval = scf_value_create(h)) == NULL) { 1596 ret = ILB_STATUS_ENOMEM; 1597 goto done; 1598 } 1599 1600 if (pg_type == ILBD_SCF_RULE) { 1601 scftype = SCF_TYPE_BOOLEAN; 1602 scf_value_set_boolean(scfval, *(boolean_t *)new_val); 1603 } else if (pg_type == ILBD_SCF_SG) { 1604 scftype = SCF_TYPE_ASTRING; 1605 (void) scf_value_set_astring(scfval, (char *)new_val); 1606 } 1607 ret = ilbd_scf_set_prop(scfpg, prop_name, scftype, scfval); 1608 1609 done: 1610 if (scf_pg_handle(scfpg) != NULL) 1611 scf_handle_destroy(scf_pg_handle(scfpg)); 1612 if (scfpg != NULL) 1613 scf_pg_destroy(scfpg); 1614 if (scfval != NULL) 1615 scf_value_destroy(scfval); 1616 return (ret); 1617 } 1618 1619 /* 1620 * Update the persistent configuration with a new server, srv, added to a 1621 * server group, sg. 1622 */ 1623 ilb_status_t 1624 ilbd_scf_add_srv(ilbd_sg_t *sg, ilbd_srv_t *srv) 1625 { 1626 scf_propertygroup_t *pg; 1627 scf_handle_t *h; 1628 scf_value_t *val; 1629 ilb_status_t ret; 1630 int scf_name_len = ILBD_MAX_NAME_LEN; 1631 char *buf = NULL; 1632 1633 if ((buf = malloc(scf_name_len)) == NULL) 1634 return (ILB_STATUS_ENOMEM); 1635 1636 ilbd_name_to_scfpgname(ILBD_SCF_SG, sg->isg_name, buf); 1637 ret = ilbd_scf_retrieve_pg(buf, &pg, B_FALSE); 1638 /* 1639 * The server group does not exist in persistent storage. This 1640 * cannot happen. Should probably transition the service to 1641 * maintenance since it should be there. 1642 */ 1643 if (ret != ILB_STATUS_EEXIST) { 1644 logerr("ilbd_scf_add_srv: SCF update failed - entering" 1645 " maintenance mode"); 1646 (void) smf_maintain_instance(ILB_FMRI, SMF_IMMEDIATE); 1647 free(buf); 1648 return (ILB_STATUS_INTERNAL); 1649 } 1650 1651 if ((h = scf_pg_handle(pg)) == NULL) { 1652 ilbd_scf_destroy(NULL, NULL, NULL, pg); 1653 free(buf); 1654 return (ilbd_scf_err_to_ilb_err()); 1655 } 1656 1657 if ((val = scf_value_create(h)) == NULL) { 1658 ilbd_scf_destroy(h, NULL, NULL, pg); 1659 free(buf); 1660 return (ILB_STATUS_ENOMEM); 1661 } 1662 ilbd_srv_scf_val(srv, buf); 1663 (void) scf_value_set_astring(val, buf); 1664 1665 (void) snprintf(buf, scf_name_len, "server%d", srv->isv_id); 1666 ret = ilbd_scf_set_prop(pg, buf, SCF_TYPE_ASTRING, val); 1667 free(buf); 1668 ilbd_scf_destroy(h, NULL, NULL, pg); 1669 scf_value_destroy(val); 1670 1671 return (ret); 1672 } 1673 1674 /* 1675 * Delete a server, srv, of a server group, sg, from the persistent 1676 * configuration. 1677 */ 1678 ilb_status_t 1679 ilbd_scf_del_srv(ilbd_sg_t *sg, ilbd_srv_t *srv) 1680 { 1681 ilb_status_t ret; 1682 scf_propertygroup_t *pg; 1683 scf_handle_t *h; 1684 int scf_name_len = ILBD_MAX_NAME_LEN; 1685 char *buf; 1686 scf_transaction_t *tx = NULL; 1687 scf_transaction_entry_t *entry = NULL; 1688 1689 if ((buf = malloc(scf_name_len)) == NULL) 1690 return (ILB_STATUS_ENOMEM); 1691 ilbd_name_to_scfpgname(ILBD_SCF_SG, sg->isg_name, buf); 1692 ret = ilbd_scf_retrieve_pg(buf, &pg, B_FALSE); 1693 /* 1694 * The server group does not exist in persistent storage. This 1695 * cannot happen. THe caller of this function puts service in 1696 * maintenance mode. 1697 */ 1698 if (ret != ILB_STATUS_EEXIST) { 1699 free(buf); 1700 return (ILB_STATUS_INTERNAL); 1701 } 1702 ret = ILB_STATUS_OK; 1703 1704 if ((h = scf_pg_handle(pg)) == NULL) { 1705 logdebug("ilbd_scf_del_srv: scf_pg_handle: %s\n", 1706 scf_strerror(scf_error())); 1707 ilbd_scf_destroy(NULL, NULL, NULL, pg); 1708 free(buf); 1709 return (ilbd_scf_err_to_ilb_err()); 1710 } 1711 1712 if ((tx = scf_transaction_create(h)) == NULL || 1713 (entry = scf_entry_create(h)) == NULL) { 1714 logdebug("ilbd_scf_del_srv: create scf transaction failed: " 1715 "%s\n", scf_strerror(scf_error())); 1716 ret = ilbd_scf_err_to_ilb_err(); 1717 goto out; 1718 } 1719 1720 (void) snprintf(buf, scf_name_len, "server%d", srv->isv_id); 1721 1722 if (scf_transaction_start(tx, pg) == -1) { 1723 logdebug("ilbd_scf_set_prop: start scf transaction failed: " 1724 "%s\n", scf_strerror(scf_error())); 1725 ret = ilbd_scf_err_to_ilb_err(); 1726 goto out; 1727 } 1728 if (scf_transaction_property_delete(tx, entry, buf) == -1) { 1729 logdebug("ilbd_scf_set_prop: delete property failed: %s\n", 1730 scf_strerror(scf_error())); 1731 ret = ilbd_scf_err_to_ilb_err(); 1732 goto out; 1733 } 1734 if (scf_transaction_commit(tx) != 1) { 1735 logdebug("ilbd_scf_set_prop: commit transaction failed: %s\n", 1736 scf_strerror(scf_error())); 1737 ret = ilbd_scf_err_to_ilb_err(); 1738 } 1739 1740 out: 1741 free(buf); 1742 if (entry != NULL) 1743 scf_entry_destroy(entry); 1744 if (tx != NULL) 1745 scf_transaction_destroy(tx); 1746 ilbd_scf_destroy(h, NULL, NULL, pg); 1747 1748 return (ret); 1749 } 1750