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