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