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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2000-2003 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <sys/types.h> 30 #include <unistd.h> 31 #include <stdlib.h> 32 #include <stdio.h> 33 #include <sys/socket.h> 34 #include <netinet/in.h> 35 #include <netinet/tcp.h> 36 #include <sys/sockio.h> 37 #include <net/if.h> 38 #include <errno.h> 39 #include <strings.h> 40 #include <ipmp_mpathd.h> 41 #include <libintl.h> 42 43 static int if_down(int ifsock, struct lifreq *lifr); 44 static int if_up(int ifsock, struct lifreq *lifr); 45 static void send_cmd(int cmd, char *ifname); 46 static int connect_to_mpathd(sa_family_t family); 47 static void do_offline(char *ifname); 48 static void undo_offline(char *ifname); 49 static boolean_t offline_set(char *ifname); 50 51 #define IF_SEPARATOR ':' 52 #define MAX_RETRIES 3 53 54 static void 55 usage() 56 { 57 fprintf(stderr, "Usage : if_mpadm [-d | -r] <interface_name>\n"); 58 } 59 60 static void 61 print_mpathd_error_msg(uint32_t error) 62 { 63 switch (error) { 64 case MPATHD_MIN_RED_ERROR: 65 (void) fprintf(stderr, gettext( 66 "Offline failed as there is no other functional " 67 "interface available in the multipathing group " 68 "for failing over the network access.\n")); 69 break; 70 71 case MPATHD_FAILBACK_PARTIAL: 72 (void) fprintf(stderr, gettext( 73 "Offline cannot be undone because multipathing " 74 "configuration is not consistent across all the " 75 "interfaces in the group.\n")); 76 break; 77 78 default: 79 /* 80 * We shouldn't get here. All errors should have a 81 * meaningful error message, as shown in the above 82 * cases. If we get here, someone has made a mistake. 83 */ 84 (void) fprintf(stderr, gettext( 85 "Operation returned an unrecognized error: %u\n"), 86 error); 87 break; 88 } 89 } 90 91 main(int argc, char **argv) 92 { 93 char *ifname; 94 int cmd = 0; 95 int c; 96 97 #if !defined(TEXT_DOMAIN) 98 #define TEXT_DOMAIN "SYS_TEST" 99 #endif 100 (void) textdomain(TEXT_DOMAIN); 101 102 while ((c = getopt(argc, argv, "d:r:")) != EOF) { 103 switch (c) { 104 case 'd': 105 ifname = optarg; 106 cmd = MI_OFFLINE; 107 if (offline_set(ifname)) { 108 fprintf(stderr, gettext("Interface already " 109 "offlined\n")); 110 exit(1); 111 } 112 break; 113 case 'r': 114 ifname = optarg; 115 cmd = MI_UNDO_OFFLINE; 116 if (!offline_set(ifname)) { 117 fprintf(stderr, gettext("Interface not " 118 "offlined\n")); 119 exit(1); 120 } 121 break; 122 default : 123 usage(); 124 exit(1); 125 } 126 } 127 128 if (cmd == 0) { 129 usage(); 130 exit(1); 131 } 132 133 /* 134 * Send the command to in.mpathd which is generic to 135 * both the commands. send_cmd returns only if there 136 * is no error. 137 */ 138 send_cmd(cmd, ifname); 139 if (cmd == MI_OFFLINE) { 140 do_offline(ifname); 141 } else { 142 undo_offline(ifname); 143 } 144 145 return (0); 146 } 147 148 /* 149 * Is IFF_OFFLINE set ? 150 * Returns B_FALSE on failure and B_TRUE on success. 151 */ 152 boolean_t 153 offline_set(char *ifname) 154 { 155 struct lifreq lifr; 156 int s4; 157 int s6; 158 int ret; 159 160 s4 = socket(AF_INET, SOCK_DGRAM, 0); 161 if (s4 < 0) { 162 perror("socket"); 163 exit(1); 164 } 165 s6 = socket(AF_INET6, SOCK_DGRAM, 0); 166 if (s6 < 0) { 167 perror("socket"); 168 exit(1); 169 } 170 (void) strncpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name)); 171 ret = ioctl(s4, SIOCGLIFFLAGS, (caddr_t)&lifr); 172 if (ret < 0) { 173 if (errno != ENXIO) { 174 perror("ioctl: SIOCGLIFFLAGS"); 175 exit(1); 176 } 177 ret = ioctl(s6, SIOCGLIFFLAGS, (caddr_t)&lifr); 178 if (ret < 0) { 179 perror("ioctl: SIOCGLIFFLAGS"); 180 exit(1); 181 } 182 } 183 (void) close(s4); 184 (void) close(s6); 185 if (lifr.lifr_flags & IFF_OFFLINE) 186 return (B_TRUE); 187 else 188 return (B_FALSE); 189 } 190 191 /* 192 * Sends the command to in.mpathd. If not successful, prints 193 * an error message and exits. 194 */ 195 void 196 send_cmd(int cmd, char *ifname) 197 { 198 struct mi_offline mio; 199 struct mi_undo_offline miu; 200 struct mi_result me; 201 int ret; 202 int cmd_len; 203 int i; 204 int s; 205 206 for (i = 0; i < MAX_RETRIES; i++) { 207 s = connect_to_mpathd(AF_INET); 208 if (s == -1) { 209 s = connect_to_mpathd(AF_INET6); 210 if (s == -1) { 211 fprintf(stderr, gettext("Cannot establish " 212 "communication with in.mpathd.\n")); 213 exit(1); 214 } 215 } 216 switch (cmd) { 217 case MI_OFFLINE : 218 cmd_len = sizeof (struct mi_offline); 219 bzero(&mio, cmd_len); 220 mio.mio_command = cmd; 221 (void) strncpy(mio.mio_ifname, ifname, LIFNAMSIZ); 222 mio.mio_min_redundancy = 1; 223 ret = write(s, &mio, cmd_len); 224 if (ret != cmd_len) { 225 /* errno is set only when ret is -1 */ 226 if (ret == -1) 227 perror("write"); 228 fprintf(stderr, gettext("Failed to " 229 "successfully send command to " 230 "in.mpathd.\n")); 231 exit(1); 232 } 233 break; 234 case MI_UNDO_OFFLINE: 235 cmd_len = sizeof (struct mi_undo_offline); 236 bzero(&miu, cmd_len); 237 miu.miu_command = cmd; 238 (void) strncpy(miu.miu_ifname, ifname, LIFNAMSIZ); 239 ret = write(s, &miu, cmd_len); 240 if (ret != cmd_len) { 241 /* errno is set only when ret is -1 */ 242 if (ret == -1) 243 perror("write"); 244 fprintf(stderr, gettext("Failed to " 245 "successfully send command to " 246 "in.mpathd.\n")); 247 exit(1); 248 } 249 break; 250 default : 251 fprintf(stderr, "Unknown command \n"); 252 exit(1); 253 } 254 255 /* Read the result from mpathd */ 256 ret = read(s, &me, sizeof (me)); 257 if (ret != sizeof (me)) { 258 /* errno is set only when ret is -1 */ 259 if (ret == -1) 260 perror("read"); 261 fprintf(stderr, gettext("Failed to successfully read " 262 "result from in.mpathd.\n")); 263 exit(1); 264 } 265 if (me.me_mpathd_error == 0) { 266 if (i != 0) { 267 /* 268 * We retried at least once. Tell the user 269 * that things succeeded now. 270 */ 271 fprintf(stderr, gettext("Retry Successful.\n")); 272 } 273 return; /* Successful */ 274 } 275 276 if (me.me_mpathd_error == MPATHD_SYS_ERROR) { 277 if (me.me_sys_error == EAGAIN) { 278 (void) close(s); 279 (void) sleep(1); 280 fprintf(stderr, gettext("Retrying ...\n")); 281 continue; /* Retry */ 282 } 283 errno = me.me_sys_error; 284 perror("if_mpadm"); 285 } else { 286 print_mpathd_error_msg(me.me_mpathd_error); 287 } 288 exit(1); 289 } 290 /* 291 * We come here only if we retry the operation multiple 292 * times and did not succeed. Let the user try it again 293 * later. 294 */ 295 fprintf(stderr, gettext("Device busy. Retry the operation later.\n")); 296 exit(1); 297 } 298 299 static void 300 do_offline(char *ifname) 301 { 302 struct lifreq lifr; 303 struct lifreq *lifcr; 304 struct lifnum lifn; 305 struct lifconf lifc; 306 char *buf; 307 int numifs; 308 int n; 309 char pi_name[LIFNAMSIZ + 1]; 310 char *cp; 311 int ifsock_v4; 312 int ifsock_v6; 313 int af; 314 int ret; 315 316 /* 317 * Verify whether IFF_OFFLINE is not set as a sanity check. 318 */ 319 if (!offline_set(ifname)) { 320 fprintf(stderr, gettext("Operation failed : in.mpathd has " 321 "not set IFF_OFFLINE on %s\n"), ifname); 322 exit(1); 323 } 324 /* 325 * Get both the sockets as we may need to bring both 326 * IPv4 and IPv6 interfaces down. 327 */ 328 ifsock_v4 = socket(AF_INET, SOCK_DGRAM, 0); 329 if (ifsock_v4 < 0) { 330 perror("socket"); 331 exit(1); 332 } 333 ifsock_v6 = socket(AF_INET6, SOCK_DGRAM, 0); 334 if (ifsock_v6 < 0) { 335 perror("socket"); 336 exit(1); 337 } 338 /* 339 * Get all the logicals for "ifname" and mark them down. 340 * There is no easy way of doing this. We get all the 341 * interfaces in the system using SICGLIFCONF and mark the 342 * ones matching the name down. 343 */ 344 lifn.lifn_family = AF_UNSPEC; 345 lifn.lifn_flags = 0; 346 if (ioctl(ifsock_v4, SIOCGLIFNUM, (char *)&lifn) < 0) { 347 perror("ioctl : SIOCGLIFNUM"); 348 exit(1); 349 } 350 numifs = lifn.lifn_count; 351 352 buf = (char *)calloc(numifs, sizeof (struct lifreq)); 353 if (buf == NULL) { 354 perror("calloc"); 355 exit(1); 356 } 357 358 lifc.lifc_family = AF_UNSPEC; 359 lifc.lifc_flags = 0; 360 lifc.lifc_len = numifs * sizeof (struct lifreq); 361 lifc.lifc_buf = buf; 362 363 if (ioctl(ifsock_v4, SIOCGLIFCONF, (char *)&lifc) < 0) { 364 perror("ioctl : SIOCGLIFCONF"); 365 exit(1); 366 } 367 368 lifcr = (struct lifreq *)lifc.lifc_req; 369 for (n = lifc.lifc_len / sizeof (struct lifreq); n > 0; n--, lifcr++) { 370 af = lifcr->lifr_addr.ss_family; 371 (void) strncpy(pi_name, lifcr->lifr_name, 372 sizeof (pi_name)); 373 pi_name[sizeof (pi_name) - 1] = '\0'; 374 if ((cp = strchr(pi_name, IF_SEPARATOR)) != NULL) 375 *cp = '\0'; 376 if (strcmp(pi_name, ifname) == 0) { 377 /* It matches the interface name that was offlined */ 378 (void) strncpy(lifr.lifr_name, lifcr->lifr_name, 379 sizeof (lifr.lifr_name)); 380 if (af == AF_INET) 381 ret = if_down(ifsock_v4, &lifr); 382 else 383 ret = if_down(ifsock_v6, &lifr); 384 if (ret != 0) { 385 fprintf(stderr, gettext("Bringing down the " 386 "interfaces failed.\n")); 387 exit(1); 388 } 389 } 390 } 391 } 392 393 static void 394 undo_offline(char *ifname) 395 { 396 struct lifreq lifr; 397 struct lifreq *lifcr; 398 struct lifnum lifn; 399 struct lifconf lifc; 400 char *buf; 401 int numifs; 402 int n; 403 char pi_name[LIFNAMSIZ + 1]; 404 char *cp; 405 int ifsock_v4; 406 int ifsock_v6; 407 int af; 408 int ret; 409 410 /* 411 * Verify whether IFF_OFFLINE is set as a sanity check. 412 */ 413 if (offline_set(ifname)) { 414 fprintf(stderr, gettext("Operation failed : in.mpathd has " 415 "not cleared IFF_OFFLINE on %s\n"), ifname); 416 exit(1); 417 } 418 /* 419 * Get both the sockets as we may need to bring both 420 * IPv4 and IPv6 interfaces UP. 421 */ 422 ifsock_v4 = socket(AF_INET, SOCK_DGRAM, 0); 423 if (ifsock_v4 < 0) { 424 perror("socket"); 425 exit(1); 426 } 427 ifsock_v6 = socket(AF_INET6, SOCK_DGRAM, 0); 428 if (ifsock_v6 < 0) { 429 perror("socket"); 430 exit(1); 431 } 432 /* 433 * Get all the logicals for "ifname" and mark them up. 434 * There is no easy way of doing this. We get all the 435 * interfaces in the system using SICGLIFCONF and mark the 436 * ones matching the name up. 437 */ 438 lifn.lifn_family = AF_UNSPEC; 439 lifn.lifn_flags = 0; 440 if (ioctl(ifsock_v4, SIOCGLIFNUM, (char *)&lifn) < 0) { 441 perror("ioctl : SIOCGLIFNUM"); 442 exit(1); 443 } 444 numifs = lifn.lifn_count; 445 446 buf = (char *)calloc(numifs, sizeof (struct lifreq)); 447 if (buf == NULL) { 448 perror("calloc"); 449 exit(1); 450 } 451 452 lifc.lifc_family = AF_UNSPEC; 453 lifc.lifc_flags = 0; 454 lifc.lifc_len = numifs * sizeof (struct lifreq); 455 lifc.lifc_buf = buf; 456 457 if (ioctl(ifsock_v4, SIOCGLIFCONF, (char *)&lifc) < 0) { 458 perror("ioctl : SIOCGLIFCONF"); 459 exit(1); 460 } 461 462 lifcr = (struct lifreq *)lifc.lifc_req; 463 for (n = lifc.lifc_len / sizeof (struct lifreq); n > 0; n--, lifcr++) { 464 af = lifcr->lifr_addr.ss_family; 465 (void) strncpy(pi_name, lifcr->lifr_name, 466 sizeof (pi_name)); 467 pi_name[sizeof (pi_name) - 1] = '\0'; 468 if ((cp = strchr(pi_name, IF_SEPARATOR)) != NULL) 469 *cp = '\0'; 470 471 if (strcmp(pi_name, ifname) == 0) { 472 /* It matches the interface name that was offlined */ 473 (void) strncpy(lifr.lifr_name, lifcr->lifr_name, 474 sizeof (lifr.lifr_name)); 475 if (af == AF_INET) 476 ret = if_up(ifsock_v4, &lifr); 477 else 478 ret = if_up(ifsock_v6, &lifr); 479 if (ret != 0) { 480 fprintf(stderr, gettext("Bringing up the " 481 "interfaces failed.\n")); 482 exit(1); 483 } 484 } 485 } 486 } 487 488 /* 489 * Returns -1 on failure. Returns the socket file descriptor on 490 * success. 491 */ 492 static int 493 connect_to_mpathd(sa_family_t family) 494 { 495 int s; 496 struct sockaddr_storage ss; 497 struct sockaddr_in *sin = (struct sockaddr_in *)&ss; 498 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&ss; 499 struct in6_addr loopback_addr = IN6ADDR_LOOPBACK_INIT; 500 int addrlen; 501 int ret; 502 int on; 503 504 s = socket(family, SOCK_STREAM, 0); 505 if (s < 0) { 506 perror("socket"); 507 return (-1); 508 } 509 bzero((char *)&ss, sizeof (ss)); 510 ss.ss_family = family; 511 /* 512 * Need to bind to a privileged port. For non-root, this 513 * will fail. in.mpathd verifies that only commands coming 514 * from privileged ports succeed so that the ordinary user 515 * can't issue offline commands. 516 */ 517 on = 1; 518 if (setsockopt(s, IPPROTO_TCP, TCP_ANONPRIVBIND, &on, 519 sizeof (on)) < 0) { 520 perror("setsockopt : TCP_ANONPRIVBIND"); 521 exit(1); 522 } 523 switch (family) { 524 case AF_INET: 525 sin->sin_port = 0; 526 sin->sin_addr.s_addr = htonl(INADDR_LOOPBACK); 527 addrlen = sizeof (struct sockaddr_in); 528 break; 529 case AF_INET6: 530 sin6->sin6_port = 0; 531 sin6->sin6_addr = loopback_addr; 532 addrlen = sizeof (struct sockaddr_in6); 533 break; 534 } 535 ret = bind(s, (struct sockaddr *)&ss, addrlen); 536 if (ret != 0) { 537 perror("bind"); 538 return (-1); 539 } 540 switch (family) { 541 case AF_INET: 542 sin->sin_port = htons(MPATHD_PORT); 543 break; 544 case AF_INET6: 545 sin6->sin6_port = htons(MPATHD_PORT); 546 break; 547 } 548 ret = connect(s, (struct sockaddr *)&ss, addrlen); 549 if (ret != 0) { 550 perror("connect"); 551 return (-1); 552 } 553 on = 0; 554 if (setsockopt(s, IPPROTO_TCP, TCP_ANONPRIVBIND, &on, 555 sizeof (on)) < 0) { 556 perror("setsockopt : TCP_ANONPRIVBIND"); 557 return (-1); 558 } 559 return (s); 560 } 561 562 /* 563 * Bring down the interface specified by the name lifr->lifr_name. 564 * 565 * Returns -1 on failure. Returns 0 on success. 566 */ 567 static int 568 if_down(int ifsock, struct lifreq *lifr) 569 { 570 int ret; 571 572 ret = ioctl(ifsock, SIOCGLIFFLAGS, (caddr_t)lifr); 573 if (ret < 0) { 574 perror("ioctl: SIOCGLIFFLAGS"); 575 return (-1); 576 } 577 578 /* IFF_OFFLINE was set to start with. Is it still there ? */ 579 if (!(lifr->lifr_flags & (IFF_OFFLINE))) { 580 fprintf(stderr, gettext("IFF_OFFLINE disappeared on %s\n"), 581 lifr->lifr_name); 582 return (-1); 583 } 584 lifr->lifr_flags &= ~IFF_UP; 585 ret = ioctl(ifsock, SIOCSLIFFLAGS, (caddr_t)lifr); 586 if (ret < 0) { 587 perror("ioctl: SIOCSLIFFLAGS"); 588 return (-1); 589 } 590 return (0); 591 } 592 593 /* 594 * Bring up the interface specified by the name lifr->lifr_name. 595 * 596 * Returns -1 on failure. Returns 0 on success. 597 */ 598 static int 599 if_up(int ifsock, struct lifreq *lifr) 600 { 601 int ret; 602 boolean_t zeroaddr = B_FALSE; 603 struct sockaddr_in *addr; 604 605 ret = ioctl(ifsock, SIOCGLIFADDR, lifr); 606 if (ret < 0) { 607 perror("ioctl: SIOCGLIFADDR"); 608 return (-1); 609 } 610 611 addr = (struct sockaddr_in *)&lifr->lifr_addr; 612 switch (addr->sin_family) { 613 case AF_INET: 614 zeroaddr = (addr->sin_addr.s_addr == INADDR_ANY); 615 break; 616 617 case AF_INET6: 618 zeroaddr = IN6_IS_ADDR_UNSPECIFIED( 619 &((struct sockaddr_in6 *)addr)->sin6_addr); 620 break; 621 622 default: 623 break; 624 } 625 626 ret = ioctl(ifsock, SIOCGLIFFLAGS, lifr); 627 if (ret < 0) { 628 perror("ioctl: SIOCGLIFFLAGS"); 629 return (-1); 630 } 631 /* 632 * Don't affect the state of addresses that failed back. 633 * 634 * XXX Link local addresses that are not marked IFF_NOFAILOVER 635 * will not be brought up. Link local addresses never failover. 636 * When the interface was offlined, we brought the link local 637 * address down. We will not bring it up now if IFF_NOFAILOVER 638 * is not marked. We check for IFF_NOFAILOVER below so that 639 * we want to maintain the state of all other addresses as it 640 * was before offline. Normally link local addresses are marked 641 * IFF_NOFAILOVER and hence this is not an issue. These can 642 * be fixed in future with RCM and it is beyond the scope 643 * of if_mpadm to maintain state and do this correctly. 644 */ 645 if (!(lifr->lifr_flags & IFF_NOFAILOVER)) 646 return (0); 647 648 /* 649 * When a data address associated with the physical interface itself 650 * is failed over (e.g., qfe0, rather than qfe0:1), the kernel must 651 * fill the ipif data structure for qfe0 with a placeholder entry (the 652 * "replacement ipif"). Replacement ipif's cannot be brought IFF_UP 653 * (nor would it make any sense to do so), so we must be careful to 654 * skip them; thankfully they can be easily identified since they 655 * all have a zeroed address. 656 */ 657 if (zeroaddr) 658 return (0); 659 660 /* IFF_OFFLINE was not set to start with. Is it there ? */ 661 if (lifr->lifr_flags & IFF_OFFLINE) { 662 fprintf(stderr, gettext("IFF_OFFLINE set wrongly on %s\n"), 663 lifr->lifr_name); 664 return (-1); 665 } 666 lifr->lifr_flags |= IFF_UP; 667 ret = ioctl(ifsock, SIOCSLIFFLAGS, lifr); 668 if (ret < 0) { 669 perror("ioctl: SIOCSLIFFLAGS"); 670 return (-1); 671 } 672 return (0); 673 } 674