/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
*/
#include <stdlib.h>
#include <stdio.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/list.h>
#include <libilb.h>
#include <assert.h>
#include <libscf.h>
#include "libilb_impl.h"
#include "ilbd.h"
#define ILBD_PG_NAME_RULE "rule_"
#define ILBD_PG_NAME_SG "sg_"
#define ILBD_PG_NAME_HC "hc_"
#define ILBD_SVC_FMRI "svc:/network/loadbalancer/ilb"
#define ILBD_INST_NAME "default"
typedef enum {
ILBD_RULE_STATUS,
ILBD_RULE_VIP,
ILBD_RULE_PROTO,
ILBD_RULE_PORT,
ILBD_RULE_ALGO,
ILBD_RULE_TOPO,
ILBD_RULE_NAT_STR,
ILBD_RULE_NAT_END,
ILBD_RULE_STI_MASK,
ILBD_RULE_SGNAME,
ILBD_RULE_HCNAME,
ILBD_RULE_HCPORT,
ILBD_RULE_HCPFLAG,
ILBD_RULE_DRAINTIME,
ILBD_RULE_NAT_TO,
ILBD_RULE_PERS_TO,
ILBD_SG_SERVER,
ILBD_HC_TEST,
ILBD_HC_TIMEOUT,
ILBD_HC_INTERVAL,
ILBD_HC_DEF_PING,
ILBD_HC_COUNT,
ILBD_VAR_INVALID
} ilbd_var_type_t;
typedef struct prop_tbl_entry {
ilbd_var_type_t val_type;
const char *scf_propname;
scf_type_t scf_proptype;
} prop_tbl_entry_t;
/*
* this table contains a map of all SCF properties, including rules,
* servergroups and health checks. The place to add new property needs to be
* watched carefully. When new properties are added, corresponding *VAR_NUM
* needs to be adjusted to reflect the correct index of the table
*/
prop_tbl_entry_t prop_tbl[] = {
/* entried for rule */
{ILBD_RULE_STATUS, "status", SCF_TYPE_BOOLEAN},
/* SCF_TYPE_NET_ADDR_V4 or SCF_TYPE_NET_ADDR_V6 */
{ILBD_RULE_VIP, "vip", SCF_TYPE_INVALID},
{ILBD_RULE_PROTO, "protocol", SCF_TYPE_ASTRING},
{ILBD_RULE_PORT, "port", SCF_TYPE_ASTRING},
{ILBD_RULE_ALGO, "ilb-algo", SCF_TYPE_ASTRING},
{ILBD_RULE_TOPO, "ilb-type", SCF_TYPE_ASTRING},
{ILBD_RULE_NAT_STR, "ilb-nat-start", SCF_TYPE_INVALID},
{ILBD_RULE_NAT_END, "ilb-nat-end", SCF_TYPE_INVALID},
{ILBD_RULE_STI_MASK, "ilb-sti-mask", SCF_TYPE_INVALID},
{ILBD_RULE_SGNAME, "servergroup", SCF_TYPE_ASTRING},
{ILBD_RULE_HCNAME, "healthcheck", SCF_TYPE_ASTRING},
{ILBD_RULE_HCPORT, "hc-port", SCF_TYPE_INTEGER},
{ILBD_RULE_HCPFLAG, "hcp-flag", SCF_TYPE_INTEGER},
{ILBD_RULE_DRAINTIME, "drain-time", SCF_TYPE_INTEGER},
{ILBD_RULE_NAT_TO, "nat-timeout", SCF_TYPE_INTEGER},
{ILBD_RULE_PERS_TO, "pers-timeout", SCF_TYPE_INTEGER},
/* add new rule related prop here */
/* entries for sg */
{ILBD_SG_SERVER, "server", SCF_TYPE_ASTRING},
/* add new sg related prop here */
/* entries for hc */
{ILBD_HC_TEST, "test", SCF_TYPE_ASTRING},
{ILBD_HC_TIMEOUT, "timeout", SCF_TYPE_INTEGER},
{ILBD_HC_INTERVAL, "interval", SCF_TYPE_INTEGER},
{ILBD_HC_DEF_PING, "ping", SCF_TYPE_BOOLEAN},
/* add new hc related prop here */
{ILBD_HC_COUNT, "count", SCF_TYPE_INTEGER}
};
#define ILBD_PROP_VAR_NUM (ILBD_HC_COUNT + 1)
#define ILBD_RULE_VAR_NUM (ILBD_SG_SERVER)
#define ILBD_SG_VAR_NUM (ILBD_HC_TEST - ILBD_SG_SERVER)
#define ILBD_HC_VAR_NUM (ILBD_PROP_VAR_NUM - ILBD_HC_TEST)
static ilb_status_t ilbd_scf_set_prop(scf_propertygroup_t *, const char *,
scf_type_t, scf_value_t *);
static ilb_status_t ilbd_scf_retrieve_pg(const char *, scf_propertygroup_t **,
boolean_t);
static ilb_status_t ilbd_scf_delete_pg(scf_propertygroup_t *);
static ilb_status_t ilbd_scf_get_prop_val(scf_propertygroup_t *, const char *,
scf_value_t **);
#define MIN(a, b) ((a) < (b) ? (a) : (b))
int
ilbd_scf_limit(int type)
{
return (MIN(scf_limit(type), 120));
}
/*
* Translate libscf error to libilb status
*/
ilb_status_t
ilbd_scf_err_to_ilb_err()
{
switch (scf_error()) {
case SCF_ERROR_NONE:
return (ILB_STATUS_OK);
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_HANDLE_DESTROYED:
case SCF_ERROR_VERSION_MISMATCH:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_CONSTRAINT_VIOLATED:
case SCF_ERROR_NOT_SET:
case SCF_ERROR_TYPE_MISMATCH:
case SCF_ERROR_INVALID_ARGUMENT:
return (ILB_STATUS_EINVAL);
case SCF_ERROR_NO_MEMORY:
case SCF_ERROR_NO_RESOURCES:
return (ILB_STATUS_ENOMEM);
case SCF_ERROR_NOT_FOUND:
case SCF_ERROR_DELETED:
return (ILB_STATUS_ENOENT);
case SCF_ERROR_EXISTS:
return (ILB_STATUS_EEXIST);
case SCF_ERROR_PERMISSION_DENIED:
return (ILB_STATUS_PERMIT);
case SCF_ERROR_CALLBACK_FAILED:
return (ILB_STATUS_CALLBACK);
case SCF_ERROR_IN_USE:
return (ILB_STATUS_INUSE);
default:
return (ILB_STATUS_INTERNAL);
}
}
static void
ilbd_name_to_scfpgname(ilbd_scf_pg_type_t pg_type, const char *pgname,
char *scf_pgname)
{
switch (pg_type) {
case ILBD_SCF_RULE:
(void) snprintf(scf_pgname, ILBD_MAX_NAME_LEN,
ILBD_PG_NAME_RULE "%s", pgname);
return;
case ILBD_SCF_SG:
(void) snprintf(scf_pgname, ILBD_MAX_NAME_LEN,
ILBD_PG_NAME_SG "%s", pgname);
return;
case ILBD_SCF_HC:
(void) snprintf(scf_pgname, ILBD_MAX_NAME_LEN,
ILBD_PG_NAME_HC "%s", pgname);
return;
/* Should not happen. Log it and put ILB service in maintenance. */
default:
logerr("ilbd_name_to_scfpgname: invalid pg type %d for pg %s",
pg_type, pgname);
(void) smf_maintain_instance(ILB_FMRI, SMF_IMMEDIATE);
exit(EXIT_FAILURE);
return;
}
}
static void
ilbd_scf_destroy(scf_handle_t *h, scf_service_t *s, scf_instance_t *inst,
scf_propertygroup_t *pg)
{
if (pg != NULL)
scf_pg_destroy(pg);
if (inst != NULL)
scf_instance_destroy(inst);
if (s != NULL)
scf_service_destroy(s);
if (h != NULL)
scf_handle_destroy(h);
}
static ilb_status_t
ilbd_scf_get_inst(scf_handle_t **h, scf_service_t **svc, scf_instance_t **inst)
{
if ((*h = scf_handle_create(SCF_VERSION)) == NULL)
return (ILB_STATUS_INTERNAL);
if (scf_handle_bind(*h) != 0) {
ilbd_scf_destroy(*h, NULL, NULL, NULL);
return (ilbd_scf_err_to_ilb_err());
}
if ((*svc = scf_service_create(*h)) == NULL) {
ilbd_scf_destroy(*h, NULL, NULL, NULL);
return (ilbd_scf_err_to_ilb_err());
}
if (scf_handle_decode_fmri(*h, ILBD_SVC_FMRI, NULL, *svc, NULL, NULL,
NULL, SCF_DECODE_FMRI_EXACT) != 0) {
ilbd_scf_destroy(*h, *svc, NULL, NULL);
return (ilbd_scf_err_to_ilb_err());
}
if ((*inst = scf_instance_create(*h)) == NULL) {
ilbd_scf_destroy(*h, *svc, NULL, NULL);
return (ilbd_scf_err_to_ilb_err());
}
if (scf_service_get_instance(*svc, ILBD_INST_NAME, *inst) != 0) {
ilbd_scf_destroy(*h, *svc, *inst, NULL);
return (ilbd_scf_err_to_ilb_err());
}
return (ILB_STATUS_OK);
}
/*
* If create is set, create a new prop group, destroy the old one if exists.
* If create not set, try to find the prop group with given name.
* The created or found entry is returned as *pg.
* Caller frees *pg and its handle scf_pg_handle(pg)
*/
static ilb_status_t
ilbd_scf_retrieve_pg(const char *pgname, scf_propertygroup_t **pg,
boolean_t create)
{
scf_instance_t *inst;
scf_handle_t *h;
scf_service_t *svc;
ilb_status_t ret;
ret = ilbd_scf_get_inst(&h, &svc, &inst);
if (ret != ILB_STATUS_OK)
return (ret);
*pg = scf_pg_create(h);
if (*pg == NULL)
return (ILB_STATUS_INTERNAL);
if (scf_instance_get_pg(inst, pgname, *pg) != 0) {
if (scf_error() != SCF_ERROR_NOT_FOUND ||
(scf_error() == SCF_ERROR_NOT_FOUND && (!create))) {
ilbd_scf_destroy(h, svc, inst, *pg);
*pg = NULL;
return (ilbd_scf_err_to_ilb_err());
}
} else {
/*
* Found pg, don't want to create, return EEXIST. Note that
* h cannot be destroyed here since the caller needs to use it.
* The caller gets it by calling scf_pg_handle().
*/
if (!create) {
ilbd_scf_destroy(NULL, svc, inst, NULL);
return (ILB_STATUS_EEXIST);
}
/* found pg, need to create, destroy the existing one */
else
(void) ilbd_scf_delete_pg(*pg);
}
if (create) {
if (scf_instance_add_pg(inst, pgname,
SCF_GROUP_APPLICATION, 0, *pg) != 0) {
ilbd_scf_destroy(h, svc, inst, *pg);
*pg = NULL;
return (ilbd_scf_err_to_ilb_err());
}
}
/*
* Note that handle cannot be destroyed here, caller sometimes needs
* to use it. It gets the handle by calling scf_pg_handle().
*/
ilbd_scf_destroy(NULL, svc, inst, NULL);
return (ILB_STATUS_OK);
}
struct algo_tbl_entry {
ilb_algo_t algo_type;
const char *algo_str;
} algo_tbl[] = {
{ILB_ALG_ROUNDROBIN, "ROUNDROBIN"},
{ILB_ALG_HASH_IP, "HASH-IP"},
{ILB_ALG_HASH_IP_SPORT, "HASH-IP-PORT"},
{ILB_ALG_HASH_IP_VIP, "HASH-IP-VIP"}
};
#define ILBD_ALGO_TBL_SIZE (sizeof (algo_tbl) / \
sizeof (*algo_tbl))
void
ilbd_algo_to_str(ilb_algo_t algo_type, char *valstr)
{
int i;
for (i = 0; i < ILBD_ALGO_TBL_SIZE; i++) {
if (algo_type == algo_tbl[i].algo_type) {
(void) strlcpy(valstr, algo_tbl[i].algo_str,
ILBD_MAX_VALUE_LEN);
return;
}
}
logerr("ilbd_algo_to_str: algo not found");
}
static void
ilbd_scf_str_to_algo(ilb_algo_t *algo_type, char *valstr)
{
int i;
for (i = 0; i < ILBD_ALGO_TBL_SIZE; i++) {
if (strcmp(valstr, algo_tbl[i].algo_str) == 0) {
*algo_type = algo_tbl[i].algo_type;
return;
}
}
logerr("ilbd_scf_str_to_algo: algo not found");
}
struct topo_tbl_entry {
ilb_topo_t topo_type;
const char *topo_str;
} topo_tbl[] = {
{ILB_TOPO_DSR, "DSR"},
{ILB_TOPO_NAT, "NAT"},
{ILB_TOPO_HALF_NAT, "HALF-NAT"}
};
#define ILBD_TOPO_TBL_SIZE (sizeof (topo_tbl) / \
sizeof (*topo_tbl))
void
ilbd_topo_to_str(ilb_topo_t topo_type, char *valstr)
{
int i;
for (i = 0; i < ILBD_TOPO_TBL_SIZE; i++) {
if (topo_type == topo_tbl[i].topo_type) {
(void) strlcpy(valstr, topo_tbl[i].topo_str,
ILBD_MAX_VALUE_LEN);
return;
}
}
logerr("ilbd_scf_topo_to_str: topo not found");
}
static void
ilbd_scf_str_to_topo(ilb_topo_t *topo_type, char *valstr)
{
int i;
for (i = 0; i < ILBD_TOPO_TBL_SIZE; i++) {
if (strcmp(valstr, topo_tbl[i].topo_str) == 0) {
*topo_type = topo_tbl[i].topo_type;
return;
}
}
logerr("ilbd_scf_str_to_topo: topo not found");
}
static void
ilbd_get_svr_field(char *valstr, struct in6_addr *sgs_addr,
int32_t *min_port, int32_t *max_port, int32_t *sgs_flags)
{
char *ipaddr, *ipverstr, *portstr, *flagstr;
int ip_ver;
ilb_ip_addr_t temp_ip;
void *addrptr;
char *max_portstr;
ipaddr = strtok(valstr, ";");
ipverstr = strtok(NULL, ";");
portstr = strtok(NULL, ";");
flagstr = strtok(NULL, ";");
if (ipaddr == NULL || ipverstr == NULL || portstr == NULL ||
flagstr == NULL) {
logerr("%s: invalid server fields", __func__);
(void) smf_maintain_instance(ILB_FMRI, SMF_IMMEDIATE);
exit(EXIT_FAILURE);
}
ip_ver = atoi(ipverstr);
addrptr = (ip_ver == AF_INET) ? (void *)&temp_ip.ia_v4 :
(void *)&temp_ip.ia_v6;
if (inet_pton(ip_ver, ipaddr, addrptr) == NULL) {
logerr("ilbd_get_svr_field: inet_pton failed");
return;
}
if (ip_ver == AF_INET) {
IN6_INADDR_TO_V4MAPPED(&(temp_ip.ia_v4), sgs_addr);
} else {
(void) memcpy(sgs_addr, &(temp_ip.ia_v6),
sizeof (struct in6_addr));
}
*sgs_flags = atoi(flagstr);
*min_port = atoi(strtok(portstr, "-"));
*min_port = ntohs(*min_port);
max_portstr = strtok(NULL, "-");
if (max_portstr != NULL) {
*max_port = atoi(max_portstr);
*max_port = ntohs(*max_port);
}
}
/*
* Convert the info of a server to its SCF string value representation.
* Argument value is assumed to be of size ILBD_MAX_VALUE_LEN.
*/
static void
ilbd_srv_scf_val(ilbd_srv_t *srv, char *value)
{
char ipstr[INET6_ADDRSTRLEN];
int ipver;
if (GET_AF(&srv->isv_addr) == AF_INET) {
struct in_addr v4_addr;
IN6_V4MAPPED_TO_INADDR(&srv->isv_addr, &v4_addr);
(void) inet_ntop(AF_INET, &v4_addr, ipstr, sizeof (ipstr));
ipver = AF_INET;
} else {
(void) inet_ntop(AF_INET6, &srv->isv_addr, ipstr,
sizeof (ipstr));
ipver = AF_INET6;
}
(void) snprintf(value, ILBD_MAX_VALUE_LEN, "%s;%d;%d-%d;%d",
ipstr, ipver, ntohs(srv->isv_minport), ntohs(srv->isv_maxport),
srv->isv_flags);
}
/* get the "ip:port:status" str of the #num server in the servergroup */
ilb_status_t
ilbd_get_svr_info(ilbd_sg_t *sg, int num, char *valstr, char *svrname)
{
int i;
ilbd_srv_t *tmp_srv = NULL;
tmp_srv = list_head(&sg->isg_srvlist);
if (tmp_srv == NULL)
return (ILB_STATUS_ENOENT);
for (i = 0; i < num; i++)
tmp_srv = list_next(&sg->isg_srvlist, tmp_srv);
assert(tmp_srv != NULL);
if (valstr != NULL)
ilbd_srv_scf_val(tmp_srv, valstr);
if (svrname != NULL) {
(void) snprintf(svrname, ILBD_MAX_NAME_LEN, "server%d",
tmp_srv->isv_id);
}
return (ILB_STATUS_OK);
}
/* convert a struct in6_addr to valstr */
ilb_status_t
ilbd_scf_ip_to_str(uint16_t ipversion, struct in6_addr *addr,
scf_type_t *scftype, char *valstr)
{
size_t vallen;
ilb_ip_addr_t ipaddr;
void *addrptr;
vallen = (ipversion == AF_INET) ? INET_ADDRSTRLEN :
INET6_ADDRSTRLEN;
if (scftype != NULL)
*scftype = (ipversion == AF_INET) ? SCF_TYPE_NET_ADDR_V4 :
SCF_TYPE_NET_ADDR_V6;
IP_COPY_IMPL_2_CLI(addr, &ipaddr);
addrptr = (ipversion == AF_INET) ?
(void *)&ipaddr.ia_v4 : (void *)&ipaddr.ia_v6;
(void) inet_ntop(ipversion, (void *)addrptr, valstr, vallen);
return (ILB_STATUS_OK);
}
/*
* This function takes a ilbd internal data struct and translate its value to
* scf value. The data struct is passed in within "data".
* Upon successful return, the scf val will be stored in "val" and the scf type
* will be returned in "scftype" if scftype != NULL, the number of values
* translated will be in "numval"
* If it failed, no data will be written to SCF
*/
static ilb_status_t
ilbd_data_to_scfval(ilbd_scf_pg_type_t pg_type, ilbd_var_type_t type,
scf_handle_t *h, void *data, scf_value_t ***val, scf_type_t *scftype,
int *numval)
{
scf_value_t *v, **varray = NULL;
int ret = ILB_STATUS_OK;
int i;
int scf_val_len = ILBD_MAX_VALUE_LEN;
char *valstr = NULL;
int valint;
uint8_t valbool = 0;
ilbd_rule_t *r_ent = NULL;
ilbd_sg_t *s_ent = NULL;
ilbd_hc_t *h_ent = NULL;
switch (pg_type) {
case ILBD_SCF_RULE:
r_ent = (ilbd_rule_t *)data;
break;
case ILBD_SCF_SG:
s_ent = (ilbd_sg_t *)data;
break;
case ILBD_SCF_HC:
h_ent = (ilbd_hc_t *)data;
break;
}
v = scf_value_create(h);
if (v == NULL)
return (ILB_STATUS_INTERNAL);
if ((valstr = malloc(scf_val_len)) == NULL)
return (ILB_STATUS_ENOMEM);
switch (type) {
case ILBD_RULE_STATUS:
valbool = r_ent->irl_flags & ILB_FLAGS_RULE_ENABLED;
break;
case ILBD_RULE_VIP:
ret = ilbd_scf_ip_to_str(r_ent->irl_ipversion, &r_ent->irl_vip,
scftype, valstr);
if (ret != ILB_STATUS_OK) {
free(valstr);
scf_value_destroy(v);
return (ret);
}
break;
case ILBD_RULE_PROTO: {
struct protoent *protoent;
protoent = getprotobynumber(r_ent->irl_proto);
(void) strlcpy(valstr, protoent->p_name, scf_val_len);
break;
}
case ILBD_RULE_PORT:
(void) snprintf(valstr, scf_val_len, "%d-%d",
r_ent->irl_minport, r_ent->irl_maxport);
break;
case ILBD_RULE_ALGO:
ilbd_algo_to_str(r_ent->irl_algo, valstr);
break;
case ILBD_RULE_TOPO:
ilbd_topo_to_str(r_ent->irl_topo, valstr);
break;
case ILBD_RULE_NAT_STR:
ret = ilbd_scf_ip_to_str(r_ent->irl_ipversion,
&r_ent->irl_nat_src_start, scftype, valstr);
if (ret != ILB_STATUS_OK) {
free(valstr);
scf_value_destroy(v);
return (ret);
}
break;
case ILBD_RULE_NAT_END:
ret = ilbd_scf_ip_to_str(r_ent->irl_ipversion,
&r_ent->irl_nat_src_end, scftype, valstr);
if (ret != ILB_STATUS_OK) {
free(valstr);
scf_value_destroy(v);
return (ret);
}
break;
case ILBD_RULE_STI_MASK:
ret = ilbd_scf_ip_to_str(r_ent->irl_ipversion,
&r_ent->irl_stickymask, scftype, valstr);
if (ret != ILB_STATUS_OK) {
free(valstr);
scf_value_destroy(v);
return (ret);
}
break;
case ILBD_RULE_SGNAME:
(void) strlcpy(valstr, r_ent->irl_sgname, scf_val_len);
break;
case ILBD_RULE_HCNAME:
if (r_ent->irl_hcname[0] != '\0')
(void) strlcpy(valstr, r_ent->irl_hcname,
scf_val_len);
else
bzero(valstr, ILBD_MAX_VALUE_LEN);
break;
case ILBD_RULE_HCPORT:
valint = r_ent->irl_hcport;
break;
case ILBD_RULE_HCPFLAG:
valint = r_ent->irl_hcpflag;
break;
case ILBD_RULE_DRAINTIME:
valint = r_ent->irl_conndrain;
break;
case ILBD_RULE_NAT_TO:
valint = r_ent->irl_nat_timeout;
break;
case ILBD_RULE_PERS_TO:
valint = r_ent->irl_sticky_timeout;
break;
case ILBD_SG_SERVER:
if (s_ent->isg_srvcount == 0) {
(void) strlcpy(valstr, "EMPTY_SERVERGROUP",
scf_val_len);
break;
}
varray = calloc(sizeof (*varray), s_ent->isg_srvcount);
if (varray == NULL) {
scf_value_destroy(v);
free(valstr);
return (ILB_STATUS_ENOMEM);
}
for (i = 0; i < s_ent->isg_srvcount; i++) {
if (v == NULL) {
for (i--; i >= 0; i--)
scf_value_destroy(varray[i]);
free(valstr);
return (ILB_STATUS_ENOMEM);
}
ret = ilbd_get_svr_info(s_ent, i, valstr, NULL);
if (ret != ILB_STATUS_OK) {
scf_value_destroy(v);
for (i--; i >= 0; i--)
scf_value_destroy(varray[i]);
free(valstr);
free(varray);
return (ret);
}
(void) scf_value_set_astring(v, valstr);
varray[i] = v;
v = scf_value_create(h);
}
/* the last 'v' we created will go unused, so drop it */
scf_value_destroy(v);
*numval = s_ent->isg_srvcount;
*val = varray;
free(valstr);
return (ret);
case ILBD_HC_TEST:
(void) strlcpy(valstr, h_ent->ihc_test, scf_val_len);
break;
case ILBD_HC_TIMEOUT:
valint = h_ent->ihc_timeout;
break;
case ILBD_HC_INTERVAL:
valint = h_ent->ihc_interval;
break;
case ILBD_HC_DEF_PING:
valbool = h_ent->ihc_def_ping;
break;
case ILBD_HC_COUNT:
valint = h_ent->ihc_count;
break;
}
switch (*scftype) {
case SCF_TYPE_BOOLEAN:
scf_value_set_boolean(v, valbool);
break;
case SCF_TYPE_ASTRING:
(void) scf_value_set_astring(v, valstr);
break;
case SCF_TYPE_INTEGER:
scf_value_set_integer(v, valint);
break;
case SCF_TYPE_NET_ADDR_V4:
(void) scf_value_set_from_string(v, SCF_TYPE_NET_ADDR_V4,
valstr);
break;
case SCF_TYPE_NET_ADDR_V6:
(void) scf_value_set_from_string(v, SCF_TYPE_NET_ADDR_V6,
valstr);
break;
}
free(valstr);
varray = calloc(1, sizeof (*varray));
if (varray == NULL) {
scf_value_destroy(v);
return (ILB_STATUS_ENOMEM);
}
varray[0] = v;
*val = varray;
*numval = 1;
return (ret);
}
/*
* create a scf property group
*/
ilb_status_t
ilbd_create_pg(ilbd_scf_pg_type_t pg_type, void *data)
{
ilb_status_t ret;
char *pgname;
scf_propertygroup_t *pg = NULL;
scf_value_t **val;
scf_handle_t *h;
int scf_name_len = ILBD_MAX_NAME_LEN;
char *scfpgbuf; /* property group name or group type */
int i, i_st, i_end;
switch (pg_type) {
case ILBD_SCF_RULE: {
ilbd_rule_t *r_ent = (ilbd_rule_t *)data;
pgname = r_ent->irl_name;
i_st = 0;
i_end = ILBD_RULE_VAR_NUM;
break;
}
case ILBD_SCF_SG: {
ilbd_sg_t *s_ent = (ilbd_sg_t *)data;
pgname = s_ent->isg_name;
i_st = ILBD_RULE_VAR_NUM;
i_end = ILBD_RULE_VAR_NUM + ILBD_SG_VAR_NUM;
break;
}
case ILBD_SCF_HC: {
ilbd_hc_t *h_ent = (ilbd_hc_t *)data;
pgname = h_ent->ihc_name;
i_st = ILBD_RULE_VAR_NUM + ILBD_SG_VAR_NUM;
i_end = ILBD_PROP_VAR_NUM;
break;
}
default:
logdebug("ilbd_create_pg: invalid pg type %d for pg %s",
pg_type, pgname);
return (ILB_STATUS_EINVAL);
}
if ((scfpgbuf = malloc(scf_name_len)) == NULL)
return (ILB_STATUS_ENOMEM);
ilbd_name_to_scfpgname(pg_type, pgname, scfpgbuf);
ret = ilbd_scf_retrieve_pg(scfpgbuf, &pg, B_TRUE);
if (ret != ILB_STATUS_OK) {
free(scfpgbuf);
return (ret);
}
h = scf_pg_handle(pg);
/* fill in props */
for (i = i_st; i < i_end; i++) {
int num, j;
scf_type_t scftype = prop_tbl[i].scf_proptype;
ret = ilbd_data_to_scfval(pg_type, prop_tbl[i].val_type, h,
data, &val, &scftype, &num);
if (ret != ILB_STATUS_OK)
goto done;
for (j = 0; j < num; j++) {
if (pg_type == ILBD_SCF_SG) {
ret = ilbd_get_svr_info(data, j, NULL,
scfpgbuf);
if (ret == ILB_STATUS_ENOENT) {
(void) strlcpy(scfpgbuf,
"EMPTY_SERVER", scf_name_len);
}
ret = ilbd_scf_set_prop(pg, scfpgbuf,
scftype, val[j]);
} else {
ret = ilbd_scf_set_prop(pg,
prop_tbl[i].scf_propname, scftype, val[j]);
}
scf_value_destroy(val[j]);
}
free(val);
}
done:
free(scfpgbuf);
ilbd_scf_destroy(h, NULL, NULL, pg);
return (ret);
}
/*
* destroy a scf property group
*/
static ilb_status_t
ilbd_scf_delete_pg(scf_propertygroup_t *pg)
{
if (scf_pg_delete(pg) != 0)
return (ilbd_scf_err_to_ilb_err());
return (ILB_STATUS_OK);
}
/* sg can have same name as rule */
ilb_status_t
ilbd_destroy_pg(ilbd_scf_pg_type_t pg_t, const char *pgname)
{
ilb_status_t ret;
scf_propertygroup_t *pg;
int scf_name_len = ILBD_MAX_NAME_LEN;
char *scfname;
if ((scfname = malloc(scf_name_len)) == NULL)
return (ILB_STATUS_ENOMEM);
ilbd_name_to_scfpgname(pg_t, pgname, scfname);
ret = ilbd_scf_retrieve_pg(scfname, &pg, B_FALSE);
free(scfname);
if (ret != ILB_STATUS_EEXIST)
return (ret);
ret = ilbd_scf_delete_pg(pg);
ilbd_scf_destroy(scf_pg_handle(pg), NULL, NULL, pg);
return (ret);
}
/*
* Set named property to scf value specified. If property is new,
* create it.
*/
static ilb_status_t
ilbd_scf_set_prop(scf_propertygroup_t *pg, const char *propname,
scf_type_t proptype, scf_value_t *val)
{
scf_handle_t *h = NULL;
scf_property_t *prop = NULL;
scf_value_t *oldval = NULL;
scf_transaction_t *tx = NULL;
scf_transaction_entry_t *ent = NULL;
boolean_t new = B_FALSE;
ilb_status_t ret = ILB_STATUS_OK;
int commit_ret;
h = scf_pg_handle(pg);
if (h == NULL || propname == NULL)
return (ILB_STATUS_EINVAL);
ret = ilbd_scf_get_prop_val(pg, propname, &oldval);
if (oldval != NULL)
scf_value_destroy(oldval);
if (ret == ILB_STATUS_ENOENT)
new = B_TRUE;
else if (ret != ILB_STATUS_OK)
return (ret);
if ((prop = scf_property_create(h)) == NULL)
return (ilbd_scf_err_to_ilb_err());
if ((tx = scf_transaction_create(h)) == NULL ||
(ent = scf_entry_create(h)) == NULL) {
ret = ilbd_scf_err_to_ilb_err();
logdebug("ilbd_scf_set_prop: create scf transaction failed\n");
goto out;
}
if (scf_transaction_start(tx, pg) == -1) {
ret = ilbd_scf_err_to_ilb_err();
logdebug("ilbd_scf_set_prop: start scf transaction failed\n");
goto out;
}
if (new) {
if (scf_transaction_property_new(tx, ent, propname,
proptype) == -1) {
ret = ilbd_scf_err_to_ilb_err();
logdebug("ilbd_scf_set_prop: create scf prop failed\n");
goto out;
}
} else {
if (scf_transaction_property_change(tx, ent, propname, proptype)
== -1) {
ret = ilbd_scf_err_to_ilb_err();
logdebug("ilbd_scf_set_prop: change scf prop failed\n");
goto out;
}
}
if (scf_entry_add_value(ent, val) != 0) {
logdebug("ilbd_scf_set_prop: add scf entry failed\n");
ret = ilbd_scf_err_to_ilb_err();
goto out;
}
commit_ret = scf_transaction_commit(tx);
switch (commit_ret) {
case 1:
ret = ILB_STATUS_OK;
/* update pg here, so subsequent property setting succeeds */
(void) scf_pg_update(pg);
break;
case 0:
/* transaction failed due to not having most recent pg */
ret = ILB_STATUS_INUSE;
break;
default:
ret = ilbd_scf_err_to_ilb_err();
break;
}
out:
if (tx != NULL)
scf_transaction_destroy(tx);
if (ent != NULL)
scf_entry_destroy(ent);
if (prop != NULL)
scf_property_destroy(prop);
return (ret);
}
/*
* get a prop's scf val
*/
static ilb_status_t
ilbd_scf_get_prop_val(scf_propertygroup_t *pg, const char *propname,
scf_value_t **val)
{
scf_handle_t *h = NULL;
scf_property_t *prop = NULL;
scf_value_t *value = NULL;
ilb_status_t ret = ILB_STATUS_OK;
h = scf_pg_handle(pg);
if (h == NULL || propname == NULL)
return (ILB_STATUS_EINVAL);
if ((prop = scf_property_create(h)) == NULL)
return (ilbd_scf_err_to_ilb_err());
if (scf_pg_get_property(pg, propname, prop) != 0) {
ret = ilbd_scf_err_to_ilb_err();
goto out;
}
if ((value = scf_value_create(h)) == NULL) {
ret = ilbd_scf_err_to_ilb_err();
goto out;
}
if (scf_property_get_value(prop, value) != 0) {
scf_value_destroy(value);
ret = ilbd_scf_err_to_ilb_err();
goto out;
}
*val = value;
out:
if (prop != NULL)
scf_property_destroy(prop);
return (ret);
}
typedef struct ilbd_data
{
union {
ilb_sg_info_t *sg_info;
ilb_hc_info_t *hc_info;
ilb_rule_info_t *rule_info;
} data;
ilbd_scf_pg_type_t pg_type; /* type of data */
#define sg_data data.sg_info
#define hc_data data.hc_info
#define rule_data data.rule_info
} ilbd_data_t;
void
ilbd_scf_str_to_ip(int ipversion, char *ipstr, struct in6_addr *addr)
{
ilb_ip_addr_t ipaddr;
void *addrptr;
addrptr = (ipversion == AF_INET) ?
(void *)&ipaddr.ia_v4 : (void *)&ipaddr.ia_v6;
(void) inet_pton(ipversion, ipstr, addrptr);
if (ipversion == AF_INET) {
IN6_INADDR_TO_V4MAPPED(&(ipaddr.ia_v4), addr);
} else {
(void) memcpy(addr, &(ipaddr.ia_v6),
sizeof (struct in6_addr));
}
}
/*
* This function takes a scf value and writes it to the correct field of the
* corresponding data struct.
*/
static ilb_status_t
ilbd_scfval_to_data(const char *propname, ilbd_var_type_t ilb_type,
scf_value_t *val, ilbd_data_t *ilb_data)
{
scf_type_t scf_type = scf_value_type(val);
ilbd_scf_pg_type_t pg_type = ilb_data->pg_type;
int ret = 0;
ilb_rule_info_t *r_ent = NULL;
ilb_sg_info_t *s_ent = NULL;
ilb_hc_info_t *h_ent = NULL;
char ipstr[INET6_ADDRSTRLEN];
char *valstr;
int64_t valint;
uint8_t valbool;
int ipversion;
switch (pg_type) {
case ILBD_SCF_RULE:
r_ent = ilb_data->rule_data;
break;
case ILBD_SCF_HC:
h_ent = ilb_data->hc_data;
break;
case ILBD_SCF_SG:
s_ent = ilb_data->sg_data;
break;
}
/* get scf value out */
if ((valstr = malloc(ILBD_MAX_VALUE_LEN)) == NULL)
return (ILB_STATUS_ENOMEM);
switch (scf_type) {
case SCF_TYPE_NET_ADDR_V4:
if (scf_value_get_as_string_typed(val,
SCF_TYPE_NET_ADDR_V4, ipstr, INET_ADDRSTRLEN) < 0) {
free(valstr);
return (ILB_STATUS_INTERNAL);
}
ipversion = AF_INET;
break;
case SCF_TYPE_NET_ADDR_V6:
if (scf_value_get_as_string_typed(val,
SCF_TYPE_NET_ADDR_V6, ipstr,
INET6_ADDRSTRLEN) < 0) {
free(valstr);
return (ILB_STATUS_INTERNAL);
}
ipversion = AF_INET6;
break;
case SCF_TYPE_BOOLEAN:
if (scf_value_get_boolean(val, &valbool) < 0) {
free(valstr);
return (ILB_STATUS_INTERNAL);
}
break;
case SCF_TYPE_ASTRING:
if (scf_value_get_astring(val, valstr,
ILBD_MAX_VALUE_LEN) < 0) {
free(valstr);
return (ILB_STATUS_INTERNAL);
}
break;
case SCF_TYPE_INTEGER:
if (scf_value_get_integer(val, &valint) < 0) {
free(valstr);
return (ILB_STATUS_INTERNAL);
}
break;
default:
free(valstr);
return (ILB_STATUS_INTERNAL);
}
ret = ILB_STATUS_OK;
switch (ilb_type) {
case ILBD_RULE_STATUS:
if (valbool)
r_ent->rl_flags |= ILB_FLAGS_RULE_ENABLED;
break;
case ILBD_RULE_VIP:
r_ent->rl_ipversion = ipversion;
ilbd_scf_str_to_ip(ipversion, ipstr, &r_ent->rl_vip);
break;
case ILBD_RULE_PROTO: {
struct protoent *protoent;
protoent = getprotobyname(valstr);
r_ent->rl_proto = protoent->p_proto;
break;
}
case ILBD_RULE_PORT: {
char *token1, *token2;
token1 = strtok(valstr, "-");
token2 = strtok(NULL, "-");
r_ent->rl_minport = atoi(token1);
r_ent->rl_maxport = atoi(token2);
break;
}
case ILBD_RULE_ALGO:
ilbd_scf_str_to_algo(&(r_ent->rl_algo), valstr);
break;
case ILBD_RULE_TOPO:
ilbd_scf_str_to_topo(&(r_ent->rl_topo), valstr);
break;
case ILBD_RULE_NAT_STR:
ilbd_scf_str_to_ip(ipversion, ipstr, &r_ent->rl_nat_src_start);
break;
case ILBD_RULE_NAT_END:
ilbd_scf_str_to_ip(ipversion, ipstr, &r_ent->rl_nat_src_end);
break;
case ILBD_RULE_STI_MASK:
ilbd_scf_str_to_ip(ipversion, ipstr, &r_ent->rl_stickymask);
if (ipversion == AF_INET) {
if (!IN6_IS_ADDR_V4MAPPED_ANY(&r_ent->rl_stickymask))
r_ent->rl_flags |= ILB_FLAGS_RULE_STICKY;
} else {
if (!IN6_IS_ADDR_UNSPECIFIED(&r_ent->rl_stickymask))
r_ent->rl_flags |= ILB_FLAGS_RULE_STICKY;
}
break;
case ILBD_RULE_SGNAME:
(void) strlcpy(r_ent->rl_sgname, valstr,
sizeof (r_ent->rl_sgname));
break;
case ILBD_RULE_HCNAME:
(void) strlcpy(r_ent->rl_hcname, valstr,
sizeof (r_ent->rl_hcname));
break;
case ILBD_RULE_HCPORT:
r_ent->rl_hcport = valint;
break;
case ILBD_RULE_HCPFLAG:
r_ent->rl_hcpflag = valint;
break;
case ILBD_RULE_DRAINTIME:
r_ent->rl_conndrain = valint;
break;
case ILBD_RULE_NAT_TO:
r_ent->rl_nat_timeout = valint;
break;
case ILBD_RULE_PERS_TO:
r_ent->rl_sticky_timeout = valint;
break;
case ILBD_SG_SERVER: {
int svr_cnt = s_ent->sg_srvcount;
/* found a new server, increase the svr count of this sg */
s_ent->sg_srvcount++;
/*
* valstr contains information of one server in the servergroup
* valstr is in the format of "ip:minport-maxport:enable"
*/
s_ent = realloc(s_ent, sizeof (ilb_sg_info_t) +
s_ent->sg_srvcount * sizeof (ilb_sg_srv_t));
/* sgs_srvID is the sg name, leave it blank */
/*
* sgs_id is the digit in propname, propname is in a format of
* "server" + the digital serverID. We get the serverID by
* reading from the 7th char of propname.
*/
s_ent->sg_servers[svr_cnt].sgs_id = atoi(&propname[6]);
ilbd_get_svr_field(valstr,
&s_ent->sg_servers[svr_cnt].sgs_addr,
&s_ent->sg_servers[svr_cnt].sgs_minport,
&s_ent->sg_servers[svr_cnt].sgs_maxport,
&s_ent->sg_servers[svr_cnt].sgs_flags);
ilb_data->sg_data = s_ent;
break;
}
case ILBD_HC_TEST:
(void) strlcpy(h_ent->hci_test, valstr,
sizeof (h_ent->hci_test));
break;
case ILBD_HC_TIMEOUT:
h_ent->hci_timeout = valint;
break;
case ILBD_HC_INTERVAL:
h_ent->hci_interval = valint;
break;
case ILBD_HC_DEF_PING:
h_ent->hci_def_ping = valbool;
break;
case ILBD_HC_COUNT:
h_ent->hci_count = valint;
break;
case ILBD_VAR_INVALID:
/*
* An empty server group is represented by an invalid
* SCF property. So when loading a server group, this
* case can be hit. But it should happen only for this
* single case. So if it happens in another case, move
* the service into maintenance mode.
*/
if (pg_type != ILBD_SCF_SG || scf_type != SCF_TYPE_ASTRING) {
logerr("%s: invalid ilb type", __func__);
(void) smf_maintain_instance(ILB_FMRI, SMF_IMMEDIATE);
} else {
logdebug("%s: invalid ilb type", __func__);
}
break;
}
free(valstr);
return (ret);
}
static ilbd_var_type_t
ilbd_name_to_valtype(const char *prop_name)
{
int i;
for (i = 0; i < ILBD_PROP_VAR_NUM; i++)
if (strncmp(prop_name, prop_tbl[i].scf_propname,
strlen(prop_tbl[i].scf_propname)) == 0)
return (prop_tbl[i].val_type);
logdebug("ilbd_name_to_valtype: couldn't find prop %s", prop_name);
return (ILBD_VAR_INVALID);
}
/* callback for pg_walk_prop, arg is ilbd_data_t */
static ilb_status_t
ilbd_scf_load_prop(scf_propertygroup_t *pg, const char *prop_name, void *arg)
{
scf_handle_t *h;
scf_value_t *val;
ilb_status_t ret;
ilbd_data_t *ilb_data = (ilbd_data_t *)arg;
ilbd_var_type_t val_type = ilbd_name_to_valtype(prop_name);
h = scf_pg_handle(pg);
if (h == NULL)
return (ILB_STATUS_EINVAL);
ret = ilbd_scf_get_prop_val(pg, prop_name, &val);
if (ret == ILB_STATUS_ENOENT)
return (ILB_STATUS_OK);
else if (ret != ILB_STATUS_OK)
return (ret);
/*
* Load value to ilb_data.
*/
ret = ilbd_scfval_to_data(prop_name, val_type, val, ilb_data);
out:
if (val != NULL)
scf_value_destroy(val);
return (ret);
}
/*
* walk properties in one prop group, arg is ilbd_data
* cb is ilbd_scf_load_prop()
*/
static ilb_status_t
ilbd_scf_pg_walk_props(scf_propertygroup_t *pg,
ilb_status_t (*cb)(scf_propertygroup_t *, const char *, void *),
void *arg)
{
scf_handle_t *h;
scf_iter_t *propiter;
scf_property_t *prop;
int scf_name_len = ILBD_MAX_NAME_LEN;
char *prop_name = NULL;
ilb_status_t ret = ILB_STATUS_OK;
int scf_ret = -1;
h = scf_pg_handle(pg);
if (h == NULL)
return (ILB_STATUS_EINVAL);
prop = scf_property_create(h);
propiter = scf_iter_create(h);
if (prop == NULL || propiter == NULL)
goto out;
if (scf_iter_pg_properties(propiter, pg) != 0)
goto out;
if ((prop_name = malloc(scf_name_len)) == NULL) {
ret = ILB_STATUS_ENOMEM;
goto out;
}
while ((scf_ret = scf_iter_next_property(propiter, prop)) == 1) {
if (scf_property_get_name(prop, prop_name, scf_name_len)
< 0) {
ret = ilbd_scf_err_to_ilb_err();
goto out;
}
ret = cb(pg, prop_name, arg);
if (ret != ILB_STATUS_OK)
break;
}
out:
if (prop_name != NULL)
free(prop_name);
if (scf_ret == -1)
ret = ilbd_scf_err_to_ilb_err();
if (prop != NULL)
scf_property_destroy(prop);
if (propiter != NULL)
scf_iter_destroy(propiter);
return (ret);
}
/* cbs are libd_create_X */
static ilb_status_t
ilbd_scf_instance_walk_pg(scf_instance_t *inst,
ilbd_scf_pg_type_t pg_type,
ilb_status_t (*cb)(void *, int, struct passwd *, ucred_t *),
void *arg1, void *arg2)
{
int scf_ret;
ilb_status_t ret;
scf_handle_t *h;
scf_iter_t *pgiter;
scf_propertygroup_t *newpg;
int port = *((int *)arg1);
int scf_name_len = ILBD_MAX_NAME_LEN;
char *pg_name = NULL;
if (inst == NULL)
return (ILB_STATUS_EINVAL);
h = scf_instance_handle(inst);
if (h == NULL)
return (ILB_STATUS_EINVAL);
if ((newpg = scf_pg_create(h)) == NULL)
return (ilbd_scf_err_to_ilb_err());
if ((pgiter = scf_iter_create(h)) == NULL) {
scf_pg_destroy(newpg);
return (ilbd_scf_err_to_ilb_err());
}
if ((scf_ret = scf_iter_instance_pgs(pgiter, inst)) < 0)
goto out;
if ((pg_name = malloc(scf_name_len)) == NULL) {
ret = ILB_STATUS_ENOMEM;
goto out;
}
while ((scf_ret = scf_iter_next_pg(pgiter, newpg)) > 0) {
ilbd_data_t data;
if (scf_pg_get_name(newpg, pg_name, scf_name_len) < 0) {
ret = ilbd_scf_err_to_ilb_err();
goto out;
}
/*
* if pg name indicates it's a ilb configuration, walk its prop
*/
data.pg_type = pg_type;
data.hc_data = NULL;
data.sg_data = NULL;
data.rule_data = NULL;
switch (pg_type) {
case ILBD_SCF_RULE:
if (strncmp(ILBD_PG_NAME_RULE, pg_name,
strlen(ILBD_PG_NAME_RULE)) == 0) {
data.rule_data = calloc(1,
sizeof (ilb_rule_info_t));
if (data.rule_data == NULL) {
ret = ILB_STATUS_ENOMEM;
goto out;
}
ret = ilbd_scf_pg_walk_props(newpg,
ilbd_scf_load_prop, &data);
if (ret != ILB_STATUS_OK)
goto out;
assert(data.rule_data != NULL);
/* set rule name */
(void) strlcpy(data.rule_data->rl_name,
&pg_name[strlen(ILBD_PG_NAME_RULE)],
sizeof (data.rule_data->rl_name));
ret = cb(data.rule_data, port, arg2, NULL);
free(data.rule_data);
if (ret != ILB_STATUS_OK)
goto out;
}
break;
case ILBD_SCF_SG:
if (strncmp(ILBD_PG_NAME_SG, pg_name,
strlen(ILBD_PG_NAME_SG)) == 0) {
data.sg_data = calloc(1,
sizeof (ilb_sg_info_t));
if (data.sg_data == NULL) {
ret = ILB_STATUS_ENOMEM;
goto out;
}
ret = ilbd_scf_pg_walk_props(newpg,
ilbd_scf_load_prop, &data);
if (ret != ILB_STATUS_OK) {
free(data.sg_data);
goto out;
}
assert(data.sg_data != NULL);
/* set sg name */
(void) strlcpy(data.sg_data->sg_name,
&pg_name[strlen(ILBD_PG_NAME_SG)],
sizeof (data.sg_data->sg_name));
ret = cb(data.sg_data, port, arg2, NULL);
if (ret != ILB_STATUS_OK) {
free(data.sg_data);
goto out;
}
/*
* create a servergroup is two-step operation.
* 1. create an empty servergroup.
* 2. add server(s) to the group.
*
* since we are here from:
* main_loop()->ilbd_read_config()->
* ilbd_walk_sg_pgs()
* there is no cli to send. So in this
* path auditing will skip the
* adt_set_from_ucred() check
*/
if (data.sg_data->sg_srvcount > 0) {
ret = ilbd_add_server_to_group(
data.sg_data, port, NULL, NULL);
if (ret != ILB_STATUS_OK) {
free(data.sg_data);
goto out;
}
free(data.sg_data);
}
}
break;
case ILBD_SCF_HC:
if (strncmp(ILBD_PG_NAME_HC, pg_name,
strlen(ILBD_PG_NAME_HC)) == 0) {
data.hc_data = calloc(1,
sizeof (ilb_hc_info_t));
if (data.hc_data == NULL) {
ret = ILB_STATUS_ENOMEM;
goto out;
}
ret = ilbd_scf_pg_walk_props(newpg,
ilbd_scf_load_prop, &data);
if (ret != ILB_STATUS_OK)
goto out;
assert(data.hc_data != NULL);
/* set hc name */
(void) strlcpy(data.hc_data->hci_name,
&pg_name[strlen(ILBD_PG_NAME_HC)],
sizeof (data.hc_data->hci_name));
ret = cb(data.hc_data, port, arg2, NULL);
free(data.hc_data);
if (ret != ILB_STATUS_OK)
goto out;
}
break;
}
}
out:
if (pg_name != NULL)
free(pg_name);
if (scf_ret < 0)
ret = ilbd_scf_err_to_ilb_err();
scf_pg_destroy(newpg);
scf_iter_destroy(pgiter);
return (ret);
}
typedef ilb_status_t (*ilbd_scf_walker_fn)(void *, int, struct passwd *,
ucred_t *);
ilb_status_t
ilbd_walk_rule_pgs(ilb_status_t (*func)(ilb_rule_info_t *, int,
const struct passwd *, ucred_t *), void *arg1, void *arg2)
{
scf_instance_t *inst;
scf_handle_t *h;
scf_service_t *svc;
ilb_status_t ret;
ret = ilbd_scf_get_inst(&h, &svc, &inst);
if (ret != ILB_STATUS_OK)
return (ret);
/* get rule prop group, transfer it to ilb_lrule_info_t */
ret = ilbd_scf_instance_walk_pg(inst, ILBD_SCF_RULE,
(ilbd_scf_walker_fn)func, arg1, arg2);
ilbd_scf_destroy(h, svc, inst, NULL);
return (ret);
}
ilb_status_t
ilbd_walk_sg_pgs(ilb_status_t (*func)(ilb_sg_info_t *, int,
const struct passwd *, ucred_t *), void *arg1, void *arg2)
{
scf_instance_t *inst;
scf_handle_t *h;
scf_service_t *svc;
ilb_status_t ret;
ret = ilbd_scf_get_inst(&h, &svc, &inst);
if (ret != ILB_STATUS_OK)
return (ret);
ret = ilbd_scf_instance_walk_pg(inst, ILBD_SCF_SG,
(ilbd_scf_walker_fn)func, arg1, arg2);
ilbd_scf_destroy(h, svc, inst, NULL);
return (ret);
}
ilb_status_t
ilbd_walk_hc_pgs(ilb_status_t (*func)(const ilb_hc_info_t *, int,
const struct passwd *, ucred_t *), void *arg1, void *arg2)
{
scf_instance_t *inst;
scf_handle_t *h;
scf_service_t *svc;
ilb_status_t ret;
ret = ilbd_scf_get_inst(&h, &svc, &inst);
if (ret != ILB_STATUS_OK)
return (ret);
ret = ilbd_scf_instance_walk_pg(inst, ILBD_SCF_HC,
(ilbd_scf_walker_fn)func, arg1, arg2);
ilbd_scf_destroy(h, svc, inst, NULL);
return (ret);
}
ilb_status_t
ilbd_change_prop(ilbd_scf_pg_type_t pg_type, const char *pg_name,
const char *prop_name, void *new_val)
{
int ret;
scf_propertygroup_t *scfpg = NULL;
char *scf_pgname = NULL;
scf_type_t scftype;
scf_value_t *scfval;
scf_handle_t *h;
if ((scf_pgname = malloc(ILBD_MAX_NAME_LEN)) == NULL)
return (ILB_STATUS_ENOMEM);
ilbd_name_to_scfpgname(pg_type, pg_name, scf_pgname);
ret = ilbd_scf_retrieve_pg(scf_pgname, &scfpg, B_FALSE);
free(scf_pgname);
if (ret != ILB_STATUS_EEXIST)
return (ret);
assert(scfpg != NULL);
h = scf_pg_handle(scfpg);
if (h == NULL) {
ret = ILB_STATUS_EINVAL;
goto done;
}
if ((scfval = scf_value_create(h)) == NULL) {
ret = ILB_STATUS_ENOMEM;
goto done;
}
if (pg_type == ILBD_SCF_RULE) {
scftype = SCF_TYPE_BOOLEAN;
scf_value_set_boolean(scfval, *(boolean_t *)new_val);
} else if (pg_type == ILBD_SCF_SG) {
scftype = SCF_TYPE_ASTRING;
(void) scf_value_set_astring(scfval, (char *)new_val);
}
ret = ilbd_scf_set_prop(scfpg, prop_name, scftype, scfval);
done:
if (scf_pg_handle(scfpg) != NULL)
scf_handle_destroy(scf_pg_handle(scfpg));
if (scfpg != NULL)
scf_pg_destroy(scfpg);
if (scfval != NULL)
scf_value_destroy(scfval);
return (ret);
}
/*
* Update the persistent configuration with a new server, srv, added to a
* server group, sg.
*/
ilb_status_t
ilbd_scf_add_srv(ilbd_sg_t *sg, ilbd_srv_t *srv)
{
scf_propertygroup_t *pg;
scf_handle_t *h;
scf_value_t *val;
ilb_status_t ret;
int scf_name_len = ILBD_MAX_NAME_LEN;
char *buf = NULL;
if ((buf = malloc(scf_name_len)) == NULL)
return (ILB_STATUS_ENOMEM);
ilbd_name_to_scfpgname(ILBD_SCF_SG, sg->isg_name, buf);
ret = ilbd_scf_retrieve_pg(buf, &pg, B_FALSE);
/*
* The server group does not exist in persistent storage. This
* cannot happen. Should probably transition the service to
* maintenance since it should be there.
*/
if (ret != ILB_STATUS_EEXIST) {
logerr("ilbd_scf_add_srv: SCF update failed - entering"
" maintenance mode");
(void) smf_maintain_instance(ILB_FMRI, SMF_IMMEDIATE);
free(buf);
return (ILB_STATUS_INTERNAL);
}
if ((h = scf_pg_handle(pg)) == NULL) {
ilbd_scf_destroy(NULL, NULL, NULL, pg);
free(buf);
return (ilbd_scf_err_to_ilb_err());
}
if ((val = scf_value_create(h)) == NULL) {
ilbd_scf_destroy(h, NULL, NULL, pg);
free(buf);
return (ILB_STATUS_ENOMEM);
}
ilbd_srv_scf_val(srv, buf);
(void) scf_value_set_astring(val, buf);
(void) snprintf(buf, scf_name_len, "server%d", srv->isv_id);
ret = ilbd_scf_set_prop(pg, buf, SCF_TYPE_ASTRING, val);
free(buf);
ilbd_scf_destroy(h, NULL, NULL, pg);
scf_value_destroy(val);
return (ret);
}
/*
* Delete a server, srv, of a server group, sg, from the persistent
* configuration.
*/
ilb_status_t
ilbd_scf_del_srv(ilbd_sg_t *sg, ilbd_srv_t *srv)
{
ilb_status_t ret;
scf_propertygroup_t *pg;
scf_handle_t *h;
int scf_name_len = ILBD_MAX_NAME_LEN;
char *buf;
scf_transaction_t *tx = NULL;
scf_transaction_entry_t *entry = NULL;
if ((buf = malloc(scf_name_len)) == NULL)
return (ILB_STATUS_ENOMEM);
ilbd_name_to_scfpgname(ILBD_SCF_SG, sg->isg_name, buf);
ret = ilbd_scf_retrieve_pg(buf, &pg, B_FALSE);
/*
* The server group does not exist in persistent storage. This
* cannot happen. THe caller of this function puts service in
* maintenance mode.
*/
if (ret != ILB_STATUS_EEXIST) {
free(buf);
return (ILB_STATUS_INTERNAL);
}
ret = ILB_STATUS_OK;
if ((h = scf_pg_handle(pg)) == NULL) {
logdebug("ilbd_scf_del_srv: scf_pg_handle: %s\n",
scf_strerror(scf_error()));
ilbd_scf_destroy(NULL, NULL, NULL, pg);
free(buf);
return (ilbd_scf_err_to_ilb_err());
}
if ((tx = scf_transaction_create(h)) == NULL ||
(entry = scf_entry_create(h)) == NULL) {
logdebug("ilbd_scf_del_srv: create scf transaction failed: "
"%s\n", scf_strerror(scf_error()));
ret = ilbd_scf_err_to_ilb_err();
goto out;
}
(void) snprintf(buf, scf_name_len, "server%d", srv->isv_id);
if (scf_transaction_start(tx, pg) == -1) {
logdebug("ilbd_scf_set_prop: start scf transaction failed: "
"%s\n", scf_strerror(scf_error()));
ret = ilbd_scf_err_to_ilb_err();
goto out;
}
if (scf_transaction_property_delete(tx, entry, buf) == -1) {
logdebug("ilbd_scf_set_prop: delete property failed: %s\n",
scf_strerror(scf_error()));
ret = ilbd_scf_err_to_ilb_err();
goto out;
}
if (scf_transaction_commit(tx) != 1) {
logdebug("ilbd_scf_set_prop: commit transaction failed: %s\n",
scf_strerror(scf_error()));
ret = ilbd_scf_err_to_ilb_err();
}
out:
free(buf);
if (entry != NULL)
scf_entry_destroy(entry);
if (tx != NULL)
scf_transaction_destroy(tx);
ilbd_scf_destroy(h, NULL, NULL, pg);
return (ret);
}