4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 * Copyright 2012 Milan Jurik. All rights reserved.
32 #include <sys/types.h>
33 #include <sys/socket.h>
34 #include <netinet/in.h>
35 #include <arpa/inet.h>
42 #include <libilb_impl.h>
52 ilbadm_val_type_t algo_types
[] = {
53 {(int)ILB_ALG_ROUNDROBIN
, "roundrobin", "rr"},
54 {(int)ILB_ALG_HASH_IP
, "hash-ip", "hip"},
55 {(int)ILB_ALG_HASH_IP_SPORT
, "hash-ip-port", "hipp"},
56 {(int)ILB_ALG_HASH_IP_VIP
, "hash-ip-vip", "hipv"},
57 {ILBD_BAD_VAL
, '\0', '\0'}
60 ilbadm_val_type_t topo_types
[] = {
61 {(int)ILB_TOPO_DSR
, "DSR", "d"},
62 {(int)ILB_TOPO_NAT
, "NAT", "n"},
63 {(int)ILB_TOPO_HALF_NAT
, "HALF-NAT", "h"},
64 {ILBD_BAD_VAL
, '\0', '\0'}
68 ip2str(ilb_ip_addr_t
*ip
, char *buf
, size_t sz
, int flags
)
74 if (*(uint32_t *)&ip
->ia_v4
== 0)
77 (void) inet_ntop(AF_INET
, (void *)&ip
->ia_v4
, buf
, sz
);
80 if (IN6_IS_ADDR_UNSPECIFIED(&ip
->ia_v6
)) {
84 if (!(flags
& V6_ADDRONLY
))
87 (void) inet_ntop(ip
->ia_af
, (void *)&ip
->ia_v6
, buf
, sz
);
88 if (!(flags
& V6_ADDRONLY
)) {
94 default: buf
[0] = '\0';
99 i_str_from_val(int val
, ilbadm_val_type_t
*types
)
101 ilbadm_val_type_t
*v
;
103 for (v
= types
; v
->v_type
!= ILBD_BAD_VAL
; v
++) {
104 if (v
->v_type
== val
)
107 /* we return this in all cases */
112 i_val_from_str(char *name
, ilbadm_val_type_t
*types
)
114 ilbadm_val_type_t
*v
;
116 for (v
= types
; v
->v_type
!= ILBD_BAD_VAL
; v
++) {
117 if (strncasecmp(name
, v
->v_name
, sizeof (v
->v_name
)) == 0 ||
118 strncasecmp(name
, v
->v_alias
, sizeof (v
->v_alias
)) == 0)
121 /* we return this in all cases */
126 i_match_key(char *key
, ilbadm_key_name_t
*keylist
)
128 ilbadm_key_name_t
*t_key
;
130 for (t_key
= keylist
; t_key
->k_key
!= ILB_KEY_BAD
; t_key
++) {
131 if (strncasecmp(key
, t_key
->k_name
,
132 sizeof (t_key
->k_name
)) == 0 ||
133 strncasecmp(key
, t_key
->k_alias
,
134 sizeof (t_key
->k_alias
)) == 0)
137 return (t_key
->k_key
);
146 static ilbadm_status_t
147 i_match_onehost(const char *val
, ilb_ip_addr_t
*ip
, addr_type_t
*a_type
)
149 struct addrinfo
*ai
= NULL
;
150 struct addrinfo hints
;
151 addr_type_t at
= numeric
;
153 (void) memset(&hints
, 0, sizeof (hints
));
154 hints
.ai_flags
|= AI_NUMERICHOST
;
157 * if *a_type == numeric, we only want to check whether this
158 * is a (valid) numeric IP address. If we do and it is NOT,
161 if (getaddrinfo(val
, NULL
, &hints
, &ai
) != 0) {
162 if (a_type
!= NULL
&& (*a_type
== numeric
))
163 return (ILBADM_INVAL_ADDR
);
166 if (getaddrinfo(val
, NULL
, NULL
, &ai
) != 0)
167 return (ILBADM_INVAL_ADDR
);
170 ip
->ia_af
= ai
->ai_family
;
173 struct sockaddr_in sa
;
175 assert(ai
->ai_addrlen
== sizeof (sa
));
176 (void) memcpy(&sa
, ai
->ai_addr
, sizeof (sa
));
177 ip
->ia_v4
= sa
.sin_addr
;
181 struct sockaddr_in6 sa
;
183 assert(ai
->ai_addrlen
== sizeof (sa
));
184 (void) memcpy(&sa
, ai
->ai_addr
, sizeof (sa
));
185 ip
->ia_v6
= sa
.sin6_addr
;
189 return (ILBADM_INVAL_AF
);
197 static ilbadm_status_t
198 i_store_serverID(void *store
, char *val
)
200 ilbadm_servnode_t
*s
= (ilbadm_servnode_t
*)store
;
201 ilb_server_data_t
*sn
= &s
->s_spec
;
204 * we shouldn't need to check for length here, as a name that's
205 * too long won't exist in the system anyway.
207 (void) strlcpy(sn
->sd_srvID
, val
, sizeof (sn
->sd_srvID
));
211 static struct in_addr
212 i_next_in_addr(struct in_addr
*a
, int dir
)
214 struct in_addr new_in
;
217 iah
= ntohl(a
->s_addr
);
222 new_in
.s_addr
= htonl(iah
);
226 static ilbadm_status_t
227 i_expand_ipv4range(ilbadm_sgroup_t
*sg
, ilb_server_data_t
*srv
,
228 ilb_ip_addr_t
*ip1
, ilb_ip_addr_t
*ip2
)
231 ilbadm_servnode_t
*sn_new
;
232 ilb_ip_addr_t new_ip
;
236 new_ip
.ia_af
= AF_INET
;
237 new_ip
.ia_v4
= i_next_in_addr(a1
, 1);
238 while (ilb_cmp_ipaddr(&new_ip
, ip2
, NULL
) < 1) {
239 sn_new
= i_new_sg_elem(sg
);
240 sn_new
->s_spec
.sd_addr
= new_ip
;
241 sn_new
->s_spec
.sd_minport
= srv
->sd_minport
;
242 sn_new
->s_spec
.sd_maxport
= srv
->sd_maxport
;
243 new_ip
.ia_v4
= i_next_in_addr(&new_ip
.ia_v4
, 1);
248 static struct in6_addr
249 i_next_in6_addr(struct in6_addr
*a
, int dir
)
254 ah
= INV6_N2H_MSB64(a
);
255 al
= INV6_N2H_LSB64(a
);
263 if (--al
== 0xffffffff)
267 INV6_H2N_MSB64(&ia6
, ah
);
268 INV6_H2N_LSB64(&ia6
, al
);
273 static ilbadm_status_t
274 i_expand_ipv6range(ilbadm_sgroup_t
*sg
, ilb_server_data_t
*srv
,
275 ilb_ip_addr_t
*ip1
, ilb_ip_addr_t
*ip2
)
278 ilbadm_servnode_t
*sn_new
;
279 ilb_ip_addr_t new_ip
;
283 new_ip
.ia_af
= AF_INET6
;
284 new_ip
.ia_v6
= i_next_in6_addr(a1
, 1);
285 while (ilb_cmp_ipaddr(&new_ip
, ip2
, NULL
) < 1) {
286 sn_new
= i_new_sg_elem(sg
);
287 sn_new
->s_spec
.sd_addr
= new_ip
;
288 sn_new
->s_spec
.sd_minport
= srv
->sd_minport
;
289 sn_new
->s_spec
.sd_maxport
= srv
->sd_maxport
;
290 new_ip
.ia_v6
= i_next_in6_addr(&new_ip
.ia_v6
, 1);
297 * we create a list node in the servergroup for every ip address
298 * in the range [ip1, ip2], where we interpret the ip addresses as
300 * the first ip address is already stored in "sn"
302 static ilbadm_status_t
303 i_expand_iprange(ilbadm_sgroup_t
*sg
, ilb_server_data_t
*sr
,
304 ilb_ip_addr_t
*ip1
, ilb_ip_addr_t
*ip2
)
312 if (ip1
->ia_af
!= ip2
->ia_af
) {
313 ilbadm_err(gettext("IP address mismatch"));
314 return (ILBADM_LIBERR
);
317 /* if ip addresses are the same, we're done */
318 if ((cmp
= ilb_cmp_ipaddr(ip1
, ip2
, &delta
)) == 0)
321 ilbadm_err(gettext("starting IP address is must be less"
322 " than ending ip address in ip range specification"));
323 return (ILBADM_LIBERR
);
326 /* if the implicit number of IPs is too large, stop */
327 if (abs((int)delta
) > MAX_IP_SPREAD
)
328 return (ILBADM_TOOMANYIPADDR
);
330 switch (ip1
->ia_af
) {
332 return (i_expand_ipv4range(sg
, sr
, ip1
, ip2
));
334 return (i_expand_ipv6range(sg
, sr
, ip1
, ip2
));
336 return (ILBADM_INVAL_AF
);
340 * parse a port spec (number or by service name) and
341 * return the numeric port in *host* byte order
343 * Upon return, *flags contains ILB_FLAGS_SRV_PORTNAME if a service name matches
346 i_parseport(char *port
, char *proto
, int *flags
)
350 /* assumption: port names start with a non-digit */
351 if (isdigit(port
[0])) {
353 *flags
&= ~ILB_FLAGS_SRV_PORTNAME
;
354 return ((int)strtol(port
, NULL
, 10));
357 se
= getservbyname(port
, proto
);
362 *flags
|= ILB_FLAGS_SRV_PORTNAME
;
365 * we need to convert to host byte order to be in sync with
366 * numerical ports. since result needs to be compared, this
367 * is preferred to returning NW byte order
369 return ((int)(ntohs(se
->s_port
)));
373 * matches one hostname or IP address and stores it in "store".
374 * space must have been pre-allocated to accept data
375 * "sg" != NULL only for cases where ip ranges may be coming in.
377 static ilbadm_status_t
378 i_match_hostorip(void *store
, ilbadm_sgroup_t
*sg
, char *val
,
379 int flags
, ilbadm_key_code_t keyword
)
381 boolean_t is_ip_range_ok
= flags
& OPT_IP_RANGE
;
382 boolean_t is_addr_numeric
= flags
& OPT_NUMERIC_ONLY
;
383 boolean_t is_ports_ok
= flags
& OPT_PORTS
;
384 boolean_t ports_only
= flags
& OPT_PORTS_ONLY
;
385 boolean_t is_nat_src
= flags
& OPT_NAT
;
386 char *port_pref
, *dash
;
387 char *port1p
, *port2p
, *host2p
, *host1p
;
388 char *close1
, *close2
;
389 ilb_ip_addr_t ip2store
;
390 ilb_ip_addr_t
*ip1
, *ip2
;
392 ilb_server_data_t
*s
= NULL
;
393 ilbadm_status_t rc
= ILBADM_OK
;
397 struct in6_addr v6nameaddr
;
399 port1p
= port2p
= host2p
= host1p
= NULL
;
400 port_pref
= dash
= NULL
;
401 close1
= close2
= NULL
;
405 ilb_rule_data_t
*rd
= (ilb_rule_data_t
*)store
;
407 ip1
= &rd
->r_nat_src_start
;
408 ip2
= &rd
->r_nat_src_end
;
410 ilbadm_servnode_t
*sn
= (ilbadm_servnode_t
*)store
;
415 bzero(ip2
, sizeof (*ip2
));
419 is_ports_ok
= B_TRUE
;
420 port_pref
= val
- 1; /* we increment again later on */
425 * we parse the syntax ip[-ip][:port[-port]]
426 * since IPv6 addresses contain ':'s as well, they need to be
427 * enclosed in "[]" to be distinct from a potential port spec.
428 * therefore, we need to first check whether we're dealing with
429 * IPv6 addresses before we can go search for the port seperator
430 * and ipv6 range could look like this: [ff::0]-[ff::255]:80
432 if ((keyword
== ILB_KEY_SERVER
) && (strchr(val
, ':') != NULL
) &&
433 (*val
!= '[') && ((inet_pton(AF_INET6
, val
, &v6nameaddr
)) != 0)) {
435 * V6 addresses must be enclosed within
436 * brackets when specifying server addresses
438 rc
= ILBADM_INVAL_SYNTAX
;
448 close1
= strchr(val
, (int)']');
449 if (close1
== NULL
) {
450 rc
= ILBADM_INVAL_SYNTAX
;
455 rc
= i_match_onehost(host1p
, ip1
, &at
);
459 rc
= ILBADM_INVAL_ADDR
;
462 if (ip1
->ia_af
!= af
) {
463 rc
= ILBADM_INVAL_AF
;
468 if (*val
== PORT_SEP
) {
474 if (!is_ip_range_ok
) {
475 ilbadm_err(gettext("port ranges not allowed"));
481 rc
= ILBADM_INVAL_SYNTAX
;
485 close2
= strchr(val
, (int)']');
486 if (close2
== NULL
) {
487 rc
= ILBADM_INVAL_SYNTAX
;
493 rc
= i_match_onehost(host2p
, ip2
, &at
);
497 rc
= ILBADM_INVAL_ADDR
;
500 if (ip2
->ia_af
!= af
) {
501 rc
= ILBADM_INVAL_AF
;
508 /* ports always potentially allow ranges - XXXms: check? */
509 port_pref
= strchr(val
, (int)PORT_SEP
);
511 if (port_pref
!= NULL
&& is_ports_ok
) {
512 port1p
= port_pref
+ 1;
515 dash
= strchr(port1p
, (int)'-');
520 if (port1p
!= NULL
) {
521 p1
= i_parseport(port1p
, NULL
, &p_flg
);
522 if (p1
== -1 || p1
== 0 || p1
> ILB_MAX_PORT
) {
523 ilbadm_err(gettext("invalid port value %s"
524 " specified"), port1p
);
528 s
->sd_minport
= htons((in_port_t
)p1
);
529 if (p_flg
& ILB_FLAGS_SRV_PORTNAME
)
530 s
->sd_flags
|= ILB_FLAGS_SRV_PORTNAME
;
532 if (port2p
!= NULL
) {
533 /* ranges are only allowed for numeric ports */
534 if (p_flg
& ILB_FLAGS_SRV_PORTNAME
) {
535 ilbadm_err(gettext("ranges are only allowed"
536 " for numeric ports"));
540 p2
= i_parseport(port2p
, NULL
, &p_flg
);
541 if (p2
== -1 || p2
<= p1
|| p2
> ILB_MAX_PORT
||
542 (p_flg
& ILB_FLAGS_SRV_PORTNAME
) ==
543 ILB_FLAGS_SRV_PORTNAME
) {
544 ilbadm_err(gettext("invalid port value %s"
545 " specified"), port2p
);
549 s
->sd_maxport
= htons((in_port_t
)p2
);
552 * we fill the '-' back in, but not the port seperator,
553 * as the \0 in its place terminates the ip address(es)
565 * we need to handle these situations for hosts:
567 * b. ip address range (ip1-ip2)
568 * c. a hostname (may include '-' or start with a digit)
570 * We want to do hostname lookup only if we're quite sure that
571 * we actually are looking at neither a single IP address nor a
572 * range of same, as this can hang if name service is not set up
573 * (sth. likely in a LB environment).
575 * here's how we proceed:
576 * 1. try to match numeric only. If that succeeds, we're done.
577 * (getaddrinfo, which we call in i_match_onehost(), fails if
578 * it encounters a '-')
579 * 2. search for a '-'; if we find one, try numeric match for
580 * both sides. if this fails:
581 * 3. re-insert '-' and try for a legal hostname.
585 rc
= i_match_onehost(val
, ip1
, &at
);
590 dash
= strchr(val
, (int)'-');
591 if (dash
!= NULL
&& is_ip_range_ok
) {
595 rc
= i_match_onehost(host2p
, ip2
, &at
);
596 if (rc
!= ILBADM_OK
|| at
!= numeric
) {
599 bzero(ip2
, sizeof (*ip2
));
603 * if the RHS of '-' is an IP but LHS is not, we might
604 * have a hostname of form x-y where y is just a number
605 * (this seems a valid IPv4 address), so we need to
606 * try a complete hostname
608 rc
= i_match_onehost(val
, ip1
, &at
);
609 if (rc
!= ILBADM_OK
|| at
!= numeric
) {
623 rc
= i_match_onehost(val
, ip1
, &at
);
624 if (rc
!= ILBADM_OK
) {
628 s
->sd_flags
|= ILB_FLAGS_SRV_HOSTNAME
;
629 /* XXX: todo: save hostname for re-display for admin */
633 if (dash
!= NULL
&& !is_nat_src
) {
634 rc
= i_expand_iprange(sg
, s
, ip1
, ip2
);
639 if (is_nat_src
&& host2p
== NULL
)
644 * we re-insert what we overwrote, especially in the error case
652 if (port_pref
!= NULL
&& !ports_only
)
653 *port_pref
= PORT_SEP
;
659 * type-agnostic helper function to return a pointer to a
660 * pristine (and maybe freshly allocated) piece of storage
661 * ready for something fitting "key"
664 i_new_storep(void *store
, ilbadm_key_code_t key
)
670 case ILB_KEY_SERVRANGE
:
671 case ILB_KEY_SERVERID
:
672 res
= (void *) i_new_sg_elem(store
);
682 * make sure everything that needs to be there is there
685 i_check_rule_spec(ilb_rule_data_t
*rd
)
687 int32_t vip_af
= rd
->r_vip
.ia_af
;
688 ilb_ip_addr_t
*prxy_src
;
690 if (vip_af
!= AF_INET
&& vip_af
!= AF_INET6
)
691 return (ILBADM_INVAL_AF
);
693 if (*rd
->r_sgname
== '\0')
694 return (ILBADM_ENOSGNAME
);
696 if (rd
->r_algo
== 0 || rd
->r_topo
== 0) {
697 ilbadm_err(gettext("lbalg or type is unspecified"));
698 return (ILBADM_LIBERR
);
701 if (rd
->r_topo
== ILB_TOPO_NAT
) {
702 prxy_src
= &rd
->r_nat_src_start
;
703 if (prxy_src
->ia_af
!= vip_af
) {
704 ilbadm_err(gettext("proxy-src is either missing"
705 " or its address family does not"
706 " match that of the VIP address"));
707 return (ILBADM_LIBERR
);
710 /* extend as necessary */
716 * in parameter "sz" describes size (in bytes) of mask
719 mask_to_prefixlen(const uchar_t
*mask
, const int sz
)
727 * for every byte in the mask, we start with most significant
728 * bit and work our way down to the least significant bit; as
729 * long as we find the bit set, we add 1 to the length. the
730 * first unset bit we encounter terminates this process
732 for (i
= 0; i
< sz
; i
++) {
735 for (j
= 7; j
>= 0; j
--) {
736 if ((c
& tmask
) == 0)
746 ilbadm_mask_to_prefixlen(ilb_ip_addr_t
*ip
)
751 assert(af
== AF_INET
|| af
== AF_INET6
);
754 len
= mask_to_prefixlen((uchar_t
*)&ip
->ia_v4
.s_addr
,
758 len
= mask_to_prefixlen((uchar_t
*)&ip
->ia_v6
.s6_addr
,
765 /* copied from ifconfig.c, changed to return symbolic constants */
767 * Convert a prefix length to a mask.
768 * Returns 1 if ok. 0 otherwise.
769 * Assumes the mask array is zero'ed by the caller.
772 in_prefixlentomask(int prefixlen
, int maxlen
, uchar_t
*mask
)
774 if (prefixlen
< 0 || prefixlen
> maxlen
)
777 while (prefixlen
> 0) {
778 if (prefixlen
>= 8) {
783 *mask
|= 1 << (8 - prefixlen
);
790 ilbadm_set_netmask(char *val
, ilb_ip_addr_t
*ip
, int af
)
792 int prefixlen
, maxval
;
796 assert(af
== AF_INET
|| af
== AF_INET6
);
798 maxval
= (af
== AF_INET
) ? 32 : 128;
802 prefixlen
= strtol(val
, &end
, 10);
803 if ((val
== end
) || (*end
!= '\0')) {
804 ilbadm_err(gettext("invalid pmask provided"));
805 return (ILBADM_LIBERR
);
808 if (prefixlen
< 1 || prefixlen
> maxval
) {
809 ilbadm_err(gettext("invalid pmask provided (AF mismatch?)"));
810 return (ILBADM_LIBERR
);
815 r
= in_prefixlentomask(prefixlen
, maxval
,
816 (uchar_t
*)&ip
->ia_v4
.s_addr
);
819 r
= in_prefixlentomask(prefixlen
, maxval
,
820 (uchar_t
*)&ip
->ia_v6
.s6_addr
);
824 ilbadm_err(gettext("cannot convert %s to a netmask"), val
);
825 return (ILBADM_LIBERR
);
831 static ilbadm_status_t
832 i_store_val(char *val
, void *store
, ilbadm_key_code_t keyword
)
834 ilbadm_status_t rc
= ILBADM_OK
;
835 void *storep
= store
;
836 ilb_rule_data_t
*rd
= NULL
;
837 ilbadm_sgroup_t
*sg
= NULL
;
838 ilb_hc_info_t
*hc_info
= NULL
;
843 return (ILBADM_NOKEYWORD_VAL
);
845 /* some types need new storage, others don't */
848 case ILB_KEY_SERVERID
:
849 sg
= (ilbadm_sgroup_t
*)store
;
850 storep
= i_new_storep(store
, keyword
);
852 case ILB_KEY_HEALTHCHECK
:
853 case ILB_KEY_SERVERGROUP
:
854 rd
= (ilb_rule_data_t
*)store
;
856 case ILB_KEY_VIP
: /* fallthrough */
857 case ILB_KEY_PORT
: /* fallthrough */
858 case ILB_KEY_HCPORT
: /* fallthrough */
859 case ILB_KEY_CONNDRAIN
: /* fallthrough */
860 case ILB_KEY_NAT_TO
: /* fallthrough */
861 case ILB_KEY_STICKY_TO
: /* fallthrough */
862 case ILB_KEY_PROTOCOL
: /* fallthrough */
863 case ILB_KEY_ALGORITHM
: /* fallthrough */
864 case ILB_KEY_STICKY
: /* fallthrough */
865 case ILB_KEY_TYPE
: /* fallthrough */
866 case ILB_KEY_SRC
: /* fallthrough */
867 rd
= (ilb_rule_data_t
*)store
;
869 case ILB_KEY_HC_TEST
:
870 case ILB_KEY_HC_COUNT
:
871 case ILB_KEY_HC_INTERVAL
:
872 case ILB_KEY_HC_TIMEOUT
:
873 hc_info
= (ilb_hc_info_t
*)store
;
874 default: /* do nothing */
881 * the proxy-src keyword is only valid for full NAT topology
882 * the value is either a single or a range of IP addresses.
884 if (rd
->r_topo
!= ILB_TOPO_NAT
) {
885 rc
= ILBADM_INVAL_PROXY
;
888 rc
= i_match_hostorip(storep
, sg
, val
, OPT_NUMERIC_ONLY
|
889 OPT_IP_RANGE
| OPT_NAT
, ILB_KEY_SRC
);
892 rc
= i_match_hostorip(storep
, sg
, val
,
893 OPT_IP_RANGE
| OPT_PORTS
, ILB_KEY_SERVER
);
895 case ILB_KEY_SERVERID
:
896 if (val
[0] != ILB_SRVID_PREFIX
)
897 rc
= ILBADM_INVAL_SRVID
;
899 rc
= i_store_serverID(storep
, val
);
902 ilb_ip_addr_t
*vip
= &rd
->r_vip
;
903 addr_type_t at
= numeric
;
907 * we duplicate some functionality of i_match_hostorip
908 * here; that function is geared to mandate '[]' for IPv6
909 * addresses, which we want to relax here, so as not to
910 * make i_match_hostorip even longer, we do what we need
915 if ((close
= strchr(val
, (int)']')) == NULL
) {
916 rc
= ILBADM_INVAL_SYNTAX
;
921 rc
= i_match_onehost(val
, vip
, &at
);
922 /* re-assemble string as we found it */
925 if (rc
== ILBADM_OK
&& vip
->ia_af
!= AF_INET6
) {
926 ilbadm_err(gettext("use of '[]' only valid"
927 " with IPv6 addresses"));
933 case ILB_KEY_CONNDRAIN
:
934 tmp_val
= strtoll(val
, NULL
, 10);
935 if (tmp_val
<= 0 || tmp_val
> UINT_MAX
) {
939 rd
->r_conndrain
= tmp_val
;
942 tmp_val
= strtoll(val
, NULL
, 10);
943 if (tmp_val
< 0 || tmp_val
> UINT_MAX
) {
947 rd
->r_nat_timeout
= tmp_val
;
949 case ILB_KEY_STICKY_TO
:
950 tmp_val
= strtoll(val
, NULL
, 10);
951 if (tmp_val
<= 0 || tmp_val
> UINT_MAX
) {
955 rd
->r_sticky_timeout
= tmp_val
;
959 ilbadm_servnode_t sn
;
961 bzero(&sn
, sizeof (sn
));
962 rc
= i_match_hostorip((void *)&sn
, sg
, val
,
963 OPT_PORTS_ONLY
, ILB_KEY_PORT
);
966 rd
->r_minport
= sn
.s_spec
.sd_minport
;
967 rd
->r_maxport
= sn
.s_spec
.sd_maxport
;
971 se
= getservbyname(val
, NULL
);
973 rc
= ILBADM_ENOSERVICE
;
976 rd
->r_minport
= se
->s_port
;
982 int hcport
= atoi(val
);
984 if (hcport
< 1 || hcport
> 65535) {
985 ilbadm_err(gettext("illegal number for"
990 rd
->r_hcport
= htons(hcport
);
991 rd
->r_hcpflag
= ILB_HCI_PROBE_FIX
;
992 } else if (strcasecmp(val
, "ANY") == 0) {
994 rd
->r_hcpflag
= ILB_HCI_PROBE_ANY
;
996 return (ILBADM_EINVAL
);
999 case ILB_KEY_PROTOCOL
:
1000 pe
= getprotobyname(val
);
1002 rc
= ILBADM_ENOPROTO
;
1004 rd
->r_proto
= pe
->p_proto
;
1006 case ILB_KEY_ALGORITHM
:
1007 rd
->r_algo
= i_val_from_str(val
, &algo_types
[0]);
1008 if (rd
->r_algo
== ILBD_BAD_VAL
)
1009 rc
= ILBADM_INVAL_ALG
;
1011 case ILB_KEY_STICKY
:
1012 rd
->r_flags
|= ILB_FLAGS_RULE_STICKY
;
1014 * CAVEAT: the use of r_vip.ia_af implies that the VIP
1015 * *must* be specified on the commandline *before*
1018 if (AF_UNSPEC
== rd
->r_vip
.ia_af
) {
1019 ilbadm_err(gettext("option '%s' requires that VIP be "
1020 "specified first"), ilbadm_key_to_opt(keyword
));
1024 rc
= ilbadm_set_netmask(val
, &rd
->r_stickymask
,
1028 rd
->r_topo
= i_val_from_str(val
, &topo_types
[0]);
1029 if (rd
->r_topo
== ILBD_BAD_VAL
)
1030 rc
= ILBADM_INVAL_OPER
;
1032 case ILB_KEY_SERVERGROUP
:
1033 (void) strlcpy(rd
->r_sgname
, (char *)val
,
1034 sizeof (rd
->r_sgname
));
1036 case ILB_KEY_HEALTHCHECK
:
1037 (void) strlcpy(rd
->r_hcname
, (char *)val
,
1038 sizeof (rd
->r_hcname
));
1040 case ILB_KEY_HC_TEST
:
1041 (void) strlcpy(hc_info
->hci_test
, (char *)val
,
1042 sizeof (hc_info
->hci_test
));
1044 case ILB_KEY_HC_COUNT
:
1046 hc_info
->hci_count
= atoi(val
);
1048 return (ILBADM_EINVAL
);
1050 case ILB_KEY_HC_INTERVAL
:
1052 hc_info
->hci_interval
= atoi(val
);
1054 return (ILBADM_EINVAL
);
1056 case ILB_KEY_HC_TIMEOUT
:
1058 hc_info
->hci_timeout
= atoi(val
);
1060 return (ILBADM_EINVAL
);
1062 default: rc
= ILBADM_INVAL_KEYWORD
;
1070 * generic parsing function.
1071 * parses "key=value[,value]" strings in "arg". keylist determines the
1072 * list of valid keys in the LHS. keycode determines interpretation and
1074 * XXXms: looks like "key=value[,value]" violates spec. needs a fix
1077 i_parse_optstring(char *arg
, void *store
, ilbadm_key_name_t
*keylist
,
1078 int flags
, int *count
)
1080 ilbadm_status_t rc
= ILBADM_OK
;
1081 char *comma
= NULL
, *equals
= NULL
;
1082 char *key
, *nextkey
, *val
;
1083 ilbadm_key_code_t keyword
;
1084 boolean_t is_value_list
= flags
& OPT_VALUE_LIST
;
1085 boolean_t assign_seen
= B_FALSE
;
1092 * 1. find any commas indicating and seperating current value
1093 * from a following value
1094 * 2. if we're expecting a list of values (seperated by commas)
1095 * and have already seen the assignment, then
1096 * get the next "value"
1097 * 3. else (we're looking at the first element of the RHS)
1099 * 5. match the keyword to the list we were passed in
1100 * 6. store the value.
1102 while (key
!= NULL
&& *key
!= '\0') {
1103 comma
= equals
= NULL
;
1106 nextkey
= strchr(key
, (int)',');
1107 if (nextkey
!= NULL
) {
1113 if (is_value_list
&& assign_seen
) {
1118 equals
= strchr(key
, (int)'=');
1119 if (equals
== NULL
) {
1120 ilbadm_err("%s: %s", key
,
1121 ilbadm_errstr(ILBADM_ASSIGNREQ
));
1127 assign_seen
= B_TRUE
;
1130 keyword
= i_match_key(key
, keylist
);
1131 if (keyword
== ILB_KEY_BAD
) {
1132 ilbadm_err(gettext("bad keyword %s"), key
);
1139 rc
= i_store_val(val
, store
, keyword
);
1140 if (rc
!= ILBADM_OK
) {
1141 ilbadm_err("%s: %s", key
, ilbadm_errstr(rc
));
1142 /* Change to ILBADM_ILBERR to avoid more err msgs. */