/*
* 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 <assert.h>
#include <errno.h>
#include <libilb.h>
#include "libilb_impl.h"
#include "ilbd.h"
typedef enum {
} srch_ind_t;
/* Last parameter to pass to i_find_srv(), specifying the matching mode */
void
i_setup_sg_hlist(void)
{
}
/*
* allocate storage for a daemon-internal server group, init counters
*/
static ilbd_sg_t *
{
goto out;
out:
return (d_sg);
}
static ilb_status_t
char *valstr)
{
switch (scf_cmd) {
case ILBD_SCF_CREATE:
case ILBD_SCF_DESTROY:
case ILBD_SCF_ENABLE_DISABLE:
return (ILB_STATUS_EINVAL);
default:
return (ILB_STATUS_EINVAL);
}
}
{
/* assert: the same rule is attached to any sg only once */
return (ILB_STATUS_OK);
}
static void
{
return;
sg->isg_srvcount--;
}
}
{
/* find position of sg in list */
return (sg);
}
return (sg);
}
/*
* Generates an audit record for enable-server, disable-server, remove-server
* delete-servergroup, create-servergroup and add-server subcommands.
*/
static void
{
int audit_error;
(cmd == ILBD_CREATE_SERVERGROUP))) {
/*
* We came here from the path where ilbd is
* incorporating the ILB configuration from
* SCF. In that case, we skip auditing
*/
return;
}
logerr("ilbd_audit_server_event: adt_start_session failed");
}
(void) adt_end_session(ah);
logerr("ilbd_audit_server_event: adt_set_from_ucred failed");
}
if (cmd == ILBD_ENABLE_SERVER)
else if (cmd == ILBD_DISABLE_SERVER)
else if (cmd == ILBD_REM_SERVER_FROM_GROUP)
else if (cmd == ILBD_ADD_SERVER_TO_GROUP)
else if (cmd == ILBD_CREATE_SERVERGROUP)
else if (cmd == ILBD_DESTROY_SERVERGROUP)
logerr("ilbd_audit_server_event: adt_alloc_event failed");
}
switch (cmd) {
case ILBD_ENABLE_SERVER:
(sizeof (data->ed_server_address)));
break;
case ILBD_DISABLE_SERVER:
(sizeof (data->ed_server_address)));
break;
(sizeof (data->ed_server_address)));
break;
case ILBD_CREATE_SERVERGROUP:
break;
case ILBD_ADD_SERVER_TO_GROUP:
(sizeof (data->ed_server_address)));
break;
case ILBD_DESTROY_SERVERGROUP:
break;
}
if (rc == ILB_STATUS_OK) {
logerr("ilbd_audit_server_event:"
" adt_put_event failed");
}
} else {
logerr("ilbd_audit_server_event:"
" adt_put_event failed");
}
}
(void) adt_end_session(ah);
}
{
if (rc != ILB_STATUS_OK) {
return (rc);
}
logdebug("ilbd_destroy_sg: cannot find specified server"
" group %s", sg_name);
return (ILB_STATUS_SGUNAVAIL);
}
/*
* we only destroy SGs that don't have any rules associated with
* them anymore.
*/
logdebug("ilbd_destroy_sg: server group %s has rules"
" associated with it and thus cannot be"
return (ILB_STATUS_SGINUSE);
}
if (rc != ILB_STATUS_OK) {
return (rc);
}
}
return (rc);
}
/* ARGSUSED */
/*
* Parameter ev_port is not used but has to have for read persistent configure
* ilbd_create_sg(), ilbd_create_hc() and ilbd_create_rule() are callbacks
* for ilbd_scf_instance_walk_pg() which requires the same signature.
*/
{
if (rc != ILB_STATUS_OK) {
return (rc);
}
}
logdebug("ilbd_create_sg: server group %s already exists",
return (ILB_STATUS_SGEXISTS);
}
return (ILB_STATUS_ENOMEM);
}
/*
* we've successfully created the sg in memory. Before we can
* return "success", we need to reflect this in persistent
* storage
*/
if (rc != ILB_STATUS_OK) {
return (rc);
}
}
return (rc);
}
/*
* and does so if possible.
* We keep the list in sorted order so we don't have to search it
* in its entirety for overlap every time we insert a new server.
* Return code:
* stop_found: don't continue searching because we found a place
* cont_search: continue with next element in the list
* fail_search: search failed (caller translates to ILB_STATUS_EEXIST)
*/
static srch_ind_t
{
int fnd;
return (cont_search); /* search can continue */
if (fnd == 0) {
logdebug("i_test_and_insert: specified server already exists");
return (fail_search);
}
/* the list is kept in ascending order */
return (stop_found);
}
/*
* copy a server description [ip1,ip2,port1,port2,srvID,flags]
*/
static ilb_status_t
{
return (ILB_STATUS_ENOMEM);
/*
* port info is in network byte order - we need host byte order
* for comparisons purposes
*/
} else {
srvlist);
if (search != cont_search)
break;
/* if reaches the end of list, insert to the tail */
}
if (search == fail_search)
}
if (rc == ILB_STATUS_OK) {
dsg->isg_srvcount++;
} else {
}
return (rc);
}
/*
* Allocate a server ID. The algorithm is simple. Just check the ID array
* of the server group and find an unused ID. If *set_id is given, it
* means that the ID is already allocated and the ID array needs to be
* updated. This is the case when ilbd reads from the persistent
* configuration.
*/
static int32_t
{
int32_t i;
/* The server ID is already allocated, just update the ID array. */
return (*set_id);
}
/* if we're "full up", give back something invalid */
return (BAD_SRVID);
i = sg->isg_max_id;
break;
}
return (sg->isg_max_id);
}
/*
* Free a server ID by updating the server group's ID array.
*/
static void
{
}
/*
* This function is called by ilbd_add_server_to_group() and
* ilb_remove_server_group() to create a audit record for a
* failed servicing of add-server/remove-server command
*/
static void
{
int i;
for (i = 0; i < sg->sg_srvcount; i++) {
if (cmd == ILBD_ADD_SERVER_TO_GROUP) {
} else {
}
} else if (cmd == ILBD_REM_SERVER_FROM_GROUP) {
audit_sg_data->ed_minport = 0;
audit_sg_data->ed_maxport = 0;
}
}
}
/*
* the name(s) of the server(s) are encoded in the sg.
*/
{
int i, j;
if (rc != ILB_STATUS_OK) {
return (rc);
}
}
logdebug("ilbd_add_server_to_group: server"
return (ILB_STATUS_ENOENT);
}
/*
* we do the dance with address family below to make sure only
* IP addresses in the same AF get into an SG; the first one to get
* in sets the "tone"
* if this is the first server to join a group, check whether
* there's no mismatch with any *rules* already attached
*/
if (tmp_sg->isg_srvcount > 0) {
} else {
}
for (i = 0; i < sg_info->sg_srvcount; i++) {
} else {
}
/* only test if we have sth to test against */
logdebug("address family mismatch with previous"
" hosts in servergroup or with rule");
goto rollback;
}
}
/*
* PS: NULL means daemon is loading configure from scf.
* ServerID is already assigned, just update the ID array.
*/
logdebug("ilbd_add_server_to_group: server"
"group %s is full, no more servers"
goto rollback;
}
} else {
}
/*
* here we implement the requirement that server IDs start
* with a character that is not legal in hostnames - in our
* case, a "_" (underscore).
*/
/*
* Before we update the kernel rules by adding the server,
* we need to make checks and fail if any of the
* following is true:
*
* o if the server has single port and the servergroup
* is associated to a DSR rule with a port range
* o if the server has a port range and the servergroup
* is associated to a DSR rule with a port range and
* the rule's min and max port does not exactly
* match that of the server's.
* o if the the server has a port range and the servergroup
* and the rule's port range size does not match that
* of the server's.
* o if the rule has a fixed hc port, check that this port
* is valid in the server's port specification.
*/
if (rc != ILB_STATUS_OK) {
goto rollback;
}
goto rollback;
}
ev_port);
if (rc != ILB_STATUS_OK) {
/*
* The failure may be due to the serverid being on
* hold in kernel for connection draining. But ilbd
* has no way of knowing that. So we are freeing up
* the serverid, and may run into the risk of
* having this failure again, if we choose this
* serverid when processing the next add-server
* command for this servergroup, while connection
* draining is underway. We assume that the user
* this failure, and learn to not add any server
* to the servergroup until connection draining of
* all servers in the servergroup is complete.
* XXX Need to revisit this when connection draining
* is reworked
*/
tmp_sg->isg_srvcount--;
goto rollback;
}
if (rc != ILB_STATUS_OK) {
/*
* The following should not fail since the
* server is just added. Just in case, we
* pass in -1 as the event port to avoid
* roll back in i_rem_srv_frm_krules() called
* by i_delete_srv().
*/
break;
}
}
}
if (rc == ILB_STATUS_OK) {
return (rc);
}
/*
* If ilbd is initializing based on the SCF data and something fails,
* the only choice is to transition the service to maintanence mode...
*/
logerr("%s: failure during initialization -"
" entering maintenance mode", __func__);
return (rc);
}
/*
* we need to roll back all servers previous to the one
* that just caused the failure
*/
for (j = i-1; j >= 0; j--) {
/* We should be able to find those servers just added. */
}
return (rc);
}
static srch_ind_t
{
return (stop_found);
}
return (cont_search);
}
/*
* Sanity check on a rule's port specification against all the servers'
* specification in its associated server group.
*
* 1. If the health check's probe port (hcport) is specified.
* - if server port range is specified, check if hcport is inside
* the range
* - if no server port is specified (meaning the port range is the same as
* the rule's port range), check if hcport is inside the rule's range.
*
* 2. If a server has no port specification, there is no conflict.
*
* 3. If the rule's load balance mode is DSR, a server port specification must
* be exactly the same as the rule's.
*
* 4. In other modes (NAT and half-NAT), the server's port range must be
* the same as the rule's, unless it is doing port collapsing (the server's
* port range is only 1).
*/
{
/* Don't allow adding a rule to a sg with no server, for now... */
if (sg->isg_srvcount == 0)
return (ILB_STATUS_SGEMPTY);
int range;
/*
* If the rule has a specific probe port, check if that port is
* valid in all the servers' port specification.
*/
/* No server port specified. */
if (srv_minport == 0) {
return (ILB_STATUS_BADSG);
}
} else {
if (hcport > srv_maxport ||
hcport < srv_minport) {
return (ILB_STATUS_BADSG);
}
}
}
/*
* There is no server port specification, so there cannot be
* any conflict.
*/
if (srv_minport == 0)
continue;
if (r_minport != srv_minport ||
r_maxport != srv_maxport) {
return (ILB_STATUS_BADSG);
}
} else {
return (ILB_STATUS_BADSG);
}
}
return (ILB_STATUS_OK);
}
static srch_ind_t
{
return (stop_found);
return (cont_search);
}
static ilbd_srv_t *
{
switch (cmpmode) {
case MODE_ADDR:
break;
case MODE_SRVID:
break;
}
if (srch_res == stop_found)
break;
}
if (srch_res == stop_found)
return (tmp_srv);
return (NULL);
}
static ilb_status_t
{
if (rc != ILB_STATUS_OK)
return (rc);
sg->isg_srvcount--;
return (ILB_STATUS_OK);
}
/*
* some people argue that returning anything here is
* something anyway?
*/
{
if (rc != ILB_STATUS_OK) {
return (rc);
}
return (ILB_STATUS_SGUNAVAIL);
}
return (ILB_STATUS_SRVUNAVAIL);
}
} else {
}
/*
* i_delete_srv frees srv, therefore we need to save
* this information for ilbd_scf_del_srv
*/
if (rc != ILB_STATUS_OK) {
return (rc);
}
logerr("%s: SCF update failed - entering maintenance"
" mode", __func__);
}
}
return (rc);
}
{
tmp_rbufsz = *rbufsz;
/* Set up the reply buffer. rbufsz will be set to the new size. */
/* Calculate how much space is left for holding name info. */
*rbufsz += sizeof (ilbd_namelist_t);
tmp_rbufsz -= *rbufsz;
nlist->ilbl_count = 0;
switch (cmd) {
case ILBD_RETRIEVE_SG_NAMES: {
tmp_rbufsz -= sizeof (ilbd_name_t)) {
}
break;
}
case ILBD_RETRIEVE_RULE_NAMES: {
extern list_t ilbd_rule_hlist;
tmp_rbufsz -= sizeof (ilbd_name_t)) {
}
break;
}
case ILBD_RETRIEVE_HC_NAMES: {
extern list_t ilbd_hc_list;
}
break;
}
default:
logdebug("ilbd_retrieve_names: unknown command");
return (ILB_STATUS_INVAL_CMD);
}
return (rc);
}
{
logdebug("ilbd_retrieve_sg_hosts: server group"
" %s not found", sg_name);
return (ILB_STATUS_SGUNAVAIL);
}
tmp_rbufsz = *rbufsz;
/* Calculate the size to hold all the hosts info. */
*rbufsz += sizeof (ilb_sg_info_t);
tmp_rbufsz -= *rbufsz;
sg_info->sg_srvcount = 0;
tmp_rbufsz -= sizeof (ilb_sg_srv_t);
}
return (ILB_STATUS_OK);
}
/*
* this mapping function works on the assumption that HC only is
* active when a server is enabled.
*/
static ilb_cmd_t
{
switch (dcmd) {
case stat_enable_server:
case stat_declare_srv_alive:
break;
case stat_disable_server:
case stat_declare_srv_dead:
break;
}
return (cmd);
}
{
int e;
if (rc != ILB_STATUS_OK)
return (rc);
logdebug("ilbd_k_Xable_server: error %s occurred",
strerror(e));
return (ilb_map_errno2ilbstat(e));
}
return (rc);
}
static ilb_status_t
{
char *dot;
void *addrptr;
/* we currently only implement a "list" of one */
if (rc != ILB_STATUS_OK) {
return (rc);
}
switch (cmd) {
case stat_disable_server:
break;
case stat_enable_server:
break;
}
return (ILB_STATUS_EINVAL);
}
/*
* the following asserts that serverIDs are constructed
* along the pattern "_"<SG name>"."<number>
* so we look for the final "." to recreate the SG name.
*/
switch (cmd) {
case stat_disable_server:
break;
case stat_enable_server:
break;
}
return (ILB_STATUS_EINVAL);
}
/* make the non-sg_name part "invisible" */
*dot = '\0';
switch (cmd) {
case stat_disable_server:
break;
case stat_enable_server:
break;
}
return (ILB_STATUS_ENOENT);
}
switch (cmd) {
case stat_disable_server:
break;
case stat_enable_server:
break;
}
return (ILB_STATUS_ENOENT);
}
/*
* if server's servergroup is not associated with
* a rule, do not enable it.
*/
switch (cmd) {
case stat_disable_server:
break;
case stat_enable_server:
break;
}
return (ILB_STATUS_INVAL_ENBSRVR);
}
/* Fill in the server IP address for audit record */
} else {
}
/*
* We have found the server in memory, perform the following
* tasks.
*
* 1. For every rule associated with this SG,
* - tell the kernel
* - tell the hc
* 2. Update our internal state and persistent configuration
* if the new state is not the same as the old one.
*/
/* 1. */
if (rc != ILB_STATUS_OK) {
switch (cmd) {
case stat_disable_server:
break;
case stat_enable_server:
break;
}
goto rollback_rules;
}
if (!RULE_HAS_HC(irl))
continue;
if (cmd == stat_disable_server) {
} else {
}
if (rc != ILB_STATUS_OK) {
logdebug("ilbd_Xable_server: cannot toggle srv "
"timer, rc =%d, srv =%s%d\n", rc,
}
}
/* 2. */
if ((cmd == stat_disable_server &&
(cmd == stat_enable_server &&
switch (cmd) {
case stat_disable_server:
break;
case stat_enable_server:
break;
}
return (ILB_STATUS_OK);
}
if (cmd == stat_enable_server)
else
logerr("ilbd_Xable_server: failed transfer ip addr to"
" str");
else
switch (cmd) {
case stat_disable_server:
break;
case stat_enable_server:
break;
}
goto rollback_rules;
}
return (ILB_STATUS_ENOMEM);
return (ILB_STATUS_ENOMEM);
}
switch (cmd) {
case stat_disable_server:
if (rc == ILB_STATUS_OK)
break;
case stat_enable_server:
if (rc == ILB_STATUS_OK)
break;
}
if (rc == ILB_STATUS_OK) {
switch (cmd) {
case stat_disable_server:
break;
case stat_enable_server:
break;
}
return (ILB_STATUS_OK);
}
if (cmd == stat_disable_server)
else
else
if (!RULE_HAS_HC(irl))
continue;
if (u_cmd == stat_disable_server)
else
}
return (rc);
}
{
}
{
}
/*
* fill in the srvID for the given IP address in the 0th server
*/
{
*rbufsz += sizeof (ilb_sg_srv_t);
return (ILB_STATUS_SGUNAVAIL);
return (ILB_STATUS_ENOENT);
return (ILB_STATUS_OK);
}
/*
* fill in the address for the given serverID in the 0th server
*/
{
return (ILB_STATUS_SGUNAVAIL);
return (ILB_STATUS_ENOENT);
*rbufsz += sizeof (ilb_sg_srv_t);
return (ILB_STATUS_OK);
}
/*
* Map ilb_status errors to similar errno values from errno.h or
* adt_event.h to be used for audit record
*/
int
{
int audit_error;
switch (rc) {
case ILB_STATUS_CFGAUTH:
break;
case ILB_STATUS_ENOMEM:
break;
case ILB_STATUS_ENOENT:
case ILB_STATUS_ENOHCINFO:
case ILB_STATUS_INVAL_CMD:
case ILB_STATUS_DUP_RULE:
case ILB_STATUS_ENORULE:
case ILB_STATUS_SGUNAVAIL:
break;
case ILB_STATUS_EINVAL:
case ILB_STATUS_MISMATCHSG:
case ILB_STATUS_MISMATCHH:
case ILB_STATUS_BADSG:
case ILB_STATUS_INVAL_SRVR:
case ILB_STATUS_INVAL_ENBSRVR:
case ILB_STATUS_BADPORT:
break;
case ILB_STATUS_EEXIST:
case ILB_STATUS_SGEXISTS:
break;
case ILB_STATUS_EWOULDBLOCK:
break;
case ILB_STATUS_INPROGRESS:
break;
case ILB_STATUS_INTERNAL:
case ILB_STATUS_CALLBACK:
case ILB_STATUS_PERMIT:
case ILB_STATUS_RULE_NO_HC:
break;
case ILB_STATUS_SOCKET:
break;
case ILB_STATUS_READ:
case ILB_STATUS_WRITE:
break;
case ILB_STATUS_SGINUSE:
break;
case ILB_STATUS_SEND:
audit_error = ECOMM;
break;
case ILB_STATUS_SGFULL:
break;
case ILB_STATUS_NAMETOOLONG:
break;
case ILB_STATUS_SRVUNAVAIL:
break;
default:
break;
}
return (audit_error);
}