| /* |
| * CDDL HEADER START |
| * |
| * The contents of this file are subject to the terms of the |
| * Common Development and Distribution License (the "License"). |
| * You may not use this file except in compliance with the License. |
| * |
| * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE |
| * or http://www.opensolaris.org/os/licensing. |
| * See the License for the specific language governing permissions |
| * and limitations under the License. |
| * |
| * When distributing Covered Code, include this CDDL HEADER in each |
| * file and include the License file at usr/src/OPENSOLARIS.LICENSE. |
| * If applicable, add the following below this CDDL HEADER, with the |
| * fields enclosed by brackets "[]" replaced with your own identifying |
| * information: Portions Copyright [yyyy] [name of copyright owner] |
| * |
| * CDDL HEADER END |
| */ |
| /* |
| * Copyright 2009 Sun Microsystems, Inc. All rights reserved. |
| * Use is subject to license terms. |
| */ |
| |
| #include <errno.h> |
| #include <ipmp_admin.h> |
| #include <libinetutil.h> |
| #include <locale.h> |
| #include <net/if.h> |
| #include <stdarg.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <unistd.h> |
| #include <sys/socket.h> |
| #include <sys/sockio.h> |
| #include <sys/types.h> |
| |
| typedef void offline_func_t(const char *, ipmp_handle_t); |
| |
| static const char *progname; |
| static int sioc4fd, sioc6fd; |
| static offline_func_t do_offline, undo_offline; |
| static boolean_t set_lifflags(const char *, uint64_t); |
| static boolean_t is_offline(const char *); |
| static void warn(const char *, ...); |
| static void die(const char *, ...); |
| |
| static void |
| usage() |
| { |
| (void) fprintf(stderr, "Usage: %s [-d | -r] <interface>\n", progname); |
| exit(1); |
| } |
| |
| static const char * |
| mpadm_errmsg(uint32_t error) |
| { |
| switch (error) { |
| case IPMP_EUNKIF: |
| return ("not a physical interface or not in an IPMP group"); |
| case IPMP_EMINRED: |
| return ("no other functioning interfaces are in its IPMP " |
| "group"); |
| default: |
| return (ipmp_errmsg(error)); |
| } |
| } |
| |
| int |
| main(int argc, char **argv) |
| { |
| int retval; |
| ipmp_handle_t handle; |
| offline_func_t *ofuncp = NULL; |
| const char *ifname; |
| int c; |
| |
| if ((progname = strrchr(argv[0], '/')) != NULL) |
| progname++; |
| else |
| progname = argv[0]; |
| |
| (void) setlocale(LC_ALL, ""); |
| (void) textdomain(TEXT_DOMAIN); |
| |
| while ((c = getopt(argc, argv, "d:r:")) != EOF) { |
| switch (c) { |
| case 'd': |
| ifname = optarg; |
| ofuncp = do_offline; |
| break; |
| case 'r': |
| ifname = optarg; |
| ofuncp = undo_offline; |
| break; |
| default : |
| usage(); |
| } |
| } |
| |
| if (ofuncp == NULL) |
| usage(); |
| |
| /* |
| * Create the global V4 and V6 socket ioctl descriptors. |
| */ |
| sioc4fd = socket(AF_INET, SOCK_DGRAM, 0); |
| sioc6fd = socket(AF_INET6, SOCK_DGRAM, 0); |
| if (sioc4fd == -1 || sioc6fd == -1) |
| die("cannot create sockets"); |
| |
| if ((retval = ipmp_open(&handle)) != IPMP_SUCCESS) |
| die("cannot create ipmp handle: %s\n", ipmp_errmsg(retval)); |
| |
| (*ofuncp)(ifname, handle); |
| |
| ipmp_close(handle); |
| (void) close(sioc4fd); |
| (void) close(sioc6fd); |
| |
| return (EXIT_SUCCESS); |
| } |
| |
| /* |
| * Checks whether IFF_OFFLINE is set on `ifname'. |
| */ |
| boolean_t |
| is_offline(const char *ifname) |
| { |
| struct lifreq lifr = { 0 }; |
| |
| (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name)); |
| if (ioctl(sioc4fd, SIOCGLIFFLAGS, &lifr) == -1) { |
| if (errno != ENXIO || |
| ioctl(sioc6fd, SIOCGLIFFLAGS, &lifr) == -1) { |
| die("cannot get interface flags on %s", ifname); |
| } |
| } |
| |
| return ((lifr.lifr_flags & IFF_OFFLINE) != 0); |
| } |
| |
| static void |
| do_offline(const char *ifname, ipmp_handle_t handle) |
| { |
| ifaddrlistx_t *ifaddrp, *ifaddrs; |
| int retval; |
| |
| if (is_offline(ifname)) |
| die("interface %s is already offline\n", ifname); |
| |
| if ((retval = ipmp_offline(handle, ifname, 1)) != IPMP_SUCCESS) |
| die("cannot offline %s: %s\n", ifname, mpadm_errmsg(retval)); |
| |
| /* |
| * Get all the up addresses for `ifname' and bring them down. |
| */ |
| if (ifaddrlistx(ifname, IFF_UP, 0, &ifaddrs) == -1) |
| die("cannot get addresses on %s", ifname); |
| |
| for (ifaddrp = ifaddrs; ifaddrp != NULL; ifaddrp = ifaddrp->ia_next) { |
| if (!(ifaddrp->ia_flags & IFF_OFFLINE)) |
| warn("IFF_OFFLINE vanished on %s\n", ifaddrp->ia_name); |
| |
| if (!set_lifflags(ifaddrp->ia_name, |
| ifaddrp->ia_flags & ~IFF_UP)) |
| warn("cannot bring down address on %s\n", |
| ifaddrp->ia_name); |
| } |
| |
| ifaddrlistx_free(ifaddrs); |
| } |
| |
| static void |
| undo_offline(const char *ifname, ipmp_handle_t handle) |
| { |
| ifaddrlistx_t *ifaddrp, *ifaddrs; |
| int retval; |
| |
| if (!is_offline(ifname)) |
| die("interface %s is not offline\n", ifname); |
| |
| /* |
| * Get all the down addresses for `ifname' and bring them up. |
| */ |
| if (ifaddrlistx(ifname, 0, IFF_UP, &ifaddrs) == -1) |
| die("cannot get addresses for %s", ifname); |
| |
| for (ifaddrp = ifaddrs; ifaddrp != NULL; ifaddrp = ifaddrp->ia_next) { |
| if (!(ifaddrp->ia_flags & IFF_OFFLINE)) |
| warn("IFF_OFFLINE vanished on %s\n", ifaddrp->ia_name); |
| |
| if (!set_lifflags(ifaddrp->ia_name, ifaddrp->ia_flags | IFF_UP)) |
| warn("cannot bring up address on %s\n", |
| ifaddrp->ia_name); |
| } |
| |
| ifaddrlistx_free(ifaddrs); |
| |
| /* |
| * Undo the offline. |
| */ |
| if ((retval = ipmp_undo_offline(handle, ifname)) != IPMP_SUCCESS) { |
| die("cannot undo-offline %s: %s\n", ifname, |
| mpadm_errmsg(retval)); |
| } |
| |
| /* |
| * Verify whether IFF_OFFLINE is set as a sanity check. |
| */ |
| if (is_offline(ifname)) |
| warn("in.mpathd has not cleared IFF_OFFLINE on %s\n", ifname); |
| } |
| |
| /* |
| * Change `lifname' to have `flags' set. Returns B_TRUE on success. |
| */ |
| static boolean_t |
| set_lifflags(const char *lifname, uint64_t flags) |
| { |
| struct lifreq lifr = { 0 }; |
| int fd = (flags & IFF_IPV4) ? sioc4fd : sioc6fd; |
| |
| (void) strlcpy(lifr.lifr_name, lifname, LIFNAMSIZ); |
| lifr.lifr_flags = flags; |
| |
| return (ioctl(fd, SIOCSLIFFLAGS, &lifr) >= 0); |
| } |
| |
| /* PRINTFLIKE1 */ |
| static void |
| die(const char *format, ...) |
| { |
| va_list alist; |
| char *errstr = strerror(errno); |
| |
| format = gettext(format); |
| (void) fprintf(stderr, gettext("%s: fatal: "), progname); |
| |
| va_start(alist, format); |
| (void) vfprintf(stderr, format, alist); |
| va_end(alist); |
| |
| if (strchr(format, '\n') == NULL) |
| (void) fprintf(stderr, ": %s\n", errstr); |
| |
| exit(EXIT_FAILURE); |
| } |
| |
| /* PRINTFLIKE1 */ |
| static void |
| warn(const char *format, ...) |
| { |
| va_list alist; |
| char *errstr = strerror(errno); |
| |
| format = gettext(format); |
| (void) fprintf(stderr, gettext("%s: warning: "), progname); |
| |
| va_start(alist, format); |
| (void) vfprintf(stderr, format, alist); |
| va_end(alist); |
| |
| if (strchr(format, '\n') == NULL) |
| (void) fprintf(stderr, ": %s\n", errstr); |
| } |