#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.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,
non_numeric
} addr_type_t;
ilbadm_val_type_t algo_types[] = {
{(int)ILB_ALG_ROUNDROBIN, "roundrobin", "rr"},
{(int)ILB_ALG_HASH_IP, "hash-ip", "hip"},
{(int)ILB_ALG_HASH_IP_SPORT, "hash-ip-port", "hipp"},
{(int)ILB_ALG_HASH_IP_VIP, "hash-ip-vip", "hipv"},
{ILBD_BAD_VAL, 0, 0}
};
ilbadm_val_type_t topo_types[] = {
{(int)ILB_TOPO_DSR, "DSR", "d"},
{(int)ILB_TOPO_NAT, "NAT", "n"},
{(int)ILB_TOPO_HALF_NAT, "HALF-NAT", "h"},
{ILBD_BAD_VAL, 0, 0}
};
void
ip2str(ilb_ip_addr_t *ip, char *buf, size_t sz, int flags)
{
int len;
switch (ip->ia_af) {
case AF_INET:
if (*(uint32_t *)&ip->ia_v4 == 0)
buf[0] = '\0';
else
(void) inet_ntop(AF_INET, (void *)&ip->ia_v4, buf, sz);
break;
case AF_INET6:
if (IN6_IS_ADDR_UNSPECIFIED(&ip->ia_v6)) {
buf[0] = '\0';
break;
}
if (!(flags & V6_ADDRONLY))
*buf++ = '[';
sz--;
(void) inet_ntop(ip->ia_af, (void *)&ip->ia_v6, buf, sz);
if (!(flags & V6_ADDRONLY)) {
len = strlen(buf);
buf[len] = ']';
buf[++len] = '\0';
}
break;
default: buf[0] = '\0';
}
}
char *
i_str_from_val(int val, ilbadm_val_type_t *types)
{
ilbadm_val_type_t *v;
for (v = types; v->v_type != ILBD_BAD_VAL; v++) {
if (v->v_type == val)
break;
}
return (v->v_name);
}
int
i_val_from_str(char *name, ilbadm_val_type_t *types)
{
ilbadm_val_type_t *v;
for (v = types; v->v_type != ILBD_BAD_VAL; v++) {
if (strncasecmp(name, v->v_name, sizeof (v->v_name)) == 0 ||
strncasecmp(name, v->v_alias, sizeof (v->v_alias)) == 0)
break;
}
return (v->v_type);
}
ilbadm_key_code_t
i_match_key(char *key, ilbadm_key_name_t *keylist)
{
ilbadm_key_name_t *t_key;
for (t_key = keylist; t_key->k_key != ILB_KEY_BAD; t_key++) {
if (strncasecmp(key, t_key->k_name,
sizeof (t_key->k_name)) == 0 ||
strncasecmp(key, t_key->k_alias,
sizeof (t_key->k_alias)) == 0)
break;
}
return (t_key->k_key);
}
static ilbadm_status_t
i_match_onehost(const char *val, ilb_ip_addr_t *ip, addr_type_t *a_type)
{
struct addrinfo *ai = NULL;
struct addrinfo hints;
addr_type_t at = numeric;
(void) memset((void *)&hints, 0, sizeof (hints));
hints.ai_flags |= AI_NUMERICHOST;
if (getaddrinfo(val, NULL, &hints, &ai) != 0) {
if (a_type != NULL && (*a_type == numeric))
return (ILBADM_INVAL_ADDR);
at = non_numeric;
if (getaddrinfo(val, NULL, NULL, &ai) != 0)
return (ILBADM_INVAL_ADDR);
}
ip->ia_af = ai->ai_family;
switch (ip->ia_af) {
case AF_INET: {
struct sockaddr_in sa;
assert(ai->ai_addrlen == sizeof (sa));
(void) memcpy(&sa, ai->ai_addr, sizeof (sa));
ip->ia_v4 = sa.sin_addr;
break;
}
case AF_INET6: {
struct sockaddr_in6 sa;
assert(ai->ai_addrlen == sizeof (sa));
(void) memcpy(&sa, ai->ai_addr, sizeof (sa));
ip->ia_v6 = sa.sin6_addr;
break;
}
default:
return (ILBADM_INVAL_AF);
}
if (a_type != NULL)
*a_type = at;
return (ILBADM_OK);
}
static ilbadm_status_t
i_store_serverID(void *store, char *val)
{
ilbadm_servnode_t *s = (ilbadm_servnode_t *)store;
ilb_server_data_t *sn = &s->s_spec;
(void) strlcpy(sn->sd_srvID, val, sizeof (sn->sd_srvID));
return (ILBADM_OK);
}
static struct in_addr
i_next_in_addr(struct in_addr *a, int dir)
{
struct in_addr new_in;
uint32_t iah;
iah = ntohl(a->s_addr);
if (dir == 1)
iah++;
else
iah--;
new_in.s_addr = htonl(iah);
return (new_in);
}
static ilbadm_status_t
i_expand_ipv4range(ilbadm_sgroup_t *sg, ilb_server_data_t *srv,
ilb_ip_addr_t *ip1, ilb_ip_addr_t *ip2)
{
struct in_addr *a1;
ilbadm_servnode_t *sn_new;
ilb_ip_addr_t new_ip;
a1 = &ip1->ia_v4;
new_ip.ia_af = AF_INET;
new_ip.ia_v4 = i_next_in_addr(a1, 1);
while (ilb_cmp_ipaddr(&new_ip, ip2, NULL) < 1) {
sn_new = i_new_sg_elem(sg);
sn_new->s_spec.sd_addr = new_ip;
sn_new->s_spec.sd_minport = srv->sd_minport;
sn_new->s_spec.sd_maxport = srv->sd_maxport;
new_ip.ia_v4 = i_next_in_addr(&new_ip.ia_v4, 1);
}
return (ILBADM_OK);
}
static struct in6_addr
i_next_in6_addr(struct in6_addr *a, int dir)
{
struct in6_addr ia6;
uint64_t al, ah;
ah = INV6_N2H_MSB64(a);
al = INV6_N2H_LSB64(a);
if (dir == 1) {
if (++al == 0)
ah++;
} else {
if (--al == 0xffffffff)
ah--;
}
INV6_H2N_MSB64(&ia6, ah);
INV6_H2N_LSB64(&ia6, al);
return (ia6);
}
static ilbadm_status_t
i_expand_ipv6range(ilbadm_sgroup_t *sg, ilb_server_data_t *srv,
ilb_ip_addr_t *ip1, ilb_ip_addr_t *ip2)
{
struct in6_addr *a1;
ilbadm_servnode_t *sn_new;
ilb_ip_addr_t new_ip;
a1 = &ip1->ia_v6;
new_ip.ia_af = AF_INET6;
new_ip.ia_v6 = i_next_in6_addr(a1, 1);
while (ilb_cmp_ipaddr(&new_ip, ip2, NULL) < 1) {
sn_new = i_new_sg_elem(sg);
sn_new->s_spec.sd_addr = new_ip;
sn_new->s_spec.sd_minport = srv->sd_minport;
sn_new->s_spec.sd_maxport = srv->sd_maxport;
new_ip.ia_v6 = i_next_in6_addr(&new_ip.ia_v6, 1);
}
return (ILBADM_OK);
}
static ilbadm_status_t
i_expand_iprange(ilbadm_sgroup_t *sg, ilb_server_data_t *sr,
ilb_ip_addr_t *ip1, ilb_ip_addr_t *ip2)
{
int cmp;
int64_t delta;
if (ip2->ia_af == 0)
return (ILBADM_OK);
if (ip1->ia_af != ip2->ia_af) {
ilbadm_err(gettext("IP address mismatch"));
return (ILBADM_LIBERR);
}
if ((cmp = ilb_cmp_ipaddr(ip1, ip2, &delta)) == 0)
return (ILBADM_OK);
if (cmp == 1) {
ilbadm_err(gettext("starting IP address is must be less"
" than ending ip address in ip range specification"));
return (ILBADM_LIBERR);
}
if (abs((int)delta) > MAX_IP_SPREAD)
return (ILBADM_TOOMANYIPADDR);
switch (ip1->ia_af) {
case AF_INET:
return (i_expand_ipv4range(sg, sr, ip1, ip2));
case AF_INET6:
return (i_expand_ipv6range(sg, sr, ip1, ip2));
}
return (ILBADM_INVAL_AF);
}
static int
i_parseport(char *port, char *proto, int *flags)
{
struct servent *se;
if (isdigit(port[0])) {
if (flags != NULL)
*flags &= ~ILB_FLAGS_SRV_PORTNAME;
return ((int)strtol(port, NULL, 10));
}
se = getservbyname(port, proto);
if (se == NULL)
return (-1);
if (flags != NULL)
*flags |= ILB_FLAGS_SRV_PORTNAME;
return ((int)(ntohs(se->s_port)));
}
static ilbadm_status_t
i_match_hostorip(void *store, ilbadm_sgroup_t *sg, char *val,
int flags, ilbadm_key_code_t keyword)
{
boolean_t is_ip_range_ok = flags & OPT_IP_RANGE;
boolean_t is_addr_numeric = flags & OPT_NUMERIC_ONLY;
boolean_t is_ports_ok = flags & OPT_PORTS;
boolean_t ports_only = flags & OPT_PORTS_ONLY;
boolean_t is_nat_src = flags & OPT_NAT;
char *port_pref, *dash;
char *port1p, *port2p, *host2p, *host1p;
char *close1, *close2;
ilb_ip_addr_t ip2store;
ilb_ip_addr_t *ip1, *ip2;
int p1, p2;
ilb_server_data_t *s = NULL;
ilbadm_status_t rc = ILBADM_OK;
int af = AF_INET;
addr_type_t at = 0;
int p_flg;
struct in6_addr v6nameaddr;
port1p = port2p = host2p = host1p = NULL;
port_pref = dash = NULL;
close1 = close2 = NULL;
errno = 0;
if (is_nat_src) {
ilb_rule_data_t *rd = (ilb_rule_data_t *)store;
ip1 = &rd->r_nat_src_start;
ip2 = &rd->r_nat_src_end;
} else {
ilbadm_servnode_t *sn = (ilbadm_servnode_t *)store;
s = &sn->s_spec;
ip1 = &s->sd_addr;
ip2 = &ip2store;
bzero(ip2, sizeof (*ip2));
}
if (ports_only) {
is_ports_ok = B_TRUE;
port_pref = val - 1;
goto ports;
}
if ((keyword == ILB_KEY_SERVER) && (strchr(val, ':') != NULL) &&
(*val != '[') && ((inet_pton(AF_INET6, val, &v6nameaddr)) != 0)) {
rc = ILBADM_INVAL_SYNTAX;
goto err_out;
}
if (*val == '[') {
af = AF_INET6;
val++;
host1p = val;
close1 = strchr(val, (int)']');
if (close1 == NULL) {
rc = ILBADM_INVAL_SYNTAX;
goto err_out;
}
*close1 = '\0';
at = 0;
rc = i_match_onehost(host1p, ip1, &at);
if (rc != ILBADM_OK)
goto err_out;
if (at != numeric) {
rc = ILBADM_INVAL_ADDR;
goto err_out;
}
if (ip1->ia_af != af) {
rc = ILBADM_INVAL_AF;
goto err_out;
}
val = close1 + 1;
if (*val == PORT_SEP) {
port_pref = val;
goto ports;
}
if (*val == '-') {
dash = val;
if (!is_ip_range_ok) {
ilbadm_err(gettext("port ranges not allowed"));
rc = ILBADM_LIBERR;
goto err_out;
}
val++;
if (*val != '[') {
rc = ILBADM_INVAL_SYNTAX;
goto err_out;
}
val++;
close2 = strchr(val, (int)']');
if (close2 == NULL) {
rc = ILBADM_INVAL_SYNTAX;
goto err_out;
}
*close2 = '\0';
host2p = val;
at = 0;
rc = i_match_onehost(host2p, ip2, &at);
if (rc != ILBADM_OK)
goto err_out;
if (at != numeric) {
rc = ILBADM_INVAL_ADDR;
goto err_out;
}
if (ip2->ia_af != af) {
rc = ILBADM_INVAL_AF;
goto err_out;
}
val = close2+1;
}
}
port_pref = strchr(val, (int)PORT_SEP);
ports:
if (port_pref != NULL && is_ports_ok) {
port1p = port_pref + 1;
*port_pref = '\0';
dash = strchr(port1p, (int)'-');
if (dash != NULL) {
port2p = dash + 1;
*dash = '\0';
}
if (port1p != NULL) {
p1 = i_parseport(port1p, NULL, &p_flg);
if (p1 == -1 || p1 == 0 || p1 > ILB_MAX_PORT) {
ilbadm_err(gettext("invalid port value %s"
" specified"), port1p);
rc = ILBADM_LIBERR;
goto err_out;
}
s->sd_minport = htons((in_port_t)p1);
if (p_flg & ILB_FLAGS_SRV_PORTNAME)
s->sd_flags |= ILB_FLAGS_SRV_PORTNAME;
}
if (port2p != NULL) {
if (p_flg & ILB_FLAGS_SRV_PORTNAME) {
ilbadm_err(gettext("ranges are only allowed"
" for numeric ports"));
rc = ILBADM_LIBERR;
goto err_out;
}
p2 = i_parseport(port2p, NULL, &p_flg);
if (p2 == -1 || p2 <= p1 || p2 > ILB_MAX_PORT ||
(p_flg & ILB_FLAGS_SRV_PORTNAME) ==
ILB_FLAGS_SRV_PORTNAME) {
ilbadm_err(gettext("invalid port value %s"
" specified"), port2p);
rc = ILBADM_LIBERR;
goto err_out;
}
s->sd_maxport = htons((in_port_t)p2);
}
if (dash != NULL)
*dash = '-';
if (ports_only)
goto out;
}
if (af == AF_INET6)
goto out;
at = numeric;
rc = i_match_onehost(val, ip1, &at);
if (rc == ILBADM_OK)
goto out;
dash = strchr(val, (int)'-');
if (dash != NULL && is_ip_range_ok) {
host2p = dash + 1;
*dash = '\0';
at = numeric;
rc = i_match_onehost(host2p, ip2, &at);
if (rc != ILBADM_OK || at != numeric) {
*dash = '-';
dash = NULL;
bzero(ip2, sizeof (*ip2));
goto hostname;
}
rc = i_match_onehost(val, ip1, &at);
if (rc != ILBADM_OK || at != numeric) {
*dash = '-';
dash = NULL;
goto hostname;
}
goto out;
}
hostname:
if (is_addr_numeric)
at = numeric;
else
at = 0;
rc = i_match_onehost(val, ip1, &at);
if (rc != ILBADM_OK) {
goto out;
}
if (s != NULL) {
s->sd_flags |= ILB_FLAGS_SRV_HOSTNAME;
}
out:
if (dash != NULL && !is_nat_src) {
rc = i_expand_iprange(sg, s, ip1, ip2);
if (rc != ILBADM_OK)
goto err_out;
}
if (is_nat_src && host2p == NULL)
*ip2 = *ip1;
err_out:
if (close2 != NULL)
*close2 = ']';
if (close1 != NULL)
*close1 = '[';
if (dash != NULL)
*dash = '-';
if (port_pref != NULL && !ports_only)
*port_pref = PORT_SEP;
return (rc);
}
static void *
i_new_storep(void *store, ilbadm_key_code_t key)
{
void *res;
switch (key) {
case ILB_KEY_SERVER:
case ILB_KEY_SERVRANGE:
case ILB_KEY_SERVERID:
res = (void *) i_new_sg_elem(store);
break;
default: res = NULL;
break;
}
return (res);
}
ilbadm_status_t
i_check_rule_spec(ilb_rule_data_t *rd)
{
int32_t vip_af = rd->r_vip.ia_af;
ilb_ip_addr_t *prxy_src;
if (vip_af != AF_INET && vip_af != AF_INET6)
return (ILBADM_INVAL_AF);
if (*rd->r_sgname == '\0')
return (ILBADM_ENOSGNAME);
if (rd->r_algo == 0 || rd->r_topo == 0) {
ilbadm_err(gettext("lbalg or type is unspecified"));
return (ILBADM_LIBERR);
}
if (rd->r_topo == ILB_TOPO_NAT) {
prxy_src = &rd->r_nat_src_start;
if (prxy_src->ia_af != vip_af) {
ilbadm_err(gettext("proxy-src is either missing"
" or its address family does not"
" match that of the VIP address"));
return (ILBADM_LIBERR);
}
}
return (ILBADM_OK);
}
static int
mask_to_prefixlen(const uchar_t *mask, const int sz)
{
uchar_t c;
int i, j;
int len = 0;
int tmask;
for (i = 0; i < sz; i++) {
c = mask[i];
tmask = 1 << 7;
for (j = 7; j >= 0; j--) {
if ((c & tmask) == 0)
return (len);
len++;
tmask >>= 1;
}
}
return (len);
}
int
ilbadm_mask_to_prefixlen(ilb_ip_addr_t *ip)
{
int af = ip->ia_af;
int len = 0;
assert(af == AF_INET || af == AF_INET6);
switch (af) {
case AF_INET:
len = mask_to_prefixlen((uchar_t *)&ip->ia_v4.s_addr,
sizeof (ip->ia_v4));
break;
case AF_INET6:
len = mask_to_prefixlen((uchar_t *)&ip->ia_v6.s6_addr,
sizeof (ip->ia_v6));
break;
}
return (len);
}
static boolean_t
in_prefixlentomask(int prefixlen, int maxlen, uchar_t *mask)
{
if (prefixlen < 0 || prefixlen > maxlen)
return (B_FALSE);
while (prefixlen > 0) {
if (prefixlen >= 8) {
*mask++ = 0xFF;
prefixlen -= 8;
continue;
}
*mask |= 1 << (8 - prefixlen);
prefixlen--;
}
return (B_TRUE);
}
ilbadm_status_t
ilbadm_set_netmask(char *val, ilb_ip_addr_t *ip, int af)
{
int prefixlen, maxval;
boolean_t r;
char *end;
assert(af == AF_INET || af == AF_INET6);
maxval = (af == AF_INET) ? 32 : 128;
if (*val == '/')
val++;
prefixlen = strtol(val, &end, 10);
if ((val == end) || (*end != '\0')) {
ilbadm_err(gettext("invalid pmask provided"));
return (ILBADM_LIBERR);
}
if (prefixlen < 1 || prefixlen > maxval) {
ilbadm_err(gettext("invalid pmask provided (AF mismatch?)"));
return (ILBADM_LIBERR);
}
switch (af) {
case AF_INET:
r = in_prefixlentomask(prefixlen, maxval,
(uchar_t *)&ip->ia_v4.s_addr);
break;
case AF_INET6:
r = in_prefixlentomask(prefixlen, maxval,
(uchar_t *)&ip->ia_v6.s6_addr);
break;
}
if (r != B_TRUE) {
ilbadm_err(gettext("cannot convert %s to a netmask"), val);
return (ILBADM_LIBERR);
}
ip->ia_af = af;
return (ILBADM_OK);
}
static ilbadm_status_t
i_store_val(char *val, void *store, ilbadm_key_code_t keyword)
{
ilbadm_status_t rc = ILBADM_OK;
void *storep = store;
ilb_rule_data_t *rd = NULL;
ilbadm_sgroup_t *sg = NULL;
ilb_hc_info_t *hc_info = NULL;
struct protoent *pe;
int64_t tmp_val;
if (*val == '\0')
return (ILBADM_NOKEYWORD_VAL);
switch (keyword) {
case ILB_KEY_SERVER:
case ILB_KEY_SERVERID:
sg = (ilbadm_sgroup_t *)store;
storep = i_new_storep(store, keyword);
break;
case ILB_KEY_HEALTHCHECK:
case ILB_KEY_SERVERGROUP:
rd = (ilb_rule_data_t *)store;
break;
case ILB_KEY_VIP:
case ILB_KEY_PORT:
case ILB_KEY_HCPORT:
case ILB_KEY_CONNDRAIN:
case ILB_KEY_NAT_TO:
case ILB_KEY_STICKY_TO:
case ILB_KEY_PROTOCOL:
case ILB_KEY_ALGORITHM:
case ILB_KEY_STICKY:
case ILB_KEY_TYPE:
case ILB_KEY_SRC:
rd = (ilb_rule_data_t *)store;
break;
case ILB_KEY_HC_TEST:
case ILB_KEY_HC_COUNT:
case ILB_KEY_HC_INTERVAL:
case ILB_KEY_HC_TIMEOUT:
hc_info = (ilb_hc_info_t *)store;
default:
;
}
switch (keyword) {
case ILB_KEY_SRC:
if (rd->r_topo != ILB_TOPO_NAT) {
rc = ILBADM_INVAL_PROXY;
break;
}
rc = i_match_hostorip(storep, sg, val, OPT_NUMERIC_ONLY |
OPT_IP_RANGE | OPT_NAT, ILB_KEY_SRC);
break;
case ILB_KEY_SERVER:
rc = i_match_hostorip(storep, sg, val,
OPT_IP_RANGE | OPT_PORTS, ILB_KEY_SERVER);
break;
case ILB_KEY_SERVERID:
if (val[0] != ILB_SRVID_PREFIX)
rc = ILBADM_INVAL_SRVID;
else
rc = i_store_serverID(storep, val);
break;
case ILB_KEY_VIP: {
ilb_ip_addr_t *vip = &rd->r_vip;
addr_type_t at = numeric;
char *close = NULL;
if (*val == '[') {
val++;
if ((close = strchr(val, (int)']')) == NULL) {
rc = ILBADM_INVAL_SYNTAX;
break;
}
*close = '\0';
}
rc = i_match_onehost(val, vip, &at);
if (close != NULL) {
*close = ']';
if (rc == ILBADM_OK && vip->ia_af != AF_INET6) {
ilbadm_err(gettext("use of '[]' only valid"
" with IPv6 addresses"));
rc = ILBADM_LIBERR;
}
}
break;
}
case ILB_KEY_CONNDRAIN:
tmp_val = strtoll(val, NULL, 10);
if (tmp_val <= 0 || tmp_val > UINT_MAX) {
rc = ILBADM_EINVAL;
break;
}
rd->r_conndrain = tmp_val;
break;
case ILB_KEY_NAT_TO:
tmp_val = strtoll(val, NULL, 10);
if (tmp_val < 0 || tmp_val > UINT_MAX) {
rc = ILBADM_EINVAL;
break;
}
rd->r_nat_timeout = tmp_val;
break;
case ILB_KEY_STICKY_TO:
tmp_val = strtoll(val, NULL, 10);
if (tmp_val <= 0 || tmp_val > UINT_MAX) {
rc = ILBADM_EINVAL;
break;
}
rd->r_sticky_timeout = tmp_val;
break;
case ILB_KEY_PORT:
if (isdigit(*val)) {
ilbadm_servnode_t sn;
bzero(&sn, sizeof (sn));
rc = i_match_hostorip((void *)&sn, sg, val,
OPT_PORTS_ONLY, ILB_KEY_PORT);
if (rc != ILBADM_OK)
break;
rd->r_minport = sn.s_spec.sd_minport;
rd->r_maxport = sn.s_spec.sd_maxport;
} else {
struct servent *se;
se = getservbyname(val, NULL);
if (se == NULL) {
rc = ILBADM_ENOSERVICE;
break;
}
rd->r_minport = se->s_port;
rd->r_maxport = 0;
}
break;
case ILB_KEY_HCPORT:
if (isdigit(*val)) {
int hcport = atoi(val);
if (hcport < 1 || hcport > 65535) {
ilbadm_err(gettext("illegal number for"
" hcport %s"), val);
rc = ILBADM_LIBERR;
break;
}
rd->r_hcport = htons(hcport);
rd->r_hcpflag = ILB_HCI_PROBE_FIX;
} else if (strcasecmp(val, "ANY") == 0) {
rd->r_hcport = 0;
rd->r_hcpflag = ILB_HCI_PROBE_ANY;
} else {
return (ILBADM_EINVAL);
}
break;
case ILB_KEY_PROTOCOL:
pe = getprotobyname(val);
if (pe == NULL)
rc = ILBADM_ENOPROTO;
else
rd->r_proto = pe->p_proto;
break;
case ILB_KEY_ALGORITHM:
rd->r_algo = i_val_from_str(val, &algo_types[0]);
if (rd->r_algo == ILBD_BAD_VAL)
rc = ILBADM_INVAL_ALG;
break;
case ILB_KEY_STICKY:
rd->r_flags |= ILB_FLAGS_RULE_STICKY;
if (AF_UNSPEC == rd->r_vip.ia_af) {
ilbadm_err(gettext("option '%s' requires that VIP be "
"specified first"), ilbadm_key_to_opt(keyword));
rc = ILBADM_LIBERR;
break;
}
rc = ilbadm_set_netmask(val, &rd->r_stickymask,
rd->r_vip.ia_af);
break;
case ILB_KEY_TYPE:
rd->r_topo = i_val_from_str(val, &topo_types[0]);
if (rd->r_topo == ILBD_BAD_VAL)
rc = ILBADM_INVAL_OPER;
break;
case ILB_KEY_SERVERGROUP:
(void) strlcpy(rd->r_sgname, (char *)val,
sizeof (rd->r_sgname));
break;
case ILB_KEY_HEALTHCHECK:
(void) strlcpy(rd->r_hcname, (char *)val,
sizeof (rd->r_hcname));
break;
case ILB_KEY_HC_TEST:
(void) strlcpy(hc_info->hci_test, (char *)val,
sizeof (hc_info->hci_test));
break;
case ILB_KEY_HC_COUNT:
if (isdigit(*val))
hc_info->hci_count = atoi(val);
else
return (ILBADM_EINVAL);
break;
case ILB_KEY_HC_INTERVAL:
if (isdigit(*val))
hc_info->hci_interval = atoi(val);
else
return (ILBADM_EINVAL);
break;
case ILB_KEY_HC_TIMEOUT:
if (isdigit(*val))
hc_info->hci_timeout = atoi(val);
else
return (ILBADM_EINVAL);
break;
default: rc = ILBADM_INVAL_KEYWORD;
break;
}
return (rc);
}
ilbadm_status_t
i_parse_optstring(char *arg, void *store, ilbadm_key_name_t *keylist,
int flags, int *count)
{
ilbadm_status_t rc = ILBADM_OK;
char *comma = NULL, *equals = NULL;
char *key, *nextkey, *val;
ilbadm_key_code_t keyword;
boolean_t is_value_list = flags & OPT_VALUE_LIST;
boolean_t assign_seen = B_FALSE;
int n;
key = arg;
n = 1;
while (key != NULL && *key != '\0') {
comma = equals = NULL;
nextkey = strchr(key, (int)',');
if (nextkey != NULL) {
comma = nextkey++;
*comma = '\0';
}
if (is_value_list && assign_seen) {
val = key;
} else {
equals = strchr(key, (int)'=');
if (equals == NULL) {
ilbadm_err("%s: %s", key,
ilbadm_errstr(ILBADM_ASSIGNREQ));
rc = ILBADM_LIBERR;
goto out;
}
val = equals + 1;
*equals = '\0';
assign_seen = B_TRUE;
keyword = i_match_key(key, keylist);
if (keyword == ILB_KEY_BAD) {
ilbadm_err(gettext("bad keyword %s"), key);
rc = ILBADM_LIBERR;
goto out;
}
}
rc = i_store_val(val, store, keyword);
if (rc != ILBADM_OK) {
ilbadm_err("%s: %s", key, ilbadm_errstr(rc));
rc = ILBADM_LIBERR;
goto out;
}
key = nextkey;
n++;
}
out:
if (comma != NULL)
*comma = ',';
if (equals != NULL)
*equals = '=';
if (count != NULL)
*count = n;
return (rc);
}