/*
* 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)
{
(void) fprintf(stderr, gettext("Usage: %s -d | -r <ifname>\n"),
progname);
exit(EXIT_FAILURE);
}
static const char *
mpadm_errmsg(uint32_t error)
{
switch (error) {
case IPMP_EUNKIF:
return (gettext("not a physical interface or not in an "
"IPMP group"));
case IPMP_EMINRED:
return (gettext("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",
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", 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);
}