#include <stdlib.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <inttypes.h>
#include <assert.h>
#include <libilb.h>
#include <libilb_impl.h>
#include <locale.h>
typedef enum {
internal,
external
} ip_addr_type_t;
static int
sign64(int64_t n)
{
if (n >= 0)
return (1);
return (-1);
}
static int
sign32(int32_t n)
{
if (n >= 0)
return (1);
return (-1);
}
static int64_t
signed_diff64(uint64_t x, uint64_t y)
{
uint64_t ud;
int s = -1;
if (x == y)
return (0);
if (x > y) {
uint64_t t;
s = 1;
t = x; x = y; y = t;
}
ud = y - x;
if (ud > INT64_MAX)
return (INT64_MAX * s);
return ((int64_t)ud * s);
}
static uint64_t
unsigned_diff64(uint64_t x, uint64_t y, int *sgn)
{
int s = -1;
if (x == y)
return (0);
if (x > y) {
uint64_t t;
s = 1;
t = x; x = y; y = t;
}
*sgn = s;
return (y - x);
}
static int
i_cmp_addr_impl(void *ip1, void *ip2, ip_addr_type_t atype, int64_t *diff)
{
struct in6_addr *a6_1, *a6_2;
uint32_t i1, i2;
uint32_t l1, l2;
int af, sgn;
int64_t d;
if (atype == internal) {
af = GET_AF((struct in6_addr *)ip1);
if (af == AF_INET) {
IN6_V4MAPPED_TO_IPADDR((struct in6_addr *)ip1, i1);
IN6_V4MAPPED_TO_IPADDR((struct in6_addr *)ip2, i2);
l1 = ntohl(i1);
l2 = ntohl(i2);
} else {
a6_1 = (struct in6_addr *)ip1;
a6_2 = (struct in6_addr *)ip2;
}
} else {
af = ((ilb_ip_addr_t *)ip1)->ia_af;
if (af == AF_INET) {
struct in_addr *a1, *a2;
a1 = &((ilb_ip_addr_t *)ip1)->ia_v4;
a2 = &((ilb_ip_addr_t *)ip2)->ia_v4;
l1 = ntohl((uint32_t)a1->s_addr);
l2 = ntohl((uint32_t)a2->s_addr);
} else {
a6_1 = &((ilb_ip_addr_t *)ip1)->ia_v6;
a6_2 = &((ilb_ip_addr_t *)ip2)->ia_v6;
}
}
if (af == AF_INET) {
d = l1 - l2;
sgn = sign32((int32_t)d);
} else {
uint64_t i1h, i1l;
uint64_t i2h, i2l;
uint64_t dl;
int64_t dh;
int l_sign;
i1h = INV6_N2H_MSB64(a6_1);
i1l = INV6_N2H_LSB64(a6_1);
i2h = INV6_N2H_MSB64(a6_2);
i2l = INV6_N2H_LSB64(a6_2);
dh = signed_diff64(i1h, i2h);
dl = unsigned_diff64(i1l, i2l, &l_sign);
if (dh == 0) {
if (dl > INT64_MAX)
dl = INT64_MAX;
d = dl * l_sign;
} else if (l_sign == sign64(dh) || abs(dh) > 1) {
if (dh > 0)
d = INT64_MAX;
else
d = -INT64_MAX;
} else {
if (dl < INT64_MAX) {
d = INT64_MAX;
} else {
if (dh == 1)
d = UINT64_MAX - dl + 1;
else
d = -INT64_MAX - (dl - INT64_MAX) - 1;
}
}
sgn = sign64(d);
}
if (diff != NULL)
*diff = d;
if (d == 0)
return (0);
return (sgn);
}
int
ilb_cmp_in6_addr(struct in6_addr *ip1, struct in6_addr *ip2, int64_t *diff)
{
int res;
res = i_cmp_addr_impl(ip1, ip2, internal, diff);
return (res);
}
int
ilb_cmp_ipaddr(ilb_ip_addr_t *ip1, ilb_ip_addr_t *ip2, int64_t *diff)
{
int res;
res = i_cmp_addr_impl(ip1, ip2, external, diff);
return (res);
}
const char *
ilb_errstr(ilb_status_t rc)
{
switch (rc) {
case ILB_STATUS_OK:
return (dgettext(TEXT_DOMAIN, "no error"));
case ILB_STATUS_INTERNAL:
return (dgettext(TEXT_DOMAIN, "error internal to the library"));
case ILB_STATUS_EINVAL:
return (dgettext(TEXT_DOMAIN, "invalid argument(s) - see"
" man page"));
case ILB_STATUS_ENOMEM:
return (dgettext(TEXT_DOMAIN, "not enough memory"
" for operation"));
case ILB_STATUS_ENOENT:
return (dgettext(TEXT_DOMAIN, "no such/no more element(s)"));
case ILB_STATUS_SOCKET:
return (dgettext(TEXT_DOMAIN, "socket() failed"));
case ILB_STATUS_READ:
return (dgettext(TEXT_DOMAIN, "read() failed"));
case ILB_STATUS_WRITE:
return (dgettext(TEXT_DOMAIN, "fflush() or send() failed"));
case ILB_STATUS_TIMER:
return (dgettext(TEXT_DOMAIN, "health check timer"
" create/setup error"));
case ILB_STATUS_INUSE:
return (dgettext(TEXT_DOMAIN, "object is in use,"
" cannot destroy"));
case ILB_STATUS_EEXIST:
return (dgettext(TEXT_DOMAIN, "object already exists"));
case ILB_STATUS_PERMIT:
return (dgettext(TEXT_DOMAIN, "no scf permit"));
case ILB_STATUS_CALLBACK:
return (dgettext(TEXT_DOMAIN, "scf callback error"));
case ILB_STATUS_INPROGRESS:
return (dgettext(TEXT_DOMAIN, "operation is progress"));
case ILB_STATUS_SEND:
return (dgettext(TEXT_DOMAIN, "send() failed"));
case ILB_STATUS_ENOHCINFO:
return (dgettext(TEXT_DOMAIN, "missing healthcheck info"));
case ILB_STATUS_INVAL_HCTESTTYPE:
return (dgettext(TEXT_DOMAIN, "invalid health check"
" test type"));
case ILB_STATUS_INVAL_CMD:
return (dgettext(TEXT_DOMAIN, "invalid command"));
case ILB_STATUS_DUP_RULE:
return (dgettext(TEXT_DOMAIN, "specified rule name already"
" exists"));
case ILB_STATUS_ENORULE:
return (dgettext(TEXT_DOMAIN, "specified rule does not exist"));
case ILB_STATUS_MISMATCHSG:
return (dgettext(TEXT_DOMAIN, "address family mismatch with"
" servergroup"));
case ILB_STATUS_MISMATCHH:
return (dgettext(TEXT_DOMAIN, "address family mismatch"
" with previous hosts in servergroup or with rule"));
case ILB_STATUS_SGUNAVAIL:
return (dgettext(TEXT_DOMAIN, "cannot find specified"
" server group"));
case ILB_STATUS_SGINUSE:
return (dgettext(TEXT_DOMAIN, "cannot remove server"
" group - its in use with other active rules"));
case ILB_STATUS_SGEXISTS:
return (dgettext(TEXT_DOMAIN, "servergroup already exists"));
case ILB_STATUS_SGFULL:
return (dgettext(TEXT_DOMAIN, "servergroup is full - cannot"
" add any more servers to this servergroup"));
case ILB_STATUS_SGEMPTY:
return (dgettext(TEXT_DOMAIN, "servergroup does not contain"
" any servers"));
case ILB_STATUS_NAMETOOLONG:
return (dgettext(TEXT_DOMAIN, "servergroup name can"
" only contain a maximum of 14 characters"));
case ILB_STATUS_CFGAUTH:
return (dgettext(TEXT_DOMAIN, "user is not authorized to"
" execute command"));
case ILB_STATUS_CFGUPDATE:
return (dgettext(TEXT_DOMAIN, "a failure occurred while trying"
" to update persistent config. Panic?"));
case ILB_STATUS_BADSG:
return (dgettext(TEXT_DOMAIN, "the rule's port range"
" does not match that of the servers' in associated"
" servergroup"));
case ILB_STATUS_INVAL_SRVR:
return (dgettext(TEXT_DOMAIN, "server cannot be added to the"
" servergroup, as the servergroup is associated to rule(s)"
" with port/port range that is incompatible"
"with the server's port"));
case ILB_STATUS_INVAL_ENBSRVR:
return (dgettext(TEXT_DOMAIN, "server cannot be enabled"
" because it's not associated with any rule"));
case ILB_STATUS_BADPORT:
return (dgettext(TEXT_DOMAIN, "the rule's port value does"
" not match that of the servers' in"
" associated servergroup"));
case ILB_STATUS_SRVUNAVAIL:
return (dgettext(TEXT_DOMAIN, "cannot find specified server"));
case ILB_STATUS_RULE_NO_HC:
return (dgettext(TEXT_DOMAIN, "rule does not have health "
"check enabled"));
case ILB_STATUS_RULE_HC_MISMATCH:
return (dgettext(TEXT_DOMAIN, "protocol used in rule and "
"health check does not match"));
case ILB_STATUS_HANDLE_CLOSING:
return (dgettext(TEXT_DOMAIN, "handle is being closed"));
default:
return (dgettext(TEXT_DOMAIN, "unknown error"));
}
}
ilb_comm_t *
i_ilb_alloc_req(ilbd_cmd_t cmd, size_t *ic_sz)
{
ilb_comm_t *ic;
size_t sz;
sz = sizeof (ilb_comm_t);
switch (cmd) {
case ILBD_CREATE_RULE:
sz += sizeof (ilb_rule_info_t);
break;
case ILBD_RETRIEVE_RULE:
case ILBD_DESTROY_RULE:
case ILBD_ENABLE_RULE:
case ILBD_DISABLE_RULE:
case ILBD_RETRIEVE_SG_HOSTS:
case ILBD_DESTROY_SERVERGROUP:
case ILBD_CREATE_SERVERGROUP:
case ILBD_DESTROY_HC:
case ILBD_GET_HC_INFO:
case ILBD_GET_HC_SRVS:
sz += sizeof (ilbd_name_t);
break;
case ILBD_ENABLE_SERVER:
case ILBD_DISABLE_SERVER:
case ILBD_ADD_SERVER_TO_GROUP:
case ILBD_REM_SERVER_FROM_GROUP:
case ILBD_SRV_ADDR2ID:
case ILBD_SRV_ID2ADDR:
sz += sizeof (ilb_sg_info_t) + sizeof (ilb_sg_srv_t);
break;
case ILBD_CREATE_HC:
sz += sizeof (ilb_hc_info_t);
break;
default:
assert(0);
break;
}
if ((ic = calloc(1, sz)) == NULL)
return (NULL);
*ic_sz = sz;
ic->ic_cmd = cmd;
ic->ic_flags = 0;
return (ic);
}