ilbadm_subr.c revision dbed73cbda2229fd1aa6dc5743993cae7f0a7ee9
/*
* 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
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <strings.h>
#include <netdb.h>
#include <errno.h>
#include <ctype.h>
#include <assert.h>
#include <limits.h>
#include <libilb.h>
#include <libilb_impl.h>
#include "ilbadm.h"
#define PORT_SEP ':'
typedef enum {
numeric = 1,
} addr_type_t;
ilbadm_val_type_t algo_types[] = {
};
ilbadm_val_type_t topo_types[] = {
};
void
{
int len;
case AF_INET:
buf[0] = '\0';
else
break;
case AF_INET6:
buf[0] = '\0';
break;
}
if (!(flags & V6_ADDRONLY))
*buf++ = '[';
sz--;
if (!(flags & V6_ADDRONLY)) {
}
break;
default: buf[0] = '\0';
}
}
char *
{
break;
}
/* we return this in all cases */
return (v->v_name);
}
int
{
break;
}
/* we return this in all cases */
return (v->v_type);
}
{
break;
}
}
/*
* try to match:
* 1) IPv4 address
* 2) IPv6 address
* 3) a hostname
*/
static ilbadm_status_t
{
/*
* if *a_type == numeric, we only want to check whether this
* is a (valid) numeric IP address. If we do and it is NOT,
* we return _ENOENT.
*/
return (ILBADM_INVAL_ADDR);
at = non_numeric;
return (ILBADM_INVAL_ADDR);
}
case AF_INET: {
struct sockaddr_in sa;
break;
}
case AF_INET6: {
struct sockaddr_in6 sa;
break;
}
default:
return (ILBADM_INVAL_AF);
break;
}
return (ILBADM_OK);
}
static ilbadm_status_t
{
/*
* we shouldn't need to check for length here, as a name that's
* too long won't exist in the system anyway.
*/
return (ILBADM_OK);
}
static struct in_addr
{
if (dir == 1)
iah++;
else
iah--;
return (new_in);
}
static ilbadm_status_t
{
}
return (ILBADM_OK);
}
static struct in6_addr
{
ah = INV6_N2H_MSB64(a);
al = INV6_N2H_LSB64(a);
if (dir == 1) {
/* overflow */
if (++al == 0)
ah++;
} else {
/* underflow */
if (--al == 0xffffffff)
ah--;
}
return (ia6);
}
static ilbadm_status_t
{
}
return (ILBADM_OK);
}
/*
* we create a list node in the servergroup for every ip address
* in the range [ip1, ip2], where we interpret the ip addresses as
* numbers
* the first ip address is already stored in "sn"
*/
static ilbadm_status_t
{
int cmp;
return (ILBADM_OK);
return (ILBADM_LIBERR);
}
/* if ip addresses are the same, we're done */
return (ILBADM_OK);
if (cmp == 1) {
" than ending ip address in ip range specification"));
return (ILBADM_LIBERR);
}
/* if the implicit number of IPs is too large, stop */
return (ILBADM_TOOMANYIPADDR);
/* not reached */
break;
/* not reached */
break;
}
return (ILBADM_INVAL_AF);
}
/*
* parse a port spec (number or by service name) and
* return the numeric port in *host* byte order
*
* Upon return, *flags contains ILB_FLAGS_SRV_PORTNAME if a service name matches
*/
static int
{
/* assumption: port names start with a non-digit */
*flags &= ~ILB_FLAGS_SRV_PORTNAME;
}
return (-1);
/*
* we need to convert to host byte order to be in sync with
* numerical ports. since result needs to be compared, this
* is preferred to returning NW byte order
*/
}
/*
* matches one hostname or IP address and stores it in "store".
* space must have been pre-allocated to accept data
* "sg" != NULL only for cases where ip ranges may be coming in.
*/
static ilbadm_status_t
{
ilb_server_data_t *s = NULL;
addr_type_t at = 0;
int p_flg;
struct in6_addr v6nameaddr;
errno = 0;
if (is_nat_src) {
} else {
}
if (ports_only) {
goto ports;
}
/*
* we parse the syntax ip[-ip][:port[-port]]
* since IPv6 addresses contain ':'s as well, they need to be
* enclosed in "[]" to be distinct from a potential port spec.
* therefore, we need to first check whether we're dealing with
* IPv6 addresses before we can go search for the port seperator
* and ipv6 range could look like this: [ff::0]-[ff::255]:80
*/
/*
* V6 addresses must be enclosed within
* brackets when specifying server addresses
*/
goto err_out;
}
if (*val == '[') {
val++;
goto err_out;
}
*close1 = '\0';
at = 0;
goto err_out;
goto err_out;
}
goto err_out;
}
goto ports;
}
if (*val == '-') {
if (!is_ip_range_ok) {
rc = ILBADM_LIBERR;
goto err_out;
}
val++;
if (*val != '[') {
goto err_out;
}
val++;
goto err_out;
}
*close2 = '\0';
at = 0;
goto err_out;
goto err_out;
}
goto err_out;
}
}
}
/* ports always potentially allow ranges - XXXms: check? */
*port_pref = '\0';
*dash = '\0';
}
" specified"), port1p);
rc = ILBADM_LIBERR;
goto err_out;
}
if (p_flg & ILB_FLAGS_SRV_PORTNAME)
s->sd_flags |= ILB_FLAGS_SRV_PORTNAME;
}
/* ranges are only allowed for numeric ports */
if (p_flg & ILB_FLAGS_SRV_PORTNAME) {
" for numeric ports"));
rc = ILBADM_LIBERR;
goto err_out;
}
(p_flg & ILB_FLAGS_SRV_PORTNAME) ==
" specified"), port2p);
rc = ILBADM_LIBERR;
goto err_out;
}
}
/*
* we fill the '-' back in, but not the port seperator,
* as the \0 in its place terminates the ip address(es)
*/
*dash = '-';
if (ports_only)
goto out;
}
goto out;
/*
* we need to handle these situations for hosts:
* a. ip address
* b. ip address range (ip1-ip2)
* c. a hostname (may include '-' or start with a digit)
*
* We want to do hostname lookup only if we're quite sure that
* we actually are looking at neither a single IP address nor a
* range of same, as this can hang if name service is not set up
* (sth. likely in a LB environment).
*
* here's how we proceed:
* 1. try to match numeric only. If that succeeds, we're done.
* (getaddrinfo, which we call in i_match_onehost(), fails if
* it encounters a '-')
* 2. search for a '-'; if we find one, try numeric match for
* both sides. if this fails:
* 3. re-insert '-' and try for a legal hostname.
*/
/* 1. */
goto out;
/* 2. */
*dash = '\0';
*dash = '-';
goto hostname;
}
/*
* if the RHS of '-' is an IP but LHS is not, we might
* have a hostname of form x-y where y is just a number
* (this seems a valid IPv4 address), so we need to
* try a complete hostname
*/
*dash = '-';
goto hostname;
}
goto out;
}
/* 3. */
if (is_addr_numeric)
else
at = 0;
goto out;
}
if (s != NULL) {
s->sd_flags |= ILB_FLAGS_SRV_HOSTNAME;
/* XXX: todo: save hostname for re-display for admin */
}
out:
goto err_out;
}
/*
* we re-insert what we overwrote, especially in the error case
*/
*close2 = ']';
*close1 = '[';
*dash = '-';
return (rc);
}
/*
* type-agnostic helper function to return a pointer to a
* pristine (and maybe freshly allocated) piece of storage
* ready for something fitting "key"
*/
static void *
{
void *res;
switch (key) {
case ILB_KEY_SERVER:
case ILB_KEY_SERVRANGE:
case ILB_KEY_SERVERID:
break;
break;
}
return (res);
}
/*
* make sure everything that needs to be there is there
*/
{
return (ILBADM_INVAL_AF);
return (ILBADM_ENOSGNAME);
return (ILBADM_LIBERR);
}
" or its address family does not"
" match that of the VIP address"));
return (ILBADM_LIBERR);
}
}
/* extend as necessary */
return (ILBADM_OK);
}
/*
* in parameter "sz" describes size (in bytes) of mask
*/
static int
{
uchar_t c;
int i, j;
int len = 0;
int tmask;
/*
* for every byte in the mask, we start with most significant
* bit and work our way down to the least significant bit; as
* long as we find the bit set, we add 1 to the length. the
* first unset bit we encounter terminates this process
*/
for (i = 0; i < sz; i++) {
c = mask[i];
for (j = 7; j >= 0; j--) {
if ((c & tmask) == 0)
return (len);
len++;
tmask >>= 1;
}
}
return (len);
}
int
{
int len = 0;
switch (af) {
case AF_INET:
break;
case AF_INET6:
break;
}
return (len);
}
/* copied from ifconfig.c, changed to return symbolic constants */
/*
* Convert a prefix length to a mask.
* Returns 1 if ok. 0 otherwise.
* Assumes the mask array is zero'ed by the caller.
*/
static boolean_t
{
return (B_FALSE);
while (prefixlen > 0) {
if (prefixlen >= 8) {
*mask++ = 0xFF;
prefixlen -= 8;
continue;
}
prefixlen--;
}
return (B_TRUE);
}
{
boolean_t r;
char *end;
if (*val == '/')
val++;
return (ILBADM_LIBERR);
}
return (ILBADM_LIBERR);
}
switch (af) {
case AF_INET:
break;
case AF_INET6:
break;
}
if (r != B_TRUE) {
return (ILBADM_LIBERR);
}
return (ILBADM_OK);
}
static ilbadm_status_t
{
if (*val == '\0')
return (ILBADM_NOKEYWORD_VAL);
/* some types need new storage, others don't */
switch (keyword) {
case ILB_KEY_SERVER:
case ILB_KEY_SERVERID:
break;
case ILB_KEY_HEALTHCHECK:
case ILB_KEY_SERVERGROUP:
break;
case ILB_KEY_VIP: /* fallthrough */
case ILB_KEY_PORT: /* fallthrough */
case ILB_KEY_HCPORT: /* fallthrough */
case ILB_KEY_CONNDRAIN: /* fallthrough */
case ILB_KEY_NAT_TO: /* fallthrough */
case ILB_KEY_STICKY_TO: /* fallthrough */
case ILB_KEY_PROTOCOL: /* fallthrough */
case ILB_KEY_ALGORITHM: /* fallthrough */
case ILB_KEY_STICKY: /* fallthrough */
case ILB_KEY_TYPE: /* fallthrough */
case ILB_KEY_SRC: /* fallthrough */
break;
case ILB_KEY_HC_TEST:
case ILB_KEY_HC_COUNT:
case ILB_KEY_HC_INTERVAL:
case ILB_KEY_HC_TIMEOUT:
default: /* do nothing */
;
}
switch (keyword) {
case ILB_KEY_SRC:
/*
* the proxy-src keyword is only valid for full NAT topology
* the value is either a single or a range of IP addresses.
*/
break;
}
break;
case ILB_KEY_SERVER:
break;
case ILB_KEY_SERVERID:
if (val[0] != ILB_SRVID_PREFIX)
else
break;
case ILB_KEY_VIP: {
/*
* we duplicate some functionality of i_match_hostorip
* here; that function is geared to mandate '[]' for IPv6
* addresses, which we want to relax here, so as not to
* make i_match_hostorip even longer, we do what we need
* here.
*/
if (*val == '[') {
val++;
break;
}
}
/* re-assemble string as we found it */
*close = ']';
" with IPv6 addresses"));
rc = ILBADM_LIBERR;
}
}
break;
}
case ILB_KEY_CONNDRAIN:
rc = ILBADM_EINVAL;
break;
}
break;
case ILB_KEY_NAT_TO:
rc = ILBADM_EINVAL;
break;
}
break;
case ILB_KEY_STICKY_TO:
rc = ILBADM_EINVAL;
break;
}
break;
case ILB_KEY_PORT:
break;
} else {
break;
}
}
break;
case ILB_KEY_HCPORT:
" hcport %s"), val);
rc = ILBADM_LIBERR;
break;
}
} else {
return (ILBADM_EINVAL);
}
break;
case ILB_KEY_PROTOCOL:
else
break;
case ILB_KEY_ALGORITHM:
break;
case ILB_KEY_STICKY:
/*
* CAVEAT: the use of r_vip.ia_af implies that the VIP
* *must* be specified on the commandline *before*
* the sticky mask.
*/
rc = ILBADM_LIBERR;
break;
}
break;
case ILB_KEY_TYPE:
break;
case ILB_KEY_SERVERGROUP:
break;
case ILB_KEY_HEALTHCHECK:
break;
case ILB_KEY_HC_TEST:
break;
case ILB_KEY_HC_COUNT:
else
return (ILBADM_EINVAL);
break;
case ILB_KEY_HC_INTERVAL:
else
return (ILBADM_EINVAL);
break;
case ILB_KEY_HC_TIMEOUT:
else
return (ILBADM_EINVAL);
break;
default: rc = ILBADM_INVAL_KEYWORD;
break;
}
return (rc);
}
/*
* generic parsing function.
* parses "key=value[,value]" strings in "arg". keylist determines the
* list of valid keys in the LHS. keycode determines interpretation and
* storage in store
* XXXms: looks like "key=value[,value]" violates spec. needs a fix
*/
{
int n;
n = 1;
/*
* Algorithm:
* 1. find any commas indicating and seperating current value
* from a following value
* 2. if we're expecting a list of values (seperated by commas)
* and have already seen the assignment, then
* get the next "value"
* 3. else (we're looking at the first element of the RHS)
* 4. find the '='
* 5. match the keyword to the list we were passed in
* 6. store the value.
*/
/* 2 */
*comma = '\0';
}
/* 3a */
if (is_value_list && assign_seen) {
/* 3b */
} else {
/* 4 */
rc = ILBADM_LIBERR;
goto out;
}
*equals = '\0';
/* 5 */
if (keyword == ILB_KEY_BAD) {
rc = ILBADM_LIBERR;
goto out;
}
}
/* 6 */
/* Change to ILBADM_ILBERR to avoid more err msgs. */
rc = ILBADM_LIBERR;
goto out;
}
n++;
}
out:
*comma = ',';
*equals = '=';
*count = n;
return (rc);
}