/*
* 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
* 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
*/
/*
*/
#include <stdlib.h>
#include <stdio.h>
#include <strings.h>
#include <libilb.h>
#include <assert.h>
#include <libscf.h>
#include "libilb_impl.h"
#include "ilbd.h"
typedef enum {
typedef struct prop_tbl_entry {
const char *scf_propname;
/*
* 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
*/
/* entried for rule */
/* SCF_TYPE_NET_ADDR_V4 or SCF_TYPE_NET_ADDR_V6 */
/* add new rule related prop here */
/* entries for sg */
/* add new sg related prop here */
/* entries for hc */
/* add new hc related prop here */
};
scf_type_t, scf_value_t *);
scf_value_t **);
#define MIN(a, b) ((a) < (b) ? (a) : (b))
int
{
}
/*
* Translate libscf error to libilb status
*/
{
switch (scf_error()) {
case SCF_ERROR_NONE:
return (ILB_STATUS_OK);
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_NOT_SET:
case SCF_ERROR_TYPE_MISMATCH:
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);
return (ILB_STATUS_PERMIT);
return (ILB_STATUS_CALLBACK);
case SCF_ERROR_IN_USE:
return (ILB_STATUS_INUSE);
default:
return (ILB_STATUS_INTERNAL);
}
}
static void
char *scf_pgname)
{
switch (pg_type) {
case ILBD_SCF_RULE:
return;
case ILBD_SCF_SG:
return;
case ILBD_SCF_HC:
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",
return;
}
}
static void
{
if (s != NULL)
if (h != NULL)
}
static ilb_status_t
{
return (ILB_STATUS_INTERNAL);
if (scf_handle_bind(*h) != 0) {
return (ilbd_scf_err_to_ilb_err());
}
return (ilbd_scf_err_to_ilb_err());
}
NULL, SCF_DECODE_FMRI_EXACT) != 0) {
return (ilbd_scf_err_to_ilb_err());
}
return (ilbd_scf_err_to_ilb_err());
}
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
{
scf_handle_t *h;
if (ret != ILB_STATUS_OK)
return (ret);
*pg = scf_pg_create(h);
return (ILB_STATUS_INTERNAL);
if (scf_error() != SCF_ERROR_NOT_FOUND ||
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) {
return (ILB_STATUS_EEXIST);
}
/* found pg, need to create, destroy the existing one */
else
(void) ilbd_scf_delete_pg(*pg);
}
if (create) {
SCF_GROUP_APPLICATION, 0, *pg) != 0) {
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().
*/
return (ILB_STATUS_OK);
}
struct algo_tbl_entry {
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"}
};
sizeof (*algo_tbl))
void
{
int i;
for (i = 0; i < ILBD_ALGO_TBL_SIZE; i++) {
return;
}
}
logerr("ilbd_algo_to_str: algo not found");
}
static void
{
int i;
for (i = 0; i < ILBD_ALGO_TBL_SIZE; i++) {
return;
}
}
logerr("ilbd_scf_str_to_algo: algo not found");
}
struct topo_tbl_entry {
const char *topo_str;
} topo_tbl[] = {
{ILB_TOPO_DSR, "DSR"},
{ILB_TOPO_NAT, "NAT"},
{ILB_TOPO_HALF_NAT, "HALF-NAT"}
};
sizeof (*topo_tbl))
void
{
int i;
for (i = 0; i < ILBD_TOPO_TBL_SIZE; i++) {
return;
}
}
logerr("ilbd_scf_topo_to_str: topo not found");
}
static void
{
int i;
for (i = 0; i < ILBD_TOPO_TBL_SIZE; i++) {
return;
}
}
logerr("ilbd_scf_str_to_topo: topo not found");
}
static void
{
int ip_ver;
void *addrptr;
char *max_portstr;
}
logerr("ilbd_get_svr_field: inet_pton failed");
return;
}
} else {
sizeof (struct in6_addr));
}
if (max_portstr != NULL) {
}
}
/*
* 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
{
int ipver;
} else {
sizeof (ipstr));
}
}
/* get the "ip:port:status" str of the #num server in the servergroup */
{
int i;
return (ILB_STATUS_ENOENT);
for (i = 0; i < num; i++)
}
return (ILB_STATUS_OK);
}
/* convert a struct in6_addr to valstr */
{
void *addrptr;
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
int *numval)
{
int i;
int valint;
switch (pg_type) {
case ILBD_SCF_RULE:
break;
case ILBD_SCF_SG:
break;
case ILBD_SCF_HC:
break;
}
v = scf_value_create(h);
if (v == NULL)
return (ILB_STATUS_INTERNAL);
return (ILB_STATUS_ENOMEM);
switch (type) {
case ILBD_RULE_STATUS:
break;
case ILBD_RULE_VIP:
if (ret != ILB_STATUS_OK) {
return (ret);
}
break;
case ILBD_RULE_PROTO: {
break;
}
case ILBD_RULE_PORT:
break;
case ILBD_RULE_ALGO:
break;
case ILBD_RULE_TOPO:
break;
case ILBD_RULE_NAT_STR:
if (ret != ILB_STATUS_OK) {
return (ret);
}
break;
case ILBD_RULE_NAT_END:
if (ret != ILB_STATUS_OK) {
return (ret);
}
break;
case ILBD_RULE_STI_MASK:
if (ret != ILB_STATUS_OK) {
return (ret);
}
break;
case ILBD_RULE_SGNAME:
break;
case ILBD_RULE_HCNAME:
else
break;
case ILBD_RULE_HCPORT:
break;
case ILBD_RULE_HCPFLAG:
break;
case ILBD_RULE_DRAINTIME:
break;
case ILBD_RULE_NAT_TO:
break;
case ILBD_RULE_PERS_TO:
break;
case ILBD_SG_SERVER:
if (s_ent->isg_srvcount == 0) {
break;
}
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]);
return (ILB_STATUS_ENOMEM);
}
if (ret != ILB_STATUS_OK) {
for (i--; i >= 0; i--)
scf_value_destroy(varray[i]);
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 */
return (ret);
case ILBD_HC_TEST:
break;
case ILBD_HC_TIMEOUT:
break;
case ILBD_HC_INTERVAL:
break;
case ILBD_HC_DEF_PING:
break;
case ILBD_HC_COUNT:
break;
}
switch (*scftype) {
case SCF_TYPE_BOOLEAN:
break;
case SCF_TYPE_ASTRING:
(void) scf_value_set_astring(v, valstr);
break;
case SCF_TYPE_INTEGER:
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;
}
return (ILB_STATUS_ENOMEM);
}
varray[0] = v;
*numval = 1;
return (ret);
}
/*
* create a scf property group
*/
{
char *pgname;
scf_handle_t *h;
switch (pg_type) {
case ILBD_SCF_RULE: {
i_st = 0;
break;
}
case ILBD_SCF_SG: {
break;
}
case ILBD_SCF_HC: {
break;
}
default:
logdebug("ilbd_create_pg: invalid pg type %d for pg %s",
return (ILB_STATUS_EINVAL);
}
return (ILB_STATUS_ENOMEM);
if (ret != ILB_STATUS_OK) {
return (ret);
}
h = scf_pg_handle(pg);
/* fill in props */
int num, j;
if (ret != ILB_STATUS_OK)
goto done;
for (j = 0; j < num; j++) {
if (pg_type == ILBD_SCF_SG) {
scfpgbuf);
if (ret == ILB_STATUS_ENOENT) {
"EMPTY_SERVER", scf_name_len);
}
} else {
}
scf_value_destroy(val[j]);
}
}
done:
return (ret);
}
/*
* destroy a scf property group
*/
static ilb_status_t
{
if (scf_pg_delete(pg) != 0)
return (ilbd_scf_err_to_ilb_err());
return (ILB_STATUS_OK);
}
/* sg can have same name as rule */
{
char *scfname;
return (ILB_STATUS_ENOMEM);
if (ret != ILB_STATUS_EEXIST)
return (ret);
return (ret);
}
/*
* Set named property to scf value specified. If property is new,
* create it.
*/
static ilb_status_t
{
scf_handle_t *h = NULL;
int commit_ret;
h = scf_pg_handle(pg);
return (ILB_STATUS_EINVAL);
if (ret == ILB_STATUS_ENOENT)
else if (ret != ILB_STATUS_OK)
return (ret);
return (ilbd_scf_err_to_ilb_err());
logdebug("ilbd_scf_set_prop: create scf transaction failed\n");
goto out;
}
logdebug("ilbd_scf_set_prop: start scf transaction failed\n");
goto out;
}
if (new) {
proptype) == -1) {
logdebug("ilbd_scf_set_prop: create scf prop failed\n");
goto out;
}
} else {
== -1) {
logdebug("ilbd_scf_set_prop: change scf prop failed\n");
goto out;
}
}
logdebug("ilbd_scf_set_prop: add scf entry failed\n");
goto out;
}
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 */
break;
default:
break;
}
out:
return (ret);
}
/*
* get a prop's scf val
*/
static ilb_status_t
scf_value_t **val)
{
scf_handle_t *h = NULL;
h = scf_pg_handle(pg);
return (ILB_STATUS_EINVAL);
return (ilbd_scf_err_to_ilb_err());
goto out;
}
goto out;
}
goto out;
}
out:
return (ret);
}
typedef struct ilbd_data
{
union {
} data;
} ilbd_data_t;
void
{
void *addrptr;
} else {
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
{
int ret = 0;
char *valstr;
int ipversion;
switch (pg_type) {
case ILBD_SCF_RULE:
break;
case ILBD_SCF_HC:
break;
case ILBD_SCF_SG:
break;
}
/* get scf value out */
return (ILB_STATUS_ENOMEM);
switch (scf_type) {
case SCF_TYPE_NET_ADDR_V4:
return (ILB_STATUS_INTERNAL);
}
break;
case SCF_TYPE_NET_ADDR_V6:
INET6_ADDRSTRLEN) < 0) {
return (ILB_STATUS_INTERNAL);
}
break;
case SCF_TYPE_BOOLEAN:
return (ILB_STATUS_INTERNAL);
}
break;
case SCF_TYPE_ASTRING:
ILBD_MAX_VALUE_LEN) < 0) {
return (ILB_STATUS_INTERNAL);
}
break;
case SCF_TYPE_INTEGER:
return (ILB_STATUS_INTERNAL);
}
break;
default:
return (ILB_STATUS_INTERNAL);
}
ret = ILB_STATUS_OK;
switch (ilb_type) {
case ILBD_RULE_STATUS:
if (valbool)
break;
case ILBD_RULE_VIP:
break;
case ILBD_RULE_PROTO: {
break;
}
case ILBD_RULE_PORT: {
break;
}
case ILBD_RULE_ALGO:
break;
case ILBD_RULE_TOPO:
break;
case ILBD_RULE_NAT_STR:
break;
case ILBD_RULE_NAT_END:
break;
case ILBD_RULE_STI_MASK:
} else {
}
break;
case ILBD_RULE_SGNAME:
break;
case ILBD_RULE_HCNAME:
break;
case ILBD_RULE_HCPORT:
break;
case ILBD_RULE_HCPFLAG:
break;
case ILBD_RULE_DRAINTIME:
break;
case ILBD_RULE_NAT_TO:
break;
case ILBD_RULE_PERS_TO:
break;
case ILBD_SG_SERVER: {
/* 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"
*/
/* 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.
*/
break;
}
case ILBD_HC_TEST:
break;
case ILBD_HC_TIMEOUT:
break;
case ILBD_HC_INTERVAL:
break;
case ILBD_HC_DEF_PING:
break;
case ILBD_HC_COUNT:
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.
*/
} else {
}
break;
}
return (ret);
}
static ilbd_var_type_t
{
int i;
for (i = 0; i < ILBD_PROP_VAR_NUM; i++)
return (ILBD_VAR_INVALID);
}
/* callback for pg_walk_prop, arg is ilbd_data_t */
static ilb_status_t
{
scf_handle_t *h;
h = scf_pg_handle(pg);
if (h == NULL)
return (ILB_STATUS_EINVAL);
if (ret == ILB_STATUS_ENOENT)
return (ILB_STATUS_OK);
else if (ret != ILB_STATUS_OK)
return (ret);
/*
* Load value to ilb_data.
*/
out:
return (ret);
}
/*
* walk properties in one prop group, arg is ilbd_data
* cb is ilbd_scf_load_prop()
*/
static ilb_status_t
void *arg)
{
scf_handle_t *h;
h = scf_pg_handle(pg);
if (h == NULL)
return (ILB_STATUS_EINVAL);
prop = scf_property_create(h);
propiter = scf_iter_create(h);
goto out;
goto out;
goto out;
}
< 0) {
goto out;
}
if (ret != ILB_STATUS_OK)
break;
}
out:
if (scf_ret == -1)
return (ret);
}
/* cbs are libd_create_X */
static ilb_status_t
{
int scf_ret;
scf_handle_t *h;
return (ILB_STATUS_EINVAL);
h = scf_instance_handle(inst);
if (h == NULL)
return (ILB_STATUS_EINVAL);
return (ilbd_scf_err_to_ilb_err());
return (ilbd_scf_err_to_ilb_err());
}
goto out;
goto out;
}
goto out;
}
/*
* if pg name indicates it's a ilb configuration, walk its prop
*/
switch (pg_type) {
case ILBD_SCF_RULE:
strlen(ILBD_PG_NAME_RULE)) == 0) {
sizeof (ilb_rule_info_t));
goto out;
}
if (ret != ILB_STATUS_OK)
goto out;
/* set rule name */
if (ret != ILB_STATUS_OK)
goto out;
}
break;
case ILBD_SCF_SG:
strlen(ILBD_PG_NAME_SG)) == 0) {
sizeof (ilb_sg_info_t));
goto out;
}
if (ret != ILB_STATUS_OK) {
goto out;
}
/* set sg name */
if (ret != ILB_STATUS_OK) {
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 (ret != ILB_STATUS_OK) {
goto out;
}
}
}
break;
case ILBD_SCF_HC:
strlen(ILBD_PG_NAME_HC)) == 0) {
sizeof (ilb_hc_info_t));
goto out;
}
if (ret != ILB_STATUS_OK)
goto out;
/* set hc name */
if (ret != ILB_STATUS_OK)
goto out;
}
break;
}
}
out:
if (scf_ret < 0)
return (ret);
}
ucred_t *);
{
scf_handle_t *h;
if (ret != ILB_STATUS_OK)
return (ret);
/* get rule prop group, transfer it to ilb_lrule_info_t */
return (ret);
}
{
scf_handle_t *h;
if (ret != ILB_STATUS_OK)
return (ret);
return (ret);
}
{
scf_handle_t *h;
if (ret != ILB_STATUS_OK)
return (ret);
return (ret);
}
{
int ret;
scf_handle_t *h;
return (ILB_STATUS_ENOMEM);
if (ret != ILB_STATUS_EEXIST)
return (ret);
h = scf_pg_handle(scfpg);
if (h == NULL) {
goto done;
}
goto done;
}
if (pg_type == ILBD_SCF_RULE) {
} else if (pg_type == ILBD_SCF_SG) {
}
done:
return (ret);
}
/*
* Update the persistent configuration with a new server, srv, added to a
* server group, sg.
*/
{
scf_handle_t *h;
return (ILB_STATUS_ENOMEM);
/*
* 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");
return (ILB_STATUS_INTERNAL);
}
return (ilbd_scf_err_to_ilb_err());
}
return (ILB_STATUS_ENOMEM);
}
return (ret);
}
/*
* Delete a server, srv, of a server group, sg, from the persistent
* configuration.
*/
{
scf_handle_t *h;
char *buf;
return (ILB_STATUS_ENOMEM);
/*
* 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) {
return (ILB_STATUS_INTERNAL);
}
ret = ILB_STATUS_OK;
logdebug("ilbd_scf_del_srv: scf_pg_handle: %s\n",
scf_strerror(scf_error()));
return (ilbd_scf_err_to_ilb_err());
}
logdebug("ilbd_scf_del_srv: create scf transaction failed: "
goto out;
}
logdebug("ilbd_scf_set_prop: start scf transaction failed: "
goto out;
}
logdebug("ilbd_scf_set_prop: delete property failed: %s\n",
scf_strerror(scf_error()));
goto out;
}
logdebug("ilbd_scf_set_prop: commit transaction failed: %s\n",
scf_strerror(scf_error()));
}
out:
return (ret);
}