/*
* 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 <strings.h>
#include <stddef.h>
#include <unistd.h>
#include <assert.h>
#include <errno.h>
#include <libintl.h>
#include <libilb.h>
#include "libilb_impl.h"
#include "ilbd.h"
/* until we all use AF_* macros ... */
static uint32_t i_flags_d2k(int);
#define ILB_SGSRV_2_KSRV(s, k) \
(k)->min_port = (s)->sgs_minport; \
(k)->max_port = (s)->sgs_maxport; \
(k)->err = 0; \
static ilb_algo_t
{
switch (a) {
case ILB_ALG_IMPL_ROUNDROBIN:
return (ILB_ALG_ROUNDROBIN);
case ILB_ALG_IMPL_HASH_IP:
return (ILB_ALG_HASH_IP);
return (ILB_ALG_HASH_IP_SPORT);
case ILB_ALG_IMPL_HASH_IP_VIP:
return (ILB_ALG_HASH_IP_VIP);
}
return (0);
}
static ilb_topo_t
{
switch (t) {
case ILB_TOPO_IMPL_DSR:
return (ILB_TOPO_DSR);
case ILB_TOPO_IMPL_NAT:
return (ILB_TOPO_NAT);
case ILB_TOPO_IMPL_HALF_NAT:
return (ILB_TOPO_HALF_NAT);
}
return (0);
}
{
switch (a) {
case ILB_ALG_ROUNDROBIN:
return (ILB_ALG_IMPL_ROUNDROBIN);
case ILB_ALG_HASH_IP:
return (ILB_ALG_IMPL_HASH_IP);
case ILB_ALG_HASH_IP_SPORT:
return (ILB_ALG_IMPL_HASH_IP_SPORT);
case ILB_ALG_HASH_IP_VIP:
return (ILB_ALG_IMPL_HASH_IP_VIP);
}
return (0);
}
{
switch (t) {
case ILB_TOPO_DSR:
return (ILB_TOPO_IMPL_DSR);
case ILB_TOPO_NAT:
return (ILB_TOPO_IMPL_NAT);
case ILB_TOPO_HALF_NAT:
return (ILB_TOPO_IMPL_HALF_NAT);
}
return (0);
}
/*
* Walk the list of rules and check if its safe to add the
* the server to the rule (this is a list of rules hanging
* off of a server group)
*/
{
return (ILB_STATUS_OK);
/* server has single port */
/*
* either we have a DSR rule with a port
* range, or both server and rule
* have single ports but their values
* don't match - this is incompatible
*/
break;
} else if (srv_minport != r_minport) {
break;
}
}
break;
}
} else if (srv_maxport > srv_minport) {
/* server has a port range */
if ((r_minport != srv_minport) ||
(r_maxport != srv_maxport)) {
/*
* we have a DSR rule with a port range
* and its min and max port values
* does not meet that of server's
* - this is incompatible
*/
break;
}
/*
* we have a DSR rule with a single
* port and a server with a port range
* - this is incompatible
*/
break;
if (rule_portrange != server_portrange) {
/*
* a port range and server with a port
* range and there is a mismatch in the
* sizes of the port ranges - this is
* incompatible
*/
break;
}
}
break;
}
}
}
return (rc);
}
void
i_setup_rule_hlist(void)
{
}
{
switch (scf_cmd) {
case ILBD_SCF_CREATE:
case ILBD_SCF_DESTROY:
case ILBD_SCF_ENABLE_DISABLE:
"status", &enable));
default:
return (ILB_STATUS_INVAL_CMD);
}
}
/*
* allocate a new daemon-specific rule from the "template" passed
* in in *r
*/
static ilbd_rule_t *
{
return (rl);
}
static ilbd_rule_t *
{
/* find position of rule in list */
}
return (rl);
}
/*
* get exactly one rule (named in rl->irl_name) data from kernel
*/
static ilb_status_t
{
if (rc != ILB_STATUS_OK)
return (rc);
return (ILB_STATUS_OK);
}
{
return (ILB_STATUS_ENOENT);
/*
* Check if the various timeout values are 0. If one is, get the
* default values from kernel.
*/
rinfo->rl_sticky_timeout == 0) {
if (rc != ILB_STATUS_OK)
return (rc);
if (rinfo->rl_conndrain == 0)
rinfo->rl_nat_timeout == 0) {
}
rinfo->rl_sticky_timeout == 0) {
}
}
*rbufsz += sizeof (ilb_rule_info_t);
return (ILB_STATUS_OK);
}
static ilb_status_t
{
/*
* as far as talking to the kernel is concerned, "all rules"
* is handled in one go somewhere else, so we only
* tell the kernel about single rules here.
*/
if (rc != ILB_STATUS_OK)
return (rc);
}
/*
* When dissociating a rule, only two errors can happen. The hc
* name is incorrect or the rule is not associated with the hc
* object. Both should not happen.... The check is for debugging
* purpose.
*/
logerr("ilbd_destroy_one_rule: cannot "
"dissociate %s from hc object %s: %d",
}
if (rc != ILB_STATUS_OK)
logdebug("ilbd_destroy_rule: save rule failed");
return (rc);
}
/*
* the following two functions are the other's opposite, and can
* call into each other for roll back purposes in case of error.
* To avoid endless recursion, the 'is_rollback' parameter must be
* set to B_TRUE in the roll back case.
*/
static ilb_status_t
{
/* no use sending a no-op to the kernel */
return (ILB_STATUS_OK);
/* "all rules" is handled in one go somewhere else, not here */
if (rc != ILB_STATUS_OK)
return (rc);
}
/* Undo the kernel work */
/* Cannot do much if ioctl fails... */
return (rc);
}
if (!is_rollback) {
if (rc == ILB_STATUS_OK)
if (rc != ILB_STATUS_OK)
/* ignore rollback return code */
}
return (rc);
}
static ilb_status_t
{
/* no use sending a no-op to the kernel */
return (ILB_STATUS_OK);
/* "all rules" is handled in one go somewhere else, not here */
if (rc != ILB_STATUS_OK)
return (rc);
}
/* Undo the kernel work */
/* Cannot do much if ioctl fails... */
return (rc);
}
if (!is_rollback) {
if (rc == ILB_STATUS_OK)
if (rc != ILB_STATUS_OK)
/* ignore rollback return code */
}
return (rc);
}
/*
* Generates an audit record for a supplied rule name
* Used for enable_rule, disable_rule, delete_rule,
* and create_rule subcommands
*/
static void
{
int audit_error;
/*
* we came here from the path where ilbd incorporates
* the configuration that is listed in SCF :
* i_ilbd_read_config->ilbd_walk_rule_pgs->
* ->ilbd_scf_instance_walk_pg->ilbd_create_rule
* We skip auditing in that case
*/
return;
}
logerr("ilbd_audit_rule_event: adt_start_session failed");
}
(void) adt_end_session(ah);
logerr("ilbd_audit_rule_event: adt_set_from_ucred failed");
}
if (cmd == ILBD_ENABLE_RULE)
else if (cmd == ILBD_DISABLE_RULE)
else if (cmd == ILBD_DESTROY_RULE)
else if (cmd == ILBD_CREATE_RULE)
logerr("ilbd_audit_rule_event: adt_alloc_event failed");
}
switch (cmd) {
case ILBD_DESTROY_RULE:
break;
case ILBD_ENABLE_RULE:
break;
case ILBD_DISABLE_RULE:
break;
case ILBD_CREATE_RULE:
logerr("ilbd_audit_rule_event: could not"
" allocate buffer");
}
/* Fill in virtual IP address type */
} else {
}
/* Fill in port - could be a single value or a range */
/* port range */
} else {
/* in audit record, max=min when single port */
}
/*
* Fill in protocol - if user does not specify it,
* its TCP by default
*/
else
/* Fill in algorithm and operation type */
/* Fill in proxy-src for the NAT case */
/* copy starting proxy-src address */
/* V4 case */
} else {
/* V6 case */
}
/* copy ending proxy-src address */
if (&rlinfo->rl_nat_src_end == 0) {
/* proxy-src is a single address */
event->
(void) memcpy(
(4 * sizeof (uint32_t)));
} else if (
/*
* proxy-src is a address range - copy ending
* proxy-src address
* V4 case
*/
} else {
/* V6 case */
}
}
/*
* Fill in pmask if user has specified one - 0 means
* no persistence
*/
valstr1[0] = '\0';
valstr1);
/* If there is a hcname */
/* Fill in hcport */
/* hcport is specified by user */
/* user has specified "ANY" */
}
/*
* Fill out the conndrain, nat_timeout and persist_timeout
* If the user does not specify them, the default value
* is set in the kernel. Userland does not know what
* the values are. So if the user
* does not specify these values they will show up as
* 0 in the audit record.
*/
/* Fill out servergroup and rule name */
break;
}
if (rc == ILB_STATUS_OK) {
logerr("ilbd_audit_rule_event:adt_put_event failed");
}
} else {
logerr("ilbd_audit_rule_event: adt_put_event failed");
}
}
(void) adt_end_session(ah);
}
/*
* converts IP address from in6_addr format to uint32_t[4]
* This conversion is needed for recording IP address in
* audit records.
*/
void
{
/* address is IPv4 */
} else {
/* address is IPv6 */
(4 * sizeof (uint32_t)));
}
}
static ilb_status_t
{
switch (cmd) {
case ILBD_DESTROY_RULE:
if (!is_rollback) {
}
return (rc);
case ILBD_ENABLE_RULE:
if (!is_rollback) {
}
return (rc);
case ILBD_DISABLE_RULE:
if (!is_rollback) {
}
return (rc);
}
return (ILB_STATUS_INVAL_CMD);
}
static ilb_cmd_t
{
ilb_cmd_t r;
switch (c) {
case ILBD_CREATE_RULE:
r = ILB_CREATE_RULE;
break;
case ILBD_DESTROY_RULE:
r = ILB_DESTROY_RULE;
break;
case ILBD_ENABLE_RULE:
r = ILB_ENABLE_RULE;
break;
case ILBD_DISABLE_RULE:
r = ILB_DISABLE_RULE;
break;
}
return (r);
}
static ilbd_cmd_t
{
switch (cmd) {
case ILBD_DESTROY_RULE:
break;
case ILBD_ENABLE_RULE:
break;
case ILBD_DISABLE_RULE:
break;
}
return (u_cmd);
}
static ilb_status_t
{
else
/* generate the audit record before bailing out */
if (rc != ILB_STATUS_OK) {
if (rule_name != '\0') {
} else {
"all");
ucredp);
}
goto out;
}
}
is_all_rules = rule_name[0] == 0;
/* just one rule */
if (!is_all_rules) {
goto out;
}
/* auditing will be done by i_ilbd_action_switch() */
goto out;
}
/* all rules: first tell the kernel, then walk the daemon's list */
if (rc != ILB_STATUS_OK) {
goto out;
}
/* auditing will be done by i_ilbd_action_switch() */
if (rc != ILB_STATUS_OK)
goto rollback_list;
}
return (rc);
if (u_cmd == ILBD_BAD_CMD)
return (rc);
if (is_all_rules) {
}
/* current list element failed, so we start with previous one */
if (is_all_rules)
/*
* When the processing of a command consists of
* multiple sequential steps, and one of them fails,
* ilbd performs rollback to undo the steps taken before the
* failing step. Since ilbd is initiating these steps
* there is not need to audit them.
*/
}
out:
return (rc);
}
{
}
{
}
{
}
/*
* allocate storage for a kernel rule command and fill from
* "template" irl, if non-NULL
*/
static ilb_rule_cmd_t *
{
return (kcmd);
}
return (kcmd);
}
/*
* ncount is the next to be used index into (*kcmdp)->servers
*/
static ilb_status_t
{
return (ILB_STATUS_OK);
/*
* the first ilb_server_info_t is part of *kcmd, so
* by using index (which is one less than the total needed) here,
* we allocate exactly the amount we need.
*/
return (ILB_STATUS_ENOMEM);
/*
* we don't count the slot we newly allocated yet.
*/
return (ILB_STATUS_OK);
}
/*
* this function adds all servers in srvlist to the kernel(!) rule
* the name of which is passed as argument.
*/
static ilb_status_t
{
int i;
/*
* If the servergroup doesn't have any servers associated with
* it yet, there's nothing more to do here.
*/
if (sg->isg_srvcount == 0)
return (ILB_STATUS_OK);
/*
* walk the list of servers attached to this SG
*/
if (rc != ILB_STATUS_OK)
goto rollback_kcmd;
/*
* "no port" means "copy rule's port" (for kernel rule)
*/
}
i++;
}
kcmd->num_servers = i;
if (rc != ILB_STATUS_OK)
goto rollback_kcmd;
for (i = 0; i < kcmd->num_servers; i++) {
int e;
logerr("i_update_ksrv_rules "
"ioctl indicates failure: %s", strerror(e));
rc = ilb_map_errno2ilbstat(e);
/*
* if adding even a single server failed, we need to
* roll back the whole wad. We ignore any errors and
* return the one that was returned by the first ioctl.
*/
goto rollback_kcmd;
}
}
return (rc);
}
/* convert a struct in6_addr to valstr */
void
{
void *addrptr;
logerr("ilbd_ip_to_str: inet_ntop failed");
return;
}
{
goto out;
}
logdebug("ilbd_create_rule: rule %s"
return (ILB_STATUS_DUP_RULE);
}
logdebug("ilbd_create_rule: rule %s uses non-existent"
return (ILB_STATUS_SGUNAVAIL);
}
return (rc);
}
/* allocs and copies contents of arg (if != NULL) into new rule */
return (ILB_STATUS_ENOMEM);
}
/* make sure rule's IPversion (via vip) and SG's match */
if (sg->isg_srvcount > 0) {
logdebug("address family mismatch with servergroup");
goto out;
}
}
/* Try associating the rule with the given hc oject. */
if (RULE_HAS_HC(irl)) {
goto out;
}
/*
* checks are done, now:
* 1. create rule in kernel
* 2. tell it about the backend server (which we maintain in SG)
* 3. attach the rule in memory
*/
/* 1. */
/* allocs and copies contents of arg (if != NULL) into new rule */
goto rollback_hc;
}
if (rc != ILB_STATUS_OK)
goto rollback_kcmd;
/* 2. */
if (rc != ILB_STATUS_OK)
goto rollback_kcmd;
/* 3. */
if (rc != ILB_STATUS_OK)
goto rollback_rule;
}
return (ILB_STATUS_OK);
/*
* ilbd_destroy_one_rule() also frees irl, as well as dissociate
* rule and HC, so all we need to do afterwards is free the kcmd
* and return.
*/
(void) ilbd_destroy_one_rule(irl);
return (rc);
/* Cannot fail since the rule is just associated with the hc object. */
if (RULE_HAS_HC(irl))
(void) ilbd_hc_dissociate_rule(irl);
out:
return (rc);
}
static uint32_t
i_flags_d2k(int f)
{
uint32_t r = 0;
if (ILB_IS_SRV_ENABLED(f))
r |= ILB_SERVER_ENABLED;
/* more as they are defined */
return (r);
}
/*
* walk the list of rules and add srv to the *kernel* rule
* (this is a list of rules hanging off of a server group)
*/
{
/*
* a note about rollback: since we need to start rollback with the
* current list element in some case, and with the previous one
* in others, we must "go back" in this latter case before
* we jump to the rollback code.
*/
/*
* sgs_minport == 0 means "no port specified"; this
* indicates that the server matches anything the rule
* provides.
* NOTE: this can be different for different rules
* using the same server group, therefore we don't modify
* this information in the servergroup, but *only* in
* the kernel's rule.
*/
if (srv->sgs_minport == 0) {
} else {
}
if (rc != ILB_STATUS_OK) {
logdebug("i_add_srv2krules: do_ioctl call failed");
goto rollback;
}
/*
* if ioctl() returns != 0, it doesn't perform the copyout
* necessary to indicate *which* server failed (we could be
* adding more than one); therefore we must check this
* 'err' field even if ioctl() returns 0.
*/
logerr("i_add_srv2krules: SIOCILB ioctl returned"
goto rollback;
}
if (RULE_HAS_HC(rl)) {
logerr("i_add_srv2krules: cannot start timer "
goto rollback;
}
}
}
return (rc);
/*
* this is almost, but not quite, the same as i_rem_srv_frm_krules()
* therefore we keep it seperate.
*/
if (RULE_HAS_HC(del_rl))
}
return (rc);
}
/*
* ev_port is only used for rollback purposes in this function
*/
{
if (rc != ILB_STATUS_OK) {
logdebug("i_rem_srv_frm_krules: do_ioctl"
"call failed");
goto rollback;
}
/*
* if ioctl() returns != 0, it doesn't perform the copyout
* necessary to indicate *which* server failed (we could be
* removing more than one); therefore we must check this
* 'err' field even if ioctl() returns 0.
*/
logerr("i_rem_srv_frm_krules: SIOCILB ioctl"
" returned error %s",
goto rollback;
}
if (RULE_HAS_HC(rl) &&
logerr("i_rem_srv_frm_krules: cannot delete "
goto rollback;
}
}
return (rc);
/* Don't do roll back if ev_port == -1. */
if (ev_port == -1)
return (rc);
if (srv->sgs_minport == 0) {
} else {
}
if (RULE_HAS_HC(add_rl))
}
return (rc);
}