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 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 * 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 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 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 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 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 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 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 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 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 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 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 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 * 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 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 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 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 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 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 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 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