1dbed73cbSSangeeta Misra /*
2dbed73cbSSangeeta Misra * CDDL HEADER START
3dbed73cbSSangeeta Misra *
4dbed73cbSSangeeta Misra * The contents of this file are subject to the terms of the
5dbed73cbSSangeeta Misra * Common Development and Distribution License (the "License").
6dbed73cbSSangeeta Misra * You may not use this file except in compliance with the License.
7dbed73cbSSangeeta Misra *
8dbed73cbSSangeeta Misra * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9dbed73cbSSangeeta Misra * or http://www.opensolaris.org/os/licensing.
10dbed73cbSSangeeta Misra * See the License for the specific language governing permissions
11dbed73cbSSangeeta Misra * and limitations under the License.
12dbed73cbSSangeeta Misra *
13dbed73cbSSangeeta Misra * When distributing Covered Code, include this CDDL HEADER in each
14dbed73cbSSangeeta Misra * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15dbed73cbSSangeeta Misra * If applicable, add the following below this CDDL HEADER, with the
16dbed73cbSSangeeta Misra * fields enclosed by brackets "[]" replaced with your own identifying
17dbed73cbSSangeeta Misra * information: Portions Copyright [yyyy] [name of copyright owner]
18dbed73cbSSangeeta Misra *
19dbed73cbSSangeeta Misra * CDDL HEADER END
20dbed73cbSSangeeta Misra */
21dbed73cbSSangeeta Misra
22dbed73cbSSangeeta Misra /*
23dbed73cbSSangeeta Misra * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24dbed73cbSSangeeta Misra * Use is subject to license terms.
25*33f5ff17SMilan Jurik * Copyright 2012 Milan Jurik. All rights reserved.
26dbed73cbSSangeeta Misra */
27dbed73cbSSangeeta Misra
28dbed73cbSSangeeta Misra #include <stdio.h>
29dbed73cbSSangeeta Misra #include <unistd.h>
30dbed73cbSSangeeta Misra #include <stdlib.h>
31dbed73cbSSangeeta Misra #include <strings.h>
32dbed73cbSSangeeta Misra #include <sys/types.h>
33dbed73cbSSangeeta Misra #include <sys/socket.h>
34dbed73cbSSangeeta Misra #include <netinet/in.h>
35dbed73cbSSangeeta Misra #include <arpa/inet.h>
36dbed73cbSSangeeta Misra #include <netdb.h>
37dbed73cbSSangeeta Misra #include <errno.h>
38dbed73cbSSangeeta Misra #include <ctype.h>
39dbed73cbSSangeeta Misra #include <assert.h>
40dbed73cbSSangeeta Misra #include <limits.h>
41dbed73cbSSangeeta Misra #include <libilb.h>
42dbed73cbSSangeeta Misra #include <libilb_impl.h>
43dbed73cbSSangeeta Misra #include "ilbadm.h"
44dbed73cbSSangeeta Misra
45dbed73cbSSangeeta Misra #define PORT_SEP ':'
46dbed73cbSSangeeta Misra
47dbed73cbSSangeeta Misra typedef enum {
48dbed73cbSSangeeta Misra numeric = 1,
49dbed73cbSSangeeta Misra non_numeric
50dbed73cbSSangeeta Misra } addr_type_t;
51dbed73cbSSangeeta Misra
52dbed73cbSSangeeta Misra ilbadm_val_type_t algo_types[] = {
53dbed73cbSSangeeta Misra {(int)ILB_ALG_ROUNDROBIN, "roundrobin", "rr"},
54dbed73cbSSangeeta Misra {(int)ILB_ALG_HASH_IP, "hash-ip", "hip"},
55dbed73cbSSangeeta Misra {(int)ILB_ALG_HASH_IP_SPORT, "hash-ip-port", "hipp"},
56dbed73cbSSangeeta Misra {(int)ILB_ALG_HASH_IP_VIP, "hash-ip-vip", "hipv"},
57dbed73cbSSangeeta Misra {ILBD_BAD_VAL, NULL, NULL}
58dbed73cbSSangeeta Misra };
59dbed73cbSSangeeta Misra
60dbed73cbSSangeeta Misra ilbadm_val_type_t topo_types[] = {
61dbed73cbSSangeeta Misra {(int)ILB_TOPO_DSR, "DSR", "d"},
62dbed73cbSSangeeta Misra {(int)ILB_TOPO_NAT, "NAT", "n"},
63dbed73cbSSangeeta Misra {(int)ILB_TOPO_HALF_NAT, "HALF-NAT", "h"},
64dbed73cbSSangeeta Misra {ILBD_BAD_VAL, NULL, NULL}
65dbed73cbSSangeeta Misra };
66dbed73cbSSangeeta Misra
67dbed73cbSSangeeta Misra void
ip2str(ilb_ip_addr_t * ip,char * buf,size_t sz,int flags)68dbed73cbSSangeeta Misra ip2str(ilb_ip_addr_t *ip, char *buf, size_t sz, int flags)
69dbed73cbSSangeeta Misra {
70dbed73cbSSangeeta Misra int len;
71dbed73cbSSangeeta Misra
72dbed73cbSSangeeta Misra switch (ip->ia_af) {
73dbed73cbSSangeeta Misra case AF_INET:
74dbed73cbSSangeeta Misra if (*(uint32_t *)&ip->ia_v4 == 0)
75dbed73cbSSangeeta Misra buf[0] = '\0';
76dbed73cbSSangeeta Misra else
77dbed73cbSSangeeta Misra (void) inet_ntop(AF_INET, (void *)&ip->ia_v4, buf, sz);
78dbed73cbSSangeeta Misra break;
79dbed73cbSSangeeta Misra case AF_INET6:
80dbed73cbSSangeeta Misra if (IN6_IS_ADDR_UNSPECIFIED(&ip->ia_v6)) {
81dbed73cbSSangeeta Misra buf[0] = '\0';
82dbed73cbSSangeeta Misra break;
83dbed73cbSSangeeta Misra }
84dbed73cbSSangeeta Misra if (!(flags & V6_ADDRONLY))
85dbed73cbSSangeeta Misra *buf++ = '[';
86dbed73cbSSangeeta Misra sz--;
87dbed73cbSSangeeta Misra (void) inet_ntop(ip->ia_af, (void *)&ip->ia_v6, buf, sz);
88dbed73cbSSangeeta Misra if (!(flags & V6_ADDRONLY)) {
89dbed73cbSSangeeta Misra len = strlen(buf);
90dbed73cbSSangeeta Misra buf[len] = ']';
91dbed73cbSSangeeta Misra buf[++len] = '\0';
92dbed73cbSSangeeta Misra }
93dbed73cbSSangeeta Misra break;
94dbed73cbSSangeeta Misra default: buf[0] = '\0';
95dbed73cbSSangeeta Misra }
96dbed73cbSSangeeta Misra }
97dbed73cbSSangeeta Misra
98dbed73cbSSangeeta Misra char *
i_str_from_val(int val,ilbadm_val_type_t * types)99dbed73cbSSangeeta Misra i_str_from_val(int val, ilbadm_val_type_t *types)
100dbed73cbSSangeeta Misra {
101dbed73cbSSangeeta Misra ilbadm_val_type_t *v;
102dbed73cbSSangeeta Misra
103dbed73cbSSangeeta Misra for (v = types; v->v_type != ILBD_BAD_VAL; v++) {
104dbed73cbSSangeeta Misra if (v->v_type == val)
105dbed73cbSSangeeta Misra break;
106dbed73cbSSangeeta Misra }
107dbed73cbSSangeeta Misra /* we return this in all cases */
108dbed73cbSSangeeta Misra return (v->v_name);
109dbed73cbSSangeeta Misra }
110dbed73cbSSangeeta Misra
111dbed73cbSSangeeta Misra int
i_val_from_str(char * name,ilbadm_val_type_t * types)112dbed73cbSSangeeta Misra i_val_from_str(char *name, ilbadm_val_type_t *types)
113dbed73cbSSangeeta Misra {
114dbed73cbSSangeeta Misra ilbadm_val_type_t *v;
115dbed73cbSSangeeta Misra
116dbed73cbSSangeeta Misra for (v = types; v->v_type != ILBD_BAD_VAL; v++) {
117dbed73cbSSangeeta Misra if (strncasecmp(name, v->v_name, sizeof (v->v_name)) == 0 ||
118dbed73cbSSangeeta Misra strncasecmp(name, v->v_alias, sizeof (v->v_alias)) == 0)
119dbed73cbSSangeeta Misra break;
120dbed73cbSSangeeta Misra }
121dbed73cbSSangeeta Misra /* we return this in all cases */
122dbed73cbSSangeeta Misra return (v->v_type);
123dbed73cbSSangeeta Misra }
124dbed73cbSSangeeta Misra
125dbed73cbSSangeeta Misra ilbadm_key_code_t
i_match_key(char * key,ilbadm_key_name_t * keylist)126dbed73cbSSangeeta Misra i_match_key(char *key, ilbadm_key_name_t *keylist)
127dbed73cbSSangeeta Misra {
128dbed73cbSSangeeta Misra ilbadm_key_name_t *t_key;
129dbed73cbSSangeeta Misra
130dbed73cbSSangeeta Misra for (t_key = keylist; t_key->k_key != ILB_KEY_BAD; t_key++) {
131dbed73cbSSangeeta Misra if (strncasecmp(key, t_key->k_name,
132dbed73cbSSangeeta Misra sizeof (t_key->k_name)) == 0 ||
133dbed73cbSSangeeta Misra strncasecmp(key, t_key->k_alias,
134dbed73cbSSangeeta Misra sizeof (t_key->k_alias)) == 0)
135dbed73cbSSangeeta Misra break;
136dbed73cbSSangeeta Misra }
137dbed73cbSSangeeta Misra return (t_key->k_key);
138dbed73cbSSangeeta Misra }
139dbed73cbSSangeeta Misra
140dbed73cbSSangeeta Misra /*
141dbed73cbSSangeeta Misra * try to match:
142dbed73cbSSangeeta Misra * 1) IPv4 address
143dbed73cbSSangeeta Misra * 2) IPv6 address
144dbed73cbSSangeeta Misra * 3) a hostname
145dbed73cbSSangeeta Misra */
146dbed73cbSSangeeta Misra static ilbadm_status_t
i_match_onehost(const char * val,ilb_ip_addr_t * ip,addr_type_t * a_type)147dbed73cbSSangeeta Misra i_match_onehost(const char *val, ilb_ip_addr_t *ip, addr_type_t *a_type)
148dbed73cbSSangeeta Misra {
149dbed73cbSSangeeta Misra struct addrinfo *ai = NULL;
150dbed73cbSSangeeta Misra struct addrinfo hints;
151dbed73cbSSangeeta Misra addr_type_t at = numeric;
152dbed73cbSSangeeta Misra
153dbed73cbSSangeeta Misra (void) memset((void *)&hints, 0, sizeof (hints));
154dbed73cbSSangeeta Misra hints.ai_flags |= AI_NUMERICHOST;
155dbed73cbSSangeeta Misra
156dbed73cbSSangeeta Misra /*
157dbed73cbSSangeeta Misra * if *a_type == numeric, we only want to check whether this
158dbed73cbSSangeeta Misra * is a (valid) numeric IP address. If we do and it is NOT,
159dbed73cbSSangeeta Misra * we return _ENOENT.
160dbed73cbSSangeeta Misra */
161dbed73cbSSangeeta Misra if (getaddrinfo(val, NULL, &hints, &ai) != 0) {
162dbed73cbSSangeeta Misra if (a_type != NULL && (*a_type == numeric))
163dbed73cbSSangeeta Misra return (ILBADM_INVAL_ADDR);
164dbed73cbSSangeeta Misra
165dbed73cbSSangeeta Misra at = non_numeric;
166dbed73cbSSangeeta Misra if (getaddrinfo(val, NULL, NULL, &ai) != 0)
167dbed73cbSSangeeta Misra return (ILBADM_INVAL_ADDR);
168dbed73cbSSangeeta Misra }
169dbed73cbSSangeeta Misra
170dbed73cbSSangeeta Misra ip->ia_af = ai->ai_family;
171dbed73cbSSangeeta Misra switch (ip->ia_af) {
172dbed73cbSSangeeta Misra case AF_INET: {
173dbed73cbSSangeeta Misra struct sockaddr_in sa;
174dbed73cbSSangeeta Misra
175dbed73cbSSangeeta Misra assert(ai->ai_addrlen == sizeof (sa));
176dbed73cbSSangeeta Misra (void) memcpy(&sa, ai->ai_addr, sizeof (sa));
177dbed73cbSSangeeta Misra ip->ia_v4 = sa.sin_addr;
178dbed73cbSSangeeta Misra break;
179dbed73cbSSangeeta Misra }
180dbed73cbSSangeeta Misra case AF_INET6: {
181dbed73cbSSangeeta Misra struct sockaddr_in6 sa;
182dbed73cbSSangeeta Misra
183dbed73cbSSangeeta Misra assert(ai->ai_addrlen == sizeof (sa));
184dbed73cbSSangeeta Misra (void) memcpy(&sa, ai->ai_addr, sizeof (sa));
185dbed73cbSSangeeta Misra ip->ia_v6 = sa.sin6_addr;
186dbed73cbSSangeeta Misra break;
187dbed73cbSSangeeta Misra }
188dbed73cbSSangeeta Misra default:
189dbed73cbSSangeeta Misra return (ILBADM_INVAL_AF);
190dbed73cbSSangeeta Misra }
191dbed73cbSSangeeta Misra
192dbed73cbSSangeeta Misra if (a_type != NULL)
193dbed73cbSSangeeta Misra *a_type = at;
194dbed73cbSSangeeta Misra return (ILBADM_OK);
195dbed73cbSSangeeta Misra }
196dbed73cbSSangeeta Misra
197dbed73cbSSangeeta Misra static ilbadm_status_t
i_store_serverID(void * store,char * val)198dbed73cbSSangeeta Misra i_store_serverID(void *store, char *val)
199dbed73cbSSangeeta Misra {
200dbed73cbSSangeeta Misra ilbadm_servnode_t *s = (ilbadm_servnode_t *)store;
201dbed73cbSSangeeta Misra ilb_server_data_t *sn = &s->s_spec;
202dbed73cbSSangeeta Misra
203dbed73cbSSangeeta Misra /*
204dbed73cbSSangeeta Misra * we shouldn't need to check for length here, as a name that's
205dbed73cbSSangeeta Misra * too long won't exist in the system anyway.
206dbed73cbSSangeeta Misra */
207dbed73cbSSangeeta Misra (void) strlcpy(sn->sd_srvID, val, sizeof (sn->sd_srvID));
208dbed73cbSSangeeta Misra return (ILBADM_OK);
209dbed73cbSSangeeta Misra }
210dbed73cbSSangeeta Misra
211dbed73cbSSangeeta Misra static struct in_addr
i_next_in_addr(struct in_addr * a,int dir)212dbed73cbSSangeeta Misra i_next_in_addr(struct in_addr *a, int dir)
213dbed73cbSSangeeta Misra {
214dbed73cbSSangeeta Misra struct in_addr new_in;
215dbed73cbSSangeeta Misra uint32_t iah;
216dbed73cbSSangeeta Misra
217dbed73cbSSangeeta Misra iah = ntohl(a->s_addr);
218dbed73cbSSangeeta Misra if (dir == 1)
219dbed73cbSSangeeta Misra iah++;
220dbed73cbSSangeeta Misra else
221dbed73cbSSangeeta Misra iah--;
222dbed73cbSSangeeta Misra new_in.s_addr = htonl(iah);
223dbed73cbSSangeeta Misra return (new_in);
224dbed73cbSSangeeta Misra }
225dbed73cbSSangeeta Misra
226dbed73cbSSangeeta Misra 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)227dbed73cbSSangeeta Misra i_expand_ipv4range(ilbadm_sgroup_t *sg, ilb_server_data_t *srv,
228dbed73cbSSangeeta Misra ilb_ip_addr_t *ip1, ilb_ip_addr_t *ip2)
229dbed73cbSSangeeta Misra {
230dbed73cbSSangeeta Misra struct in_addr *a1;
231dbed73cbSSangeeta Misra ilbadm_servnode_t *sn_new;
232dbed73cbSSangeeta Misra ilb_ip_addr_t new_ip;
233dbed73cbSSangeeta Misra
234dbed73cbSSangeeta Misra a1 = &ip1->ia_v4;
235dbed73cbSSangeeta Misra
236dbed73cbSSangeeta Misra new_ip.ia_af = AF_INET;
237dbed73cbSSangeeta Misra new_ip.ia_v4 = i_next_in_addr(a1, 1);
238dbed73cbSSangeeta Misra while (ilb_cmp_ipaddr(&new_ip, ip2, NULL) < 1) {
239dbed73cbSSangeeta Misra sn_new = i_new_sg_elem(sg);
240dbed73cbSSangeeta Misra sn_new->s_spec.sd_addr = new_ip;
241dbed73cbSSangeeta Misra sn_new->s_spec.sd_minport = srv->sd_minport;
242dbed73cbSSangeeta Misra sn_new->s_spec.sd_maxport = srv->sd_maxport;
243dbed73cbSSangeeta Misra new_ip.ia_v4 = i_next_in_addr(&new_ip.ia_v4, 1);
244dbed73cbSSangeeta Misra }
245dbed73cbSSangeeta Misra return (ILBADM_OK);
246dbed73cbSSangeeta Misra }
247dbed73cbSSangeeta Misra
248dbed73cbSSangeeta Misra static struct in6_addr
i_next_in6_addr(struct in6_addr * a,int dir)249dbed73cbSSangeeta Misra i_next_in6_addr(struct in6_addr *a, int dir)
250dbed73cbSSangeeta Misra {
251dbed73cbSSangeeta Misra struct in6_addr ia6;
252dbed73cbSSangeeta Misra uint64_t al, ah;
253dbed73cbSSangeeta Misra
254dbed73cbSSangeeta Misra ah = INV6_N2H_MSB64(a);
255dbed73cbSSangeeta Misra al = INV6_N2H_LSB64(a);
256dbed73cbSSangeeta Misra
257dbed73cbSSangeeta Misra if (dir == 1) {
258dbed73cbSSangeeta Misra /* overflow */
259dbed73cbSSangeeta Misra if (++al == 0)
260dbed73cbSSangeeta Misra ah++;
261dbed73cbSSangeeta Misra } else {
262dbed73cbSSangeeta Misra /* underflow */
263dbed73cbSSangeeta Misra if (--al == 0xffffffff)
264dbed73cbSSangeeta Misra ah--;
265dbed73cbSSangeeta Misra }
266dbed73cbSSangeeta Misra
267dbed73cbSSangeeta Misra INV6_H2N_MSB64(&ia6, ah);
268dbed73cbSSangeeta Misra INV6_H2N_LSB64(&ia6, al);
269dbed73cbSSangeeta Misra return (ia6);
270dbed73cbSSangeeta Misra }
271dbed73cbSSangeeta Misra
272dbed73cbSSangeeta Misra
273dbed73cbSSangeeta Misra 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)274dbed73cbSSangeeta Misra i_expand_ipv6range(ilbadm_sgroup_t *sg, ilb_server_data_t *srv,
275dbed73cbSSangeeta Misra ilb_ip_addr_t *ip1, ilb_ip_addr_t *ip2)
276dbed73cbSSangeeta Misra {
277dbed73cbSSangeeta Misra struct in6_addr *a1;
278dbed73cbSSangeeta Misra ilbadm_servnode_t *sn_new;
279dbed73cbSSangeeta Misra ilb_ip_addr_t new_ip;
280dbed73cbSSangeeta Misra
281dbed73cbSSangeeta Misra a1 = &ip1->ia_v6;
282dbed73cbSSangeeta Misra
283dbed73cbSSangeeta Misra new_ip.ia_af = AF_INET6;
284dbed73cbSSangeeta Misra new_ip.ia_v6 = i_next_in6_addr(a1, 1);
285dbed73cbSSangeeta Misra while (ilb_cmp_ipaddr(&new_ip, ip2, NULL) < 1) {
286dbed73cbSSangeeta Misra sn_new = i_new_sg_elem(sg);
287dbed73cbSSangeeta Misra sn_new->s_spec.sd_addr = new_ip;
288dbed73cbSSangeeta Misra sn_new->s_spec.sd_minport = srv->sd_minport;
289dbed73cbSSangeeta Misra sn_new->s_spec.sd_maxport = srv->sd_maxport;
290dbed73cbSSangeeta Misra new_ip.ia_v6 = i_next_in6_addr(&new_ip.ia_v6, 1);
291dbed73cbSSangeeta Misra }
292dbed73cbSSangeeta Misra return (ILBADM_OK);
293dbed73cbSSangeeta Misra }
294dbed73cbSSangeeta Misra
295dbed73cbSSangeeta Misra
296dbed73cbSSangeeta Misra /*
297dbed73cbSSangeeta Misra * we create a list node in the servergroup for every ip address
298dbed73cbSSangeeta Misra * in the range [ip1, ip2], where we interpret the ip addresses as
299dbed73cbSSangeeta Misra * numbers
300dbed73cbSSangeeta Misra * the first ip address is already stored in "sn"
301dbed73cbSSangeeta Misra */
302dbed73cbSSangeeta Misra 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)303dbed73cbSSangeeta Misra i_expand_iprange(ilbadm_sgroup_t *sg, ilb_server_data_t *sr,
304dbed73cbSSangeeta Misra ilb_ip_addr_t *ip1, ilb_ip_addr_t *ip2)
305dbed73cbSSangeeta Misra {
306dbed73cbSSangeeta Misra int cmp;
307dbed73cbSSangeeta Misra int64_t delta;
308dbed73cbSSangeeta Misra
309dbed73cbSSangeeta Misra if (ip2->ia_af == 0)
310dbed73cbSSangeeta Misra return (ILBADM_OK);
311dbed73cbSSangeeta Misra
312dbed73cbSSangeeta Misra if (ip1->ia_af != ip2->ia_af) {
313dbed73cbSSangeeta Misra ilbadm_err(gettext("IP address mismatch"));
314dbed73cbSSangeeta Misra return (ILBADM_LIBERR);
315dbed73cbSSangeeta Misra }
316dbed73cbSSangeeta Misra
317dbed73cbSSangeeta Misra /* if ip addresses are the same, we're done */
318dbed73cbSSangeeta Misra if ((cmp = ilb_cmp_ipaddr(ip1, ip2, &delta)) == 0)
319dbed73cbSSangeeta Misra return (ILBADM_OK);
320dbed73cbSSangeeta Misra if (cmp == 1) {
321dbed73cbSSangeeta Misra ilbadm_err(gettext("starting IP address is must be less"
322dbed73cbSSangeeta Misra " than ending ip address in ip range specification"));
323dbed73cbSSangeeta Misra return (ILBADM_LIBERR);
324dbed73cbSSangeeta Misra }
325dbed73cbSSangeeta Misra
326dbed73cbSSangeeta Misra /* if the implicit number of IPs is too large, stop */
327dbed73cbSSangeeta Misra if (abs((int)delta) > MAX_IP_SPREAD)
328dbed73cbSSangeeta Misra return (ILBADM_TOOMANYIPADDR);
329dbed73cbSSangeeta Misra
330dbed73cbSSangeeta Misra switch (ip1->ia_af) {
331*33f5ff17SMilan Jurik case AF_INET:
332*33f5ff17SMilan Jurik return (i_expand_ipv4range(sg, sr, ip1, ip2));
333*33f5ff17SMilan Jurik case AF_INET6:
334*33f5ff17SMilan Jurik return (i_expand_ipv6range(sg, sr, ip1, ip2));
335dbed73cbSSangeeta Misra }
336dbed73cbSSangeeta Misra return (ILBADM_INVAL_AF);
337dbed73cbSSangeeta Misra }
338dbed73cbSSangeeta Misra
339dbed73cbSSangeeta Misra /*
340dbed73cbSSangeeta Misra * parse a port spec (number or by service name) and
341dbed73cbSSangeeta Misra * return the numeric port in *host* byte order
342dbed73cbSSangeeta Misra *
343dbed73cbSSangeeta Misra * Upon return, *flags contains ILB_FLAGS_SRV_PORTNAME if a service name matches
344dbed73cbSSangeeta Misra */
345dbed73cbSSangeeta Misra static int
i_parseport(char * port,char * proto,int * flags)346dbed73cbSSangeeta Misra i_parseport(char *port, char *proto, int *flags)
347dbed73cbSSangeeta Misra {
348dbed73cbSSangeeta Misra struct servent *se;
349dbed73cbSSangeeta Misra
350dbed73cbSSangeeta Misra /* assumption: port names start with a non-digit */
351dbed73cbSSangeeta Misra if (isdigit(port[0])) {
352dbed73cbSSangeeta Misra if (flags != NULL)
353dbed73cbSSangeeta Misra *flags &= ~ILB_FLAGS_SRV_PORTNAME;
354dbed73cbSSangeeta Misra return ((int)strtol(port, NULL, 10));
355dbed73cbSSangeeta Misra }
356dbed73cbSSangeeta Misra
357dbed73cbSSangeeta Misra se = getservbyname(port, proto);
358dbed73cbSSangeeta Misra if (se == NULL)
359dbed73cbSSangeeta Misra return (-1);
360dbed73cbSSangeeta Misra
361dbed73cbSSangeeta Misra if (flags != NULL)
362dbed73cbSSangeeta Misra *flags |= ILB_FLAGS_SRV_PORTNAME;
363dbed73cbSSangeeta Misra
364dbed73cbSSangeeta Misra /*
365dbed73cbSSangeeta Misra * we need to convert to host byte order to be in sync with
366dbed73cbSSangeeta Misra * numerical ports. since result needs to be compared, this
367dbed73cbSSangeeta Misra * is preferred to returning NW byte order
368dbed73cbSSangeeta Misra */
369dbed73cbSSangeeta Misra return ((int)(ntohs(se->s_port)));
370dbed73cbSSangeeta Misra }
371dbed73cbSSangeeta Misra
372dbed73cbSSangeeta Misra /*
373dbed73cbSSangeeta Misra * matches one hostname or IP address and stores it in "store".
374dbed73cbSSangeeta Misra * space must have been pre-allocated to accept data
375dbed73cbSSangeeta Misra * "sg" != NULL only for cases where ip ranges may be coming in.
376dbed73cbSSangeeta Misra */
377dbed73cbSSangeeta Misra static ilbadm_status_t
i_match_hostorip(void * store,ilbadm_sgroup_t * sg,char * val,int flags,ilbadm_key_code_t keyword)378dbed73cbSSangeeta Misra i_match_hostorip(void *store, ilbadm_sgroup_t *sg, char *val,
379dbed73cbSSangeeta Misra int flags, ilbadm_key_code_t keyword)
380dbed73cbSSangeeta Misra {
381dbed73cbSSangeeta Misra boolean_t is_ip_range_ok = flags & OPT_IP_RANGE;
382dbed73cbSSangeeta Misra boolean_t is_addr_numeric = flags & OPT_NUMERIC_ONLY;
383dbed73cbSSangeeta Misra boolean_t is_ports_ok = flags & OPT_PORTS;
384dbed73cbSSangeeta Misra boolean_t ports_only = flags & OPT_PORTS_ONLY;
385dbed73cbSSangeeta Misra boolean_t is_nat_src = flags & OPT_NAT;
386dbed73cbSSangeeta Misra char *port_pref, *dash;
387dbed73cbSSangeeta Misra char *port1p, *port2p, *host2p, *host1p;
388dbed73cbSSangeeta Misra char *close1, *close2;
389dbed73cbSSangeeta Misra ilb_ip_addr_t ip2store;
390dbed73cbSSangeeta Misra ilb_ip_addr_t *ip1, *ip2;
391dbed73cbSSangeeta Misra int p1, p2;
392dbed73cbSSangeeta Misra ilb_server_data_t *s = NULL;
393dbed73cbSSangeeta Misra ilbadm_status_t rc = ILBADM_OK;
394dbed73cbSSangeeta Misra int af = AF_INET;
395dbed73cbSSangeeta Misra addr_type_t at = 0;
396dbed73cbSSangeeta Misra int p_flg;
397dbed73cbSSangeeta Misra struct in6_addr v6nameaddr;
398dbed73cbSSangeeta Misra
399dbed73cbSSangeeta Misra port1p = port2p = host2p = host1p = NULL;
400dbed73cbSSangeeta Misra port_pref = dash = NULL;
401dbed73cbSSangeeta Misra close1 = close2 = NULL;
402dbed73cbSSangeeta Misra errno = 0;
403dbed73cbSSangeeta Misra
404dbed73cbSSangeeta Misra if (is_nat_src) {
405dbed73cbSSangeeta Misra ilb_rule_data_t *rd = (ilb_rule_data_t *)store;
406dbed73cbSSangeeta Misra
407dbed73cbSSangeeta Misra ip1 = &rd->r_nat_src_start;
408dbed73cbSSangeeta Misra ip2 = &rd->r_nat_src_end;
409dbed73cbSSangeeta Misra } else {
410dbed73cbSSangeeta Misra ilbadm_servnode_t *sn = (ilbadm_servnode_t *)store;
411dbed73cbSSangeeta Misra
412dbed73cbSSangeeta Misra s = &sn->s_spec;
413dbed73cbSSangeeta Misra ip1 = &s->sd_addr;
414dbed73cbSSangeeta Misra ip2 = &ip2store;
415dbed73cbSSangeeta Misra bzero(ip2, sizeof (*ip2));
416dbed73cbSSangeeta Misra }
417dbed73cbSSangeeta Misra
418dbed73cbSSangeeta Misra if (ports_only) {
419dbed73cbSSangeeta Misra is_ports_ok = B_TRUE;
420dbed73cbSSangeeta Misra port_pref = val - 1; /* we increment again later on */
421dbed73cbSSangeeta Misra goto ports;
422dbed73cbSSangeeta Misra }
423dbed73cbSSangeeta Misra
424dbed73cbSSangeeta Misra /*
425dbed73cbSSangeeta Misra * we parse the syntax ip[-ip][:port[-port]]
426dbed73cbSSangeeta Misra * since IPv6 addresses contain ':'s as well, they need to be
427dbed73cbSSangeeta Misra * enclosed in "[]" to be distinct from a potential port spec.
428dbed73cbSSangeeta Misra * therefore, we need to first check whether we're dealing with
429dbed73cbSSangeeta Misra * IPv6 addresses before we can go search for the port seperator
430dbed73cbSSangeeta Misra * and ipv6 range could look like this: [ff::0]-[ff::255]:80
431dbed73cbSSangeeta Misra */
432dbed73cbSSangeeta Misra if ((keyword == ILB_KEY_SERVER) && (strchr(val, ':') != NULL) &&
433dbed73cbSSangeeta Misra (*val != '[') && ((inet_pton(AF_INET6, val, &v6nameaddr)) != 0)) {
434dbed73cbSSangeeta Misra /*
435dbed73cbSSangeeta Misra * V6 addresses must be enclosed within
436dbed73cbSSangeeta Misra * brackets when specifying server addresses
437dbed73cbSSangeeta Misra */
438dbed73cbSSangeeta Misra rc = ILBADM_INVAL_SYNTAX;
439dbed73cbSSangeeta Misra goto err_out;
440dbed73cbSSangeeta Misra }
441dbed73cbSSangeeta Misra
442dbed73cbSSangeeta Misra if (*val == '[') {
443dbed73cbSSangeeta Misra af = AF_INET6;
444dbed73cbSSangeeta Misra
445dbed73cbSSangeeta Misra val++;
446dbed73cbSSangeeta Misra host1p = val;
447dbed73cbSSangeeta Misra
448dbed73cbSSangeeta Misra close1 = strchr(val, (int)']');
449dbed73cbSSangeeta Misra if (close1 == NULL) {
450dbed73cbSSangeeta Misra rc = ILBADM_INVAL_SYNTAX;
451dbed73cbSSangeeta Misra goto err_out;
452dbed73cbSSangeeta Misra }
453dbed73cbSSangeeta Misra *close1 = '\0';
454dbed73cbSSangeeta Misra at = 0;
455dbed73cbSSangeeta Misra rc = i_match_onehost(host1p, ip1, &at);
456dbed73cbSSangeeta Misra if (rc != ILBADM_OK)
457dbed73cbSSangeeta Misra goto err_out;
458dbed73cbSSangeeta Misra if (at != numeric) {
459dbed73cbSSangeeta Misra rc = ILBADM_INVAL_ADDR;
460dbed73cbSSangeeta Misra goto err_out;
461dbed73cbSSangeeta Misra }
462dbed73cbSSangeeta Misra if (ip1->ia_af != af) {
463dbed73cbSSangeeta Misra rc = ILBADM_INVAL_AF;
464dbed73cbSSangeeta Misra goto err_out;
465dbed73cbSSangeeta Misra }
466dbed73cbSSangeeta Misra val = close1 + 1;
467dbed73cbSSangeeta Misra
468dbed73cbSSangeeta Misra if (*val == PORT_SEP) {
469dbed73cbSSangeeta Misra port_pref = val;
470dbed73cbSSangeeta Misra goto ports;
471dbed73cbSSangeeta Misra }
472dbed73cbSSangeeta Misra if (*val == '-') {
473dbed73cbSSangeeta Misra dash = val;
474dbed73cbSSangeeta Misra if (!is_ip_range_ok) {
475dbed73cbSSangeeta Misra ilbadm_err(gettext("port ranges not allowed"));
476dbed73cbSSangeeta Misra rc = ILBADM_LIBERR;
477dbed73cbSSangeeta Misra goto err_out;
478dbed73cbSSangeeta Misra }
479dbed73cbSSangeeta Misra val++;
480dbed73cbSSangeeta Misra if (*val != '[') {
481dbed73cbSSangeeta Misra rc = ILBADM_INVAL_SYNTAX;
482dbed73cbSSangeeta Misra goto err_out;
483dbed73cbSSangeeta Misra }
484dbed73cbSSangeeta Misra val++;
485dbed73cbSSangeeta Misra close2 = strchr(val, (int)']');
486dbed73cbSSangeeta Misra if (close2 == NULL) {
487dbed73cbSSangeeta Misra rc = ILBADM_INVAL_SYNTAX;
488dbed73cbSSangeeta Misra goto err_out;
489dbed73cbSSangeeta Misra }
490dbed73cbSSangeeta Misra *close2 = '\0';
491dbed73cbSSangeeta Misra host2p = val;
492dbed73cbSSangeeta Misra at = 0;
493dbed73cbSSangeeta Misra rc = i_match_onehost(host2p, ip2, &at);
494dbed73cbSSangeeta Misra if (rc != ILBADM_OK)
495dbed73cbSSangeeta Misra goto err_out;
496dbed73cbSSangeeta Misra if (at != numeric) {
497dbed73cbSSangeeta Misra rc = ILBADM_INVAL_ADDR;
498dbed73cbSSangeeta Misra goto err_out;
499dbed73cbSSangeeta Misra }
500dbed73cbSSangeeta Misra if (ip2->ia_af != af) {
501dbed73cbSSangeeta Misra rc = ILBADM_INVAL_AF;
502dbed73cbSSangeeta Misra goto err_out;
503dbed73cbSSangeeta Misra }
504dbed73cbSSangeeta Misra val = close2+1;
505dbed73cbSSangeeta Misra }
506dbed73cbSSangeeta Misra }
507dbed73cbSSangeeta Misra
508dbed73cbSSangeeta Misra /* ports always potentially allow ranges - XXXms: check? */
509dbed73cbSSangeeta Misra port_pref = strchr(val, (int)PORT_SEP);
510dbed73cbSSangeeta Misra ports:
511dbed73cbSSangeeta Misra if (port_pref != NULL && is_ports_ok) {
512dbed73cbSSangeeta Misra port1p = port_pref + 1;
513dbed73cbSSangeeta Misra *port_pref = '\0';
514dbed73cbSSangeeta Misra
515dbed73cbSSangeeta Misra dash = strchr(port1p, (int)'-');
516dbed73cbSSangeeta Misra if (dash != NULL) {
517dbed73cbSSangeeta Misra port2p = dash + 1;
518dbed73cbSSangeeta Misra *dash = '\0';
519dbed73cbSSangeeta Misra }
520dbed73cbSSangeeta Misra if (port1p != NULL) {
521dbed73cbSSangeeta Misra p1 = i_parseport(port1p, NULL, &p_flg);
522dbed73cbSSangeeta Misra if (p1 == -1 || p1 == 0 || p1 > ILB_MAX_PORT) {
523dbed73cbSSangeeta Misra ilbadm_err(gettext("invalid port value %s"
524dbed73cbSSangeeta Misra " specified"), port1p);
525dbed73cbSSangeeta Misra rc = ILBADM_LIBERR;
526dbed73cbSSangeeta Misra goto err_out;
527dbed73cbSSangeeta Misra }
528dbed73cbSSangeeta Misra s->sd_minport = htons((in_port_t)p1);
529dbed73cbSSangeeta Misra if (p_flg & ILB_FLAGS_SRV_PORTNAME)
530dbed73cbSSangeeta Misra s->sd_flags |= ILB_FLAGS_SRV_PORTNAME;
531dbed73cbSSangeeta Misra }
532dbed73cbSSangeeta Misra if (port2p != NULL) {
533dbed73cbSSangeeta Misra /* ranges are only allowed for numeric ports */
534dbed73cbSSangeeta Misra if (p_flg & ILB_FLAGS_SRV_PORTNAME) {
535dbed73cbSSangeeta Misra ilbadm_err(gettext("ranges are only allowed"
536dbed73cbSSangeeta Misra " for numeric ports"));
537dbed73cbSSangeeta Misra rc = ILBADM_LIBERR;
538dbed73cbSSangeeta Misra goto err_out;
539dbed73cbSSangeeta Misra }
540dbed73cbSSangeeta Misra p2 = i_parseport(port2p, NULL, &p_flg);
541dbed73cbSSangeeta Misra if (p2 == -1 || p2 <= p1 || p2 > ILB_MAX_PORT ||
542dbed73cbSSangeeta Misra (p_flg & ILB_FLAGS_SRV_PORTNAME) ==
543dbed73cbSSangeeta Misra ILB_FLAGS_SRV_PORTNAME) {
544dbed73cbSSangeeta Misra ilbadm_err(gettext("invalid port value %s"
545dbed73cbSSangeeta Misra " specified"), port2p);
546dbed73cbSSangeeta Misra rc = ILBADM_LIBERR;
547dbed73cbSSangeeta Misra goto err_out;
548dbed73cbSSangeeta Misra }
549dbed73cbSSangeeta Misra s->sd_maxport = htons((in_port_t)p2);
550dbed73cbSSangeeta Misra }
551dbed73cbSSangeeta Misra /*
552dbed73cbSSangeeta Misra * we fill the '-' back in, but not the port seperator,
553dbed73cbSSangeeta Misra * as the \0 in its place terminates the ip address(es)
554dbed73cbSSangeeta Misra */
555dbed73cbSSangeeta Misra if (dash != NULL)
556dbed73cbSSangeeta Misra *dash = '-';
557dbed73cbSSangeeta Misra if (ports_only)
558dbed73cbSSangeeta Misra goto out;
559dbed73cbSSangeeta Misra }
560dbed73cbSSangeeta Misra
561dbed73cbSSangeeta Misra if (af == AF_INET6)
562dbed73cbSSangeeta Misra goto out;
563dbed73cbSSangeeta Misra
564dbed73cbSSangeeta Misra /*
565dbed73cbSSangeeta Misra * we need to handle these situations for hosts:
566dbed73cbSSangeeta Misra * a. ip address
567dbed73cbSSangeeta Misra * b. ip address range (ip1-ip2)
568dbed73cbSSangeeta Misra * c. a hostname (may include '-' or start with a digit)
569dbed73cbSSangeeta Misra *
570dbed73cbSSangeeta Misra * We want to do hostname lookup only if we're quite sure that
571dbed73cbSSangeeta Misra * we actually are looking at neither a single IP address nor a
572dbed73cbSSangeeta Misra * range of same, as this can hang if name service is not set up
573dbed73cbSSangeeta Misra * (sth. likely in a LB environment).
574dbed73cbSSangeeta Misra *
575dbed73cbSSangeeta Misra * here's how we proceed:
576dbed73cbSSangeeta Misra * 1. try to match numeric only. If that succeeds, we're done.
577dbed73cbSSangeeta Misra * (getaddrinfo, which we call in i_match_onehost(), fails if
578dbed73cbSSangeeta Misra * it encounters a '-')
579dbed73cbSSangeeta Misra * 2. search for a '-'; if we find one, try numeric match for
580dbed73cbSSangeeta Misra * both sides. if this fails:
581dbed73cbSSangeeta Misra * 3. re-insert '-' and try for a legal hostname.
582dbed73cbSSangeeta Misra */
583dbed73cbSSangeeta Misra /* 1. */
584dbed73cbSSangeeta Misra at = numeric;
585dbed73cbSSangeeta Misra rc = i_match_onehost(val, ip1, &at);
586dbed73cbSSangeeta Misra if (rc == ILBADM_OK)
587dbed73cbSSangeeta Misra goto out;
588dbed73cbSSangeeta Misra
589dbed73cbSSangeeta Misra /* 2. */
590dbed73cbSSangeeta Misra dash = strchr(val, (int)'-');
591dbed73cbSSangeeta Misra if (dash != NULL && is_ip_range_ok) {
592dbed73cbSSangeeta Misra host2p = dash + 1;
593dbed73cbSSangeeta Misra *dash = '\0';
594dbed73cbSSangeeta Misra at = numeric;
595dbed73cbSSangeeta Misra rc = i_match_onehost(host2p, ip2, &at);
596dbed73cbSSangeeta Misra if (rc != ILBADM_OK || at != numeric) {
597dbed73cbSSangeeta Misra *dash = '-';
598dbed73cbSSangeeta Misra dash = NULL;
599dbed73cbSSangeeta Misra bzero(ip2, sizeof (*ip2));
600dbed73cbSSangeeta Misra goto hostname;
601dbed73cbSSangeeta Misra }
602dbed73cbSSangeeta Misra /*
603dbed73cbSSangeeta Misra * if the RHS of '-' is an IP but LHS is not, we might
604dbed73cbSSangeeta Misra * have a hostname of form x-y where y is just a number
605dbed73cbSSangeeta Misra * (this seems a valid IPv4 address), so we need to
606dbed73cbSSangeeta Misra * try a complete hostname
607dbed73cbSSangeeta Misra */
608dbed73cbSSangeeta Misra rc = i_match_onehost(val, ip1, &at);
609dbed73cbSSangeeta Misra if (rc != ILBADM_OK || at != numeric) {
610dbed73cbSSangeeta Misra *dash = '-';
611dbed73cbSSangeeta Misra dash = NULL;
612dbed73cbSSangeeta Misra goto hostname;
613dbed73cbSSangeeta Misra }
614dbed73cbSSangeeta Misra goto out;
615dbed73cbSSangeeta Misra }
616dbed73cbSSangeeta Misra hostname:
617dbed73cbSSangeeta Misra /* 3. */
618dbed73cbSSangeeta Misra
619dbed73cbSSangeeta Misra if (is_addr_numeric)
620dbed73cbSSangeeta Misra at = numeric;
621dbed73cbSSangeeta Misra else
622dbed73cbSSangeeta Misra at = 0;
623dbed73cbSSangeeta Misra rc = i_match_onehost(val, ip1, &at);
624dbed73cbSSangeeta Misra if (rc != ILBADM_OK) {
625dbed73cbSSangeeta Misra goto out;
626dbed73cbSSangeeta Misra }
627dbed73cbSSangeeta Misra if (s != NULL) {
628dbed73cbSSangeeta Misra s->sd_flags |= ILB_FLAGS_SRV_HOSTNAME;
629dbed73cbSSangeeta Misra /* XXX: todo: save hostname for re-display for admin */
630dbed73cbSSangeeta Misra }
631dbed73cbSSangeeta Misra
632dbed73cbSSangeeta Misra out:
633dbed73cbSSangeeta Misra if (dash != NULL && !is_nat_src) {
634dbed73cbSSangeeta Misra rc = i_expand_iprange(sg, s, ip1, ip2);
635dbed73cbSSangeeta Misra if (rc != ILBADM_OK)
636dbed73cbSSangeeta Misra goto err_out;
637dbed73cbSSangeeta Misra }
638dbed73cbSSangeeta Misra
639dbed73cbSSangeeta Misra if (is_nat_src && host2p == NULL)
640dbed73cbSSangeeta Misra *ip2 = *ip1;
641dbed73cbSSangeeta Misra
642dbed73cbSSangeeta Misra err_out:
643dbed73cbSSangeeta Misra /*
644dbed73cbSSangeeta Misra * we re-insert what we overwrote, especially in the error case
645dbed73cbSSangeeta Misra */
646dbed73cbSSangeeta Misra if (close2 != NULL)
647dbed73cbSSangeeta Misra *close2 = ']';
648dbed73cbSSangeeta Misra if (close1 != NULL)
649dbed73cbSSangeeta Misra *close1 = '[';
650dbed73cbSSangeeta Misra if (dash != NULL)
651dbed73cbSSangeeta Misra *dash = '-';
652dbed73cbSSangeeta Misra if (port_pref != NULL && !ports_only)
653dbed73cbSSangeeta Misra *port_pref = PORT_SEP;
654dbed73cbSSangeeta Misra
655dbed73cbSSangeeta Misra return (rc);
656dbed73cbSSangeeta Misra }
657dbed73cbSSangeeta Misra
658dbed73cbSSangeeta Misra /*
659dbed73cbSSangeeta Misra * type-agnostic helper function to return a pointer to a
660dbed73cbSSangeeta Misra * pristine (and maybe freshly allocated) piece of storage
661dbed73cbSSangeeta Misra * ready for something fitting "key"
662dbed73cbSSangeeta Misra */
663dbed73cbSSangeeta Misra static void *
i_new_storep(void * store,ilbadm_key_code_t key)664dbed73cbSSangeeta Misra i_new_storep(void *store, ilbadm_key_code_t key)
665dbed73cbSSangeeta Misra {
666dbed73cbSSangeeta Misra void *res;
667dbed73cbSSangeeta Misra
668dbed73cbSSangeeta Misra switch (key) {
669dbed73cbSSangeeta Misra case ILB_KEY_SERVER:
670dbed73cbSSangeeta Misra case ILB_KEY_SERVRANGE:
671dbed73cbSSangeeta Misra case ILB_KEY_SERVERID:
672dbed73cbSSangeeta Misra res = (void *) i_new_sg_elem(store);
673dbed73cbSSangeeta Misra break;
674dbed73cbSSangeeta Misra default: res = NULL;
675dbed73cbSSangeeta Misra break;
676dbed73cbSSangeeta Misra }
677dbed73cbSSangeeta Misra
678dbed73cbSSangeeta Misra return (res);
679dbed73cbSSangeeta Misra }
680dbed73cbSSangeeta Misra
681dbed73cbSSangeeta Misra /*
682dbed73cbSSangeeta Misra * make sure everything that needs to be there is there
683dbed73cbSSangeeta Misra */
684dbed73cbSSangeeta Misra ilbadm_status_t
i_check_rule_spec(ilb_rule_data_t * rd)685dbed73cbSSangeeta Misra i_check_rule_spec(ilb_rule_data_t *rd)
686dbed73cbSSangeeta Misra {
687dbed73cbSSangeeta Misra int32_t vip_af = rd->r_vip.ia_af;
688dbed73cbSSangeeta Misra ilb_ip_addr_t *prxy_src;
689dbed73cbSSangeeta Misra
690dbed73cbSSangeeta Misra if (vip_af != AF_INET && vip_af != AF_INET6)
691dbed73cbSSangeeta Misra return (ILBADM_INVAL_AF);
692dbed73cbSSangeeta Misra
693dbed73cbSSangeeta Misra if (*rd->r_sgname == '\0')
694dbed73cbSSangeeta Misra return (ILBADM_ENOSGNAME);
695dbed73cbSSangeeta Misra
696dbed73cbSSangeeta Misra if (rd->r_algo == 0 || rd->r_topo == 0) {
697dbed73cbSSangeeta Misra ilbadm_err(gettext("lbalg or type is unspecified"));
698dbed73cbSSangeeta Misra return (ILBADM_LIBERR);
699dbed73cbSSangeeta Misra }
700dbed73cbSSangeeta Misra
701dbed73cbSSangeeta Misra if (rd->r_topo == ILB_TOPO_NAT) {
702dbed73cbSSangeeta Misra prxy_src = &rd->r_nat_src_start;
703dbed73cbSSangeeta Misra if (prxy_src->ia_af != vip_af) {
704dbed73cbSSangeeta Misra ilbadm_err(gettext("proxy-src is either missing"
705dbed73cbSSangeeta Misra " or its address family does not"
706dbed73cbSSangeeta Misra " match that of the VIP address"));
707dbed73cbSSangeeta Misra return (ILBADM_LIBERR);
708dbed73cbSSangeeta Misra }
709dbed73cbSSangeeta Misra }
710dbed73cbSSangeeta Misra /* extend as necessary */
711dbed73cbSSangeeta Misra
712dbed73cbSSangeeta Misra return (ILBADM_OK);
713dbed73cbSSangeeta Misra }
714dbed73cbSSangeeta Misra
715dbed73cbSSangeeta Misra /*
716dbed73cbSSangeeta Misra * in parameter "sz" describes size (in bytes) of mask
717dbed73cbSSangeeta Misra */
718dbed73cbSSangeeta Misra static int
mask_to_prefixlen(const uchar_t * mask,const int sz)719dbed73cbSSangeeta Misra mask_to_prefixlen(const uchar_t *mask, const int sz)
720dbed73cbSSangeeta Misra {
721dbed73cbSSangeeta Misra uchar_t c;
722dbed73cbSSangeeta Misra int i, j;
723dbed73cbSSangeeta Misra int len = 0;
724dbed73cbSSangeeta Misra int tmask;
725dbed73cbSSangeeta Misra
726dbed73cbSSangeeta Misra /*
727dbed73cbSSangeeta Misra * for every byte in the mask, we start with most significant
728dbed73cbSSangeeta Misra * bit and work our way down to the least significant bit; as
729dbed73cbSSangeeta Misra * long as we find the bit set, we add 1 to the length. the
730dbed73cbSSangeeta Misra * first unset bit we encounter terminates this process
731dbed73cbSSangeeta Misra */
732dbed73cbSSangeeta Misra for (i = 0; i < sz; i++) {
733dbed73cbSSangeeta Misra c = mask[i];
734dbed73cbSSangeeta Misra tmask = 1 << 7;
735dbed73cbSSangeeta Misra for (j = 7; j >= 0; j--) {
736dbed73cbSSangeeta Misra if ((c & tmask) == 0)
737dbed73cbSSangeeta Misra return (len);
738dbed73cbSSangeeta Misra len++;
739dbed73cbSSangeeta Misra tmask >>= 1;
740dbed73cbSSangeeta Misra }
741dbed73cbSSangeeta Misra }
742dbed73cbSSangeeta Misra return (len);
743dbed73cbSSangeeta Misra }
744dbed73cbSSangeeta Misra
745dbed73cbSSangeeta Misra int
ilbadm_mask_to_prefixlen(ilb_ip_addr_t * ip)746dbed73cbSSangeeta Misra ilbadm_mask_to_prefixlen(ilb_ip_addr_t *ip)
747dbed73cbSSangeeta Misra {
748dbed73cbSSangeeta Misra int af = ip->ia_af;
749dbed73cbSSangeeta Misra int len = 0;
750dbed73cbSSangeeta Misra
751dbed73cbSSangeeta Misra assert(af == AF_INET || af == AF_INET6);
752dbed73cbSSangeeta Misra switch (af) {
753dbed73cbSSangeeta Misra case AF_INET:
754dbed73cbSSangeeta Misra len = mask_to_prefixlen((uchar_t *)&ip->ia_v4.s_addr,
755dbed73cbSSangeeta Misra sizeof (ip->ia_v4));
756dbed73cbSSangeeta Misra break;
757dbed73cbSSangeeta Misra case AF_INET6:
758dbed73cbSSangeeta Misra len = mask_to_prefixlen((uchar_t *)&ip->ia_v6.s6_addr,
759dbed73cbSSangeeta Misra sizeof (ip->ia_v6));
760dbed73cbSSangeeta Misra break;
761dbed73cbSSangeeta Misra }
762dbed73cbSSangeeta Misra return (len);
763dbed73cbSSangeeta Misra }
764dbed73cbSSangeeta Misra
765dbed73cbSSangeeta Misra /* copied from ifconfig.c, changed to return symbolic constants */
766dbed73cbSSangeeta Misra /*
767dbed73cbSSangeeta Misra * Convert a prefix length to a mask.
768dbed73cbSSangeeta Misra * Returns 1 if ok. 0 otherwise.
769dbed73cbSSangeeta Misra * Assumes the mask array is zero'ed by the caller.
770dbed73cbSSangeeta Misra */
771dbed73cbSSangeeta Misra static boolean_t
in_prefixlentomask(int prefixlen,int maxlen,uchar_t * mask)772dbed73cbSSangeeta Misra in_prefixlentomask(int prefixlen, int maxlen, uchar_t *mask)
773dbed73cbSSangeeta Misra {
774dbed73cbSSangeeta Misra if (prefixlen < 0 || prefixlen > maxlen)
775dbed73cbSSangeeta Misra return (B_FALSE);
776dbed73cbSSangeeta Misra
777dbed73cbSSangeeta Misra while (prefixlen > 0) {
778dbed73cbSSangeeta Misra if (prefixlen >= 8) {
779dbed73cbSSangeeta Misra *mask++ = 0xFF;
780dbed73cbSSangeeta Misra prefixlen -= 8;
781dbed73cbSSangeeta Misra continue;
782dbed73cbSSangeeta Misra }
783dbed73cbSSangeeta Misra *mask |= 1 << (8 - prefixlen);
784dbed73cbSSangeeta Misra prefixlen--;
785dbed73cbSSangeeta Misra }
786dbed73cbSSangeeta Misra return (B_TRUE);
787dbed73cbSSangeeta Misra }
788dbed73cbSSangeeta Misra
789dbed73cbSSangeeta Misra ilbadm_status_t
ilbadm_set_netmask(char * val,ilb_ip_addr_t * ip,int af)790dbed73cbSSangeeta Misra ilbadm_set_netmask(char *val, ilb_ip_addr_t *ip, int af)
791dbed73cbSSangeeta Misra {
792dbed73cbSSangeeta Misra int prefixlen, maxval;
793dbed73cbSSangeeta Misra boolean_t r;
794dbed73cbSSangeeta Misra char *end;
795dbed73cbSSangeeta Misra
796dbed73cbSSangeeta Misra assert(af == AF_INET || af == AF_INET6);
797dbed73cbSSangeeta Misra
798dbed73cbSSangeeta Misra maxval = (af == AF_INET) ? 32 : 128;
799dbed73cbSSangeeta Misra
800dbed73cbSSangeeta Misra if (*val == '/')
801dbed73cbSSangeeta Misra val++;
802dbed73cbSSangeeta Misra prefixlen = strtol(val, &end, 10);
803dbed73cbSSangeeta Misra if ((val == end) || (*end != '\0')) {
804dbed73cbSSangeeta Misra ilbadm_err(gettext("invalid pmask provided"));
805dbed73cbSSangeeta Misra return (ILBADM_LIBERR);
806dbed73cbSSangeeta Misra }
807dbed73cbSSangeeta Misra
808dbed73cbSSangeeta Misra if (prefixlen < 1 || prefixlen > maxval) {
809dbed73cbSSangeeta Misra ilbadm_err(gettext("invalid pmask provided (AF mismatch?)"));
810dbed73cbSSangeeta Misra return (ILBADM_LIBERR);
811dbed73cbSSangeeta Misra }
812dbed73cbSSangeeta Misra
813dbed73cbSSangeeta Misra switch (af) {
814dbed73cbSSangeeta Misra case AF_INET:
815dbed73cbSSangeeta Misra r = in_prefixlentomask(prefixlen, maxval,
816dbed73cbSSangeeta Misra (uchar_t *)&ip->ia_v4.s_addr);
817dbed73cbSSangeeta Misra break;
818dbed73cbSSangeeta Misra case AF_INET6:
819dbed73cbSSangeeta Misra r = in_prefixlentomask(prefixlen, maxval,
820dbed73cbSSangeeta Misra (uchar_t *)&ip->ia_v6.s6_addr);
821dbed73cbSSangeeta Misra break;
822dbed73cbSSangeeta Misra }
823dbed73cbSSangeeta Misra if (r != B_TRUE) {
824dbed73cbSSangeeta Misra ilbadm_err(gettext("cannot convert %s to a netmask"), val);
825dbed73cbSSangeeta Misra return (ILBADM_LIBERR);
826dbed73cbSSangeeta Misra }
827dbed73cbSSangeeta Misra ip->ia_af = af;
828dbed73cbSSangeeta Misra return (ILBADM_OK);
829dbed73cbSSangeeta Misra }
830dbed73cbSSangeeta Misra
831dbed73cbSSangeeta Misra static ilbadm_status_t
i_store_val(char * val,void * store,ilbadm_key_code_t keyword)832dbed73cbSSangeeta Misra i_store_val(char *val, void *store, ilbadm_key_code_t keyword)
833dbed73cbSSangeeta Misra {
834dbed73cbSSangeeta Misra ilbadm_status_t rc = ILBADM_OK;
835dbed73cbSSangeeta Misra void *storep = store;
836dbed73cbSSangeeta Misra ilb_rule_data_t *rd = NULL;
837dbed73cbSSangeeta Misra ilbadm_sgroup_t *sg = NULL;
838dbed73cbSSangeeta Misra ilb_hc_info_t *hc_info = NULL;
839dbed73cbSSangeeta Misra struct protoent *pe;
840dbed73cbSSangeeta Misra int64_t tmp_val;
841dbed73cbSSangeeta Misra
842dbed73cbSSangeeta Misra if (*val == '\0')
843dbed73cbSSangeeta Misra return (ILBADM_NOKEYWORD_VAL);
844dbed73cbSSangeeta Misra
845dbed73cbSSangeeta Misra /* some types need new storage, others don't */
846dbed73cbSSangeeta Misra switch (keyword) {
847dbed73cbSSangeeta Misra case ILB_KEY_SERVER:
848dbed73cbSSangeeta Misra case ILB_KEY_SERVERID:
849dbed73cbSSangeeta Misra sg = (ilbadm_sgroup_t *)store;
850dbed73cbSSangeeta Misra storep = i_new_storep(store, keyword);
851dbed73cbSSangeeta Misra break;
852dbed73cbSSangeeta Misra case ILB_KEY_HEALTHCHECK:
853dbed73cbSSangeeta Misra case ILB_KEY_SERVERGROUP:
854dbed73cbSSangeeta Misra rd = (ilb_rule_data_t *)store;
855dbed73cbSSangeeta Misra break;
856dbed73cbSSangeeta Misra case ILB_KEY_VIP: /* fallthrough */
857dbed73cbSSangeeta Misra case ILB_KEY_PORT: /* fallthrough */
858dbed73cbSSangeeta Misra case ILB_KEY_HCPORT: /* fallthrough */
859dbed73cbSSangeeta Misra case ILB_KEY_CONNDRAIN: /* fallthrough */
860dbed73cbSSangeeta Misra case ILB_KEY_NAT_TO: /* fallthrough */
861dbed73cbSSangeeta Misra case ILB_KEY_STICKY_TO: /* fallthrough */
862dbed73cbSSangeeta Misra case ILB_KEY_PROTOCOL: /* fallthrough */
863dbed73cbSSangeeta Misra case ILB_KEY_ALGORITHM: /* fallthrough */
864dbed73cbSSangeeta Misra case ILB_KEY_STICKY: /* fallthrough */
865dbed73cbSSangeeta Misra case ILB_KEY_TYPE: /* fallthrough */
866dbed73cbSSangeeta Misra case ILB_KEY_SRC: /* fallthrough */
867dbed73cbSSangeeta Misra rd = (ilb_rule_data_t *)store;
868dbed73cbSSangeeta Misra break;
869dbed73cbSSangeeta Misra case ILB_KEY_HC_TEST:
870dbed73cbSSangeeta Misra case ILB_KEY_HC_COUNT:
871dbed73cbSSangeeta Misra case ILB_KEY_HC_INTERVAL:
872dbed73cbSSangeeta Misra case ILB_KEY_HC_TIMEOUT:
873dbed73cbSSangeeta Misra hc_info = (ilb_hc_info_t *)store;
874dbed73cbSSangeeta Misra default: /* do nothing */
875dbed73cbSSangeeta Misra ;
876dbed73cbSSangeeta Misra }
877dbed73cbSSangeeta Misra
878dbed73cbSSangeeta Misra switch (keyword) {
879dbed73cbSSangeeta Misra case ILB_KEY_SRC:
880dbed73cbSSangeeta Misra /*
881dbed73cbSSangeeta Misra * the proxy-src keyword is only valid for full NAT topology
882dbed73cbSSangeeta Misra * the value is either a single or a range of IP addresses.
883dbed73cbSSangeeta Misra */
884dbed73cbSSangeeta Misra if (rd->r_topo != ILB_TOPO_NAT) {
885dbed73cbSSangeeta Misra rc = ILBADM_INVAL_PROXY;
886dbed73cbSSangeeta Misra break;
887dbed73cbSSangeeta Misra }
888dbed73cbSSangeeta Misra rc = i_match_hostorip(storep, sg, val, OPT_NUMERIC_ONLY |
889dbed73cbSSangeeta Misra OPT_IP_RANGE | OPT_NAT, ILB_KEY_SRC);
890dbed73cbSSangeeta Misra break;
891dbed73cbSSangeeta Misra case ILB_KEY_SERVER:
892dbed73cbSSangeeta Misra rc = i_match_hostorip(storep, sg, val,
893dbed73cbSSangeeta Misra OPT_IP_RANGE | OPT_PORTS, ILB_KEY_SERVER);
894dbed73cbSSangeeta Misra break;
895dbed73cbSSangeeta Misra case ILB_KEY_SERVERID:
896dbed73cbSSangeeta Misra if (val[0] != ILB_SRVID_PREFIX)
897dbed73cbSSangeeta Misra rc = ILBADM_INVAL_SRVID;
898dbed73cbSSangeeta Misra else
899dbed73cbSSangeeta Misra rc = i_store_serverID(storep, val);
900dbed73cbSSangeeta Misra break;
901dbed73cbSSangeeta Misra case ILB_KEY_VIP: {
902dbed73cbSSangeeta Misra ilb_ip_addr_t *vip = &rd->r_vip;
903dbed73cbSSangeeta Misra addr_type_t at = numeric;
904dbed73cbSSangeeta Misra char *close = NULL;
905dbed73cbSSangeeta Misra
906dbed73cbSSangeeta Misra /*
907dbed73cbSSangeeta Misra * we duplicate some functionality of i_match_hostorip
908dbed73cbSSangeeta Misra * here; that function is geared to mandate '[]' for IPv6
909dbed73cbSSangeeta Misra * addresses, which we want to relax here, so as not to
910dbed73cbSSangeeta Misra * make i_match_hostorip even longer, we do what we need
911dbed73cbSSangeeta Misra * here.
912dbed73cbSSangeeta Misra */
913dbed73cbSSangeeta Misra if (*val == '[') {
914dbed73cbSSangeeta Misra val++;
915dbed73cbSSangeeta Misra if ((close = strchr(val, (int)']')) == NULL) {
916dbed73cbSSangeeta Misra rc = ILBADM_INVAL_SYNTAX;
917dbed73cbSSangeeta Misra break;
918dbed73cbSSangeeta Misra }
919dbed73cbSSangeeta Misra *close = NULL;
920dbed73cbSSangeeta Misra }
921dbed73cbSSangeeta Misra rc = i_match_onehost(val, vip, &at);
922dbed73cbSSangeeta Misra /* re-assemble string as we found it */
923dbed73cbSSangeeta Misra if (close != NULL) {
924dbed73cbSSangeeta Misra *close = ']';
925dbed73cbSSangeeta Misra if (rc == ILBADM_OK && vip->ia_af != AF_INET6) {
926dbed73cbSSangeeta Misra ilbadm_err(gettext("use of '[]' only valid"
927dbed73cbSSangeeta Misra " with IPv6 addresses"));
928dbed73cbSSangeeta Misra rc = ILBADM_LIBERR;
929dbed73cbSSangeeta Misra }
930dbed73cbSSangeeta Misra }
931dbed73cbSSangeeta Misra break;
932dbed73cbSSangeeta Misra }
933dbed73cbSSangeeta Misra case ILB_KEY_CONNDRAIN:
934dbed73cbSSangeeta Misra tmp_val = strtoll(val, NULL, 10);
935dbed73cbSSangeeta Misra if (tmp_val <= 0 || tmp_val > UINT_MAX) {
936dbed73cbSSangeeta Misra rc = ILBADM_EINVAL;
937dbed73cbSSangeeta Misra break;
938dbed73cbSSangeeta Misra }
939dbed73cbSSangeeta Misra rd->r_conndrain = tmp_val;
940dbed73cbSSangeeta Misra break;
941dbed73cbSSangeeta Misra case ILB_KEY_NAT_TO:
942dbed73cbSSangeeta Misra tmp_val = strtoll(val, NULL, 10);
943dbed73cbSSangeeta Misra if (tmp_val < 0 || tmp_val > UINT_MAX) {
944dbed73cbSSangeeta Misra rc = ILBADM_EINVAL;
945dbed73cbSSangeeta Misra break;
946dbed73cbSSangeeta Misra }
947dbed73cbSSangeeta Misra rd->r_nat_timeout = tmp_val;
948dbed73cbSSangeeta Misra break;
949dbed73cbSSangeeta Misra case ILB_KEY_STICKY_TO:
950dbed73cbSSangeeta Misra tmp_val = strtoll(val, NULL, 10);
951dbed73cbSSangeeta Misra if (tmp_val <= 0 || tmp_val > UINT_MAX) {
952dbed73cbSSangeeta Misra rc = ILBADM_EINVAL;
953dbed73cbSSangeeta Misra break;
954dbed73cbSSangeeta Misra }
955dbed73cbSSangeeta Misra rd->r_sticky_timeout = tmp_val;
956dbed73cbSSangeeta Misra break;
957dbed73cbSSangeeta Misra case ILB_KEY_PORT:
958dbed73cbSSangeeta Misra if (isdigit(*val)) {
959dbed73cbSSangeeta Misra ilbadm_servnode_t sn;
960dbed73cbSSangeeta Misra
961dbed73cbSSangeeta Misra bzero(&sn, sizeof (sn));
962dbed73cbSSangeeta Misra rc = i_match_hostorip((void *)&sn, sg, val,
963dbed73cbSSangeeta Misra OPT_PORTS_ONLY, ILB_KEY_PORT);
964dbed73cbSSangeeta Misra if (rc != ILBADM_OK)
965dbed73cbSSangeeta Misra break;
966dbed73cbSSangeeta Misra rd->r_minport = sn.s_spec.sd_minport;
967dbed73cbSSangeeta Misra rd->r_maxport = sn.s_spec.sd_maxport;
968dbed73cbSSangeeta Misra } else {
969dbed73cbSSangeeta Misra struct servent *se;
970dbed73cbSSangeeta Misra
971dbed73cbSSangeeta Misra se = getservbyname(val, NULL);
972dbed73cbSSangeeta Misra if (se == NULL) {
973dbed73cbSSangeeta Misra rc = ILBADM_ENOSERVICE;
974dbed73cbSSangeeta Misra break;
975dbed73cbSSangeeta Misra }
976dbed73cbSSangeeta Misra rd->r_minport = se->s_port;
977dbed73cbSSangeeta Misra rd->r_maxport = 0;
978dbed73cbSSangeeta Misra }
979dbed73cbSSangeeta Misra break;
980dbed73cbSSangeeta Misra case ILB_KEY_HCPORT:
981dbed73cbSSangeeta Misra if (isdigit(*val)) {
982dbed73cbSSangeeta Misra int hcport = atoi(val);
983dbed73cbSSangeeta Misra
984dbed73cbSSangeeta Misra if (hcport < 1 || hcport > 65535) {
985dbed73cbSSangeeta Misra ilbadm_err(gettext("illegal number for"
986dbed73cbSSangeeta Misra " hcport %s"), val);
987dbed73cbSSangeeta Misra rc = ILBADM_LIBERR;
988dbed73cbSSangeeta Misra break;
989dbed73cbSSangeeta Misra }
990dbed73cbSSangeeta Misra rd->r_hcport = htons(hcport);
991dbed73cbSSangeeta Misra rd->r_hcpflag = ILB_HCI_PROBE_FIX;
992dbed73cbSSangeeta Misra } else if (strcasecmp(val, "ANY") == 0) {
993dbed73cbSSangeeta Misra rd->r_hcport = 0;
994dbed73cbSSangeeta Misra rd->r_hcpflag = ILB_HCI_PROBE_ANY;
995dbed73cbSSangeeta Misra } else {
996dbed73cbSSangeeta Misra return (ILBADM_EINVAL);
997dbed73cbSSangeeta Misra }
998dbed73cbSSangeeta Misra break;
999dbed73cbSSangeeta Misra case ILB_KEY_PROTOCOL:
1000dbed73cbSSangeeta Misra pe = getprotobyname(val);
1001dbed73cbSSangeeta Misra if (pe == NULL)
1002dbed73cbSSangeeta Misra rc = ILBADM_ENOPROTO;
1003dbed73cbSSangeeta Misra else
1004dbed73cbSSangeeta Misra rd->r_proto = pe->p_proto;
1005dbed73cbSSangeeta Misra break;
1006dbed73cbSSangeeta Misra case ILB_KEY_ALGORITHM:
1007dbed73cbSSangeeta Misra rd->r_algo = i_val_from_str(val, &algo_types[0]);
1008dbed73cbSSangeeta Misra if (rd->r_algo == ILBD_BAD_VAL)
1009dbed73cbSSangeeta Misra rc = ILBADM_INVAL_ALG;
1010dbed73cbSSangeeta Misra break;
1011dbed73cbSSangeeta Misra case ILB_KEY_STICKY:
1012dbed73cbSSangeeta Misra rd->r_flags |= ILB_FLAGS_RULE_STICKY;
1013dbed73cbSSangeeta Misra /*
1014dbed73cbSSangeeta Misra * CAVEAT: the use of r_vip.ia_af implies that the VIP
1015dbed73cbSSangeeta Misra * *must* be specified on the commandline *before*
1016dbed73cbSSangeeta Misra * the sticky mask.
1017dbed73cbSSangeeta Misra */
1018dbed73cbSSangeeta Misra if (AF_UNSPEC == rd->r_vip.ia_af) {
1019dbed73cbSSangeeta Misra ilbadm_err(gettext("option '%s' requires that VIP be "
1020dbed73cbSSangeeta Misra "specified first"), ilbadm_key_to_opt(keyword));
1021dbed73cbSSangeeta Misra rc = ILBADM_LIBERR;
1022dbed73cbSSangeeta Misra break;
1023dbed73cbSSangeeta Misra }
1024dbed73cbSSangeeta Misra rc = ilbadm_set_netmask(val, &rd->r_stickymask,
1025dbed73cbSSangeeta Misra rd->r_vip.ia_af);
1026dbed73cbSSangeeta Misra break;
1027dbed73cbSSangeeta Misra case ILB_KEY_TYPE:
1028dbed73cbSSangeeta Misra rd->r_topo = i_val_from_str(val, &topo_types[0]);
1029dbed73cbSSangeeta Misra if (rd->r_topo == ILBD_BAD_VAL)
1030dbed73cbSSangeeta Misra rc = ILBADM_INVAL_OPER;
1031dbed73cbSSangeeta Misra break;
1032dbed73cbSSangeeta Misra case ILB_KEY_SERVERGROUP:
1033dbed73cbSSangeeta Misra (void) strlcpy(rd->r_sgname, (char *)val,
1034dbed73cbSSangeeta Misra sizeof (rd->r_sgname));
1035dbed73cbSSangeeta Misra break;
1036dbed73cbSSangeeta Misra case ILB_KEY_HEALTHCHECK:
1037dbed73cbSSangeeta Misra (void) strlcpy(rd->r_hcname, (char *)val,
1038dbed73cbSSangeeta Misra sizeof (rd->r_hcname));
1039dbed73cbSSangeeta Misra break;
1040dbed73cbSSangeeta Misra case ILB_KEY_HC_TEST:
1041dbed73cbSSangeeta Misra (void) strlcpy(hc_info->hci_test, (char *)val,
1042dbed73cbSSangeeta Misra sizeof (hc_info->hci_test));
1043dbed73cbSSangeeta Misra break;
1044dbed73cbSSangeeta Misra case ILB_KEY_HC_COUNT:
1045dbed73cbSSangeeta Misra if (isdigit(*val))
1046dbed73cbSSangeeta Misra hc_info->hci_count = atoi(val);
1047dbed73cbSSangeeta Misra else
1048dbed73cbSSangeeta Misra return (ILBADM_EINVAL);
1049dbed73cbSSangeeta Misra break;
1050dbed73cbSSangeeta Misra case ILB_KEY_HC_INTERVAL:
1051dbed73cbSSangeeta Misra if (isdigit(*val))
1052dbed73cbSSangeeta Misra hc_info->hci_interval = atoi(val);
1053dbed73cbSSangeeta Misra else
1054dbed73cbSSangeeta Misra return (ILBADM_EINVAL);
1055dbed73cbSSangeeta Misra break;
1056dbed73cbSSangeeta Misra case ILB_KEY_HC_TIMEOUT:
1057dbed73cbSSangeeta Misra if (isdigit(*val))
1058dbed73cbSSangeeta Misra hc_info->hci_timeout = atoi(val);
1059dbed73cbSSangeeta Misra else
1060dbed73cbSSangeeta Misra return (ILBADM_EINVAL);
1061dbed73cbSSangeeta Misra break;
1062dbed73cbSSangeeta Misra default: rc = ILBADM_INVAL_KEYWORD;
1063dbed73cbSSangeeta Misra break;
1064dbed73cbSSangeeta Misra }
1065dbed73cbSSangeeta Misra
1066dbed73cbSSangeeta Misra return (rc);
1067dbed73cbSSangeeta Misra }
1068dbed73cbSSangeeta Misra
1069dbed73cbSSangeeta Misra /*
1070dbed73cbSSangeeta Misra * generic parsing function.
1071dbed73cbSSangeeta Misra * parses "key=value[,value]" strings in "arg". keylist determines the
1072dbed73cbSSangeeta Misra * list of valid keys in the LHS. keycode determines interpretation and
1073dbed73cbSSangeeta Misra * storage in store
1074dbed73cbSSangeeta Misra * XXXms: looks like "key=value[,value]" violates spec. needs a fix
1075dbed73cbSSangeeta Misra */
1076dbed73cbSSangeeta Misra ilbadm_status_t
i_parse_optstring(char * arg,void * store,ilbadm_key_name_t * keylist,int flags,int * count)1077dbed73cbSSangeeta Misra i_parse_optstring(char *arg, void *store, ilbadm_key_name_t *keylist,
1078dbed73cbSSangeeta Misra int flags, int *count)
1079dbed73cbSSangeeta Misra {
1080dbed73cbSSangeeta Misra ilbadm_status_t rc = ILBADM_OK;
1081dbed73cbSSangeeta Misra char *comma = NULL, *equals = NULL;
1082dbed73cbSSangeeta Misra char *key, *nextkey, *val;
1083dbed73cbSSangeeta Misra ilbadm_key_code_t keyword;
1084dbed73cbSSangeeta Misra boolean_t is_value_list = flags & OPT_VALUE_LIST;
1085dbed73cbSSangeeta Misra boolean_t assign_seen = B_FALSE;
1086dbed73cbSSangeeta Misra int n;
1087dbed73cbSSangeeta Misra
1088dbed73cbSSangeeta Misra key = arg;
1089dbed73cbSSangeeta Misra n = 1;
1090dbed73cbSSangeeta Misra /*
1091dbed73cbSSangeeta Misra * Algorithm:
1092dbed73cbSSangeeta Misra * 1. find any commas indicating and seperating current value
1093dbed73cbSSangeeta Misra * from a following value
1094dbed73cbSSangeeta Misra * 2. if we're expecting a list of values (seperated by commas)
1095dbed73cbSSangeeta Misra * and have already seen the assignment, then
1096dbed73cbSSangeeta Misra * get the next "value"
1097dbed73cbSSangeeta Misra * 3. else (we're looking at the first element of the RHS)
1098dbed73cbSSangeeta Misra * 4. find the '='
1099dbed73cbSSangeeta Misra * 5. match the keyword to the list we were passed in
1100dbed73cbSSangeeta Misra * 6. store the value.
1101dbed73cbSSangeeta Misra */
1102dbed73cbSSangeeta Misra while (key != NULL && *key != '\0') {
1103dbed73cbSSangeeta Misra comma = equals = NULL;
1104dbed73cbSSangeeta Misra
1105dbed73cbSSangeeta Misra /* 2 */
1106dbed73cbSSangeeta Misra nextkey = strchr(key, (int)',');
1107dbed73cbSSangeeta Misra if (nextkey != NULL) {
1108dbed73cbSSangeeta Misra comma = nextkey++;
1109dbed73cbSSangeeta Misra *comma = '\0';
1110dbed73cbSSangeeta Misra }
1111dbed73cbSSangeeta Misra
1112dbed73cbSSangeeta Misra /* 3a */
1113dbed73cbSSangeeta Misra if (is_value_list && assign_seen) {
1114dbed73cbSSangeeta Misra val = key;
1115dbed73cbSSangeeta Misra /* 3b */
1116dbed73cbSSangeeta Misra } else {
1117dbed73cbSSangeeta Misra /* 4 */
1118dbed73cbSSangeeta Misra equals = strchr(key, (int)'=');
1119dbed73cbSSangeeta Misra if (equals == NULL) {
1120dbed73cbSSangeeta Misra ilbadm_err("%s: %s", key,
1121dbed73cbSSangeeta Misra ilbadm_errstr(ILBADM_ASSIGNREQ));
1122dbed73cbSSangeeta Misra rc = ILBADM_LIBERR;
1123dbed73cbSSangeeta Misra goto out;
1124dbed73cbSSangeeta Misra }
1125dbed73cbSSangeeta Misra val = equals + 1;
1126dbed73cbSSangeeta Misra *equals = '\0';
1127dbed73cbSSangeeta Misra assign_seen = B_TRUE;
1128dbed73cbSSangeeta Misra
1129dbed73cbSSangeeta Misra /* 5 */
1130dbed73cbSSangeeta Misra keyword = i_match_key(key, keylist);
1131dbed73cbSSangeeta Misra if (keyword == ILB_KEY_BAD) {
1132dbed73cbSSangeeta Misra ilbadm_err(gettext("bad keyword %s"), key);
1133dbed73cbSSangeeta Misra rc = ILBADM_LIBERR;
1134dbed73cbSSangeeta Misra goto out;
1135dbed73cbSSangeeta Misra }
1136dbed73cbSSangeeta Misra }
1137dbed73cbSSangeeta Misra
1138dbed73cbSSangeeta Misra /* 6 */
1139dbed73cbSSangeeta Misra rc = i_store_val(val, store, keyword);
1140dbed73cbSSangeeta Misra if (rc != ILBADM_OK) {
1141dbed73cbSSangeeta Misra ilbadm_err("%s: %s", key, ilbadm_errstr(rc));
1142dbed73cbSSangeeta Misra /* Change to ILBADM_ILBERR to avoid more err msgs. */
1143dbed73cbSSangeeta Misra rc = ILBADM_LIBERR;
1144dbed73cbSSangeeta Misra goto out;
1145dbed73cbSSangeeta Misra }
1146dbed73cbSSangeeta Misra
1147dbed73cbSSangeeta Misra key = nextkey;
1148dbed73cbSSangeeta Misra n++;
1149dbed73cbSSangeeta Misra }
1150dbed73cbSSangeeta Misra
1151dbed73cbSSangeeta Misra out:
1152dbed73cbSSangeeta Misra if (comma != NULL)
1153dbed73cbSSangeeta Misra *comma = ',';
1154dbed73cbSSangeeta Misra if (equals != NULL)
1155dbed73cbSSangeeta Misra *equals = '=';
1156dbed73cbSSangeeta Misra if (count != NULL)
1157dbed73cbSSangeeta Misra *count = n;
1158dbed73cbSSangeeta Misra return (rc);
1159dbed73cbSSangeeta Misra }
1160