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