if_mpadm.c revision 4b908718db419b27633010608bf691c20684c0e2
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (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
* 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 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <strings.h>
#include <ipmp_mpathd.h>
#include <libintl.h>
static void do_offline(char *ifname);
static void undo_offline(char *ifname);
#define IF_SEPARATOR ':'
#define MAX_RETRIES 3
static void
usage()
{
}
static void
{
switch (error) {
case MPATHD_MIN_RED_ERROR:
"Offline failed as there is no other functional "
"interface available in the multipathing group "
"for failing over the network access.\n"));
break;
case MPATHD_FAILBACK_PARTIAL:
"Offline cannot be undone because multipathing "
"configuration is not consistent across all the "
"interfaces in the group.\n"));
break;
default:
/*
* We shouldn't get here. All errors should have a
* meaningful error message, as shown in the above
* cases. If we get here, someone has made a mistake.
*/
"Operation returned an unrecognized error: %u\n"),
error);
break;
}
}
int
{
char *ifname;
int cmd = 0;
int c;
#if !defined(TEXT_DOMAIN)
#define TEXT_DOMAIN "SYS_TEST"
#endif
(void) textdomain(TEXT_DOMAIN);
switch (c) {
case 'd':
cmd = MI_OFFLINE;
if (offline_set(ifname)) {
"already offlined\n"));
exit(1);
}
break;
case 'r':
if (!offline_set(ifname)) {
"offlined\n"));
exit(1);
}
break;
default :
usage();
exit(1);
}
}
if (cmd == 0) {
usage();
exit(1);
}
/*
* Send the command to in.mpathd which is generic to
* both the commands. send_cmd returns only if there
* is no error.
*/
if (cmd == MI_OFFLINE) {
} else {
}
return (0);
}
/*
* Is IFF_OFFLINE set ?
* Returns B_FALSE on failure and B_TRUE on success.
*/
offline_set(char *ifname)
{
int s4;
int s6;
int ret;
if (s4 < 0) {
perror("socket");
exit(1);
}
if (s6 < 0) {
perror("socket");
exit(1);
}
if (ret < 0) {
perror("ioctl: SIOCGLIFFLAGS");
exit(1);
}
if (ret < 0) {
perror("ioctl: SIOCGLIFFLAGS");
exit(1);
}
}
return (B_TRUE);
else
return (B_FALSE);
}
/*
* Sends the command to in.mpathd. If not successful, prints
* an error message and exits.
*/
void
{
struct mi_offline mio;
struct mi_undo_offline miu;
int ret;
int cmd_len;
int i;
int s;
for (i = 0; i < MAX_RETRIES; i++) {
s = connect_to_mpathd(AF_INET);
if (s == -1) {
s = connect_to_mpathd(AF_INET6);
if (s == -1) {
"establish communication with "
"in.mpathd.\n"));
exit(1);
}
}
switch (cmd) {
case MI_OFFLINE :
cmd_len = sizeof (struct mi_offline);
/* errno is set only when ret is -1 */
if (ret == -1)
perror("write");
"successfully send command to "
"in.mpathd.\n"));
exit(1);
}
break;
case MI_UNDO_OFFLINE:
cmd_len = sizeof (struct mi_undo_offline);
/* errno is set only when ret is -1 */
if (ret == -1)
perror("write");
"successfully send command to "
"in.mpathd.\n"));
exit(1);
}
break;
default :
exit(1);
}
/* Read the result from mpathd */
/* errno is set only when ret is -1 */
if (ret == -1)
perror("read");
"read result from in.mpathd.\n"));
exit(1);
}
if (me.me_mpathd_error == 0) {
if (i != 0) {
/*
* We retried at least once. Tell the user
* that things succeeded now.
*/
gettext("Retry Successful.\n"));
}
return; /* Successful */
}
(void) close(s);
(void) sleep(1);
gettext("Retrying ...\n"));
continue; /* Retry */
}
perror("if_mpadm");
} else {
}
exit(1);
}
/*
* We come here only if we retry the operation multiple
* times and did not succeed. Let the user try it again
* later.
*/
gettext("Device busy. Retry the operation later.\n"));
exit(1);
}
static void
do_offline(char *ifname)
{
char *buf;
int numifs;
int n;
char *cp;
int ifsock_v4;
int ifsock_v6;
int af;
int ret;
/*
* Verify whether IFF_OFFLINE is not set as a sanity check.
*/
if (!offline_set(ifname)) {
"has not set IFF_OFFLINE on %s\n"), ifname);
exit(1);
}
/*
* Get both the sockets as we may need to bring both
* IPv4 and IPv6 interfaces down.
*/
if (ifsock_v4 < 0) {
perror("socket");
exit(1);
}
if (ifsock_v6 < 0) {
perror("socket");
exit(1);
}
/*
* Get all the logicals for "ifname" and mark them down.
* There is no easy way of doing this. We get all the
* interfaces in the system using SICGLIFCONF and mark the
* ones matching the name down.
*/
lifn.lifn_flags = 0;
perror("ioctl : SIOCGLIFNUM");
exit(1);
}
perror("calloc");
exit(1);
}
lifc.lifc_flags = 0;
perror("ioctl : SIOCGLIFCONF");
exit(1);
}
sizeof (pi_name));
*cp = '\0';
/* It matches the interface name that was offlined */
else
if (ret != 0) {
"the interfaces failed.\n"));
exit(1);
}
}
}
}
static void
undo_offline(char *ifname)
{
char *buf;
int numifs;
int n;
char *cp;
int ifsock_v4;
int ifsock_v6;
int af;
int ret;
/*
* Verify whether IFF_OFFLINE is set as a sanity check.
*/
if (offline_set(ifname)) {
"has not cleared IFF_OFFLINE on %s\n"), ifname);
exit(1);
}
/*
* Get both the sockets as we may need to bring both
* IPv4 and IPv6 interfaces UP.
*/
if (ifsock_v4 < 0) {
perror("socket");
exit(1);
}
if (ifsock_v6 < 0) {
perror("socket");
exit(1);
}
/*
* Get all the logicals for "ifname" and mark them up.
* There is no easy way of doing this. We get all the
* interfaces in the system using SICGLIFCONF and mark the
* ones matching the name up.
*/
lifn.lifn_flags = 0;
perror("ioctl : SIOCGLIFNUM");
exit(1);
}
perror("calloc");
exit(1);
}
lifc.lifc_flags = 0;
perror("ioctl : SIOCGLIFCONF");
exit(1);
}
sizeof (pi_name));
*cp = '\0';
/* It matches the interface name that was offlined */
else
if (ret != 0) {
"the interfaces failed.\n"));
exit(1);
}
}
}
}
/*
* Returns -1 on failure. Returns the socket file descriptor on
* success.
*/
static int
{
int s;
struct sockaddr_storage ss;
int addrlen;
int ret;
int on;
if (s < 0) {
perror("socket");
return (-1);
}
/*
* Need to bind to a privileged port. For non-root, this
* will fail. in.mpathd verifies that only commands coming
* from privileged ports succeed so that the ordinary user
* can't issue offline commands.
*/
on = 1;
sizeof (on)) < 0) {
perror("setsockopt : TCP_ANONPRIVBIND");
exit(1);
}
switch (family) {
case AF_INET:
addrlen = sizeof (struct sockaddr_in);
break;
case AF_INET6:
addrlen = sizeof (struct sockaddr_in6);
break;
}
if (ret != 0) {
perror("bind");
return (-1);
}
switch (family) {
case AF_INET:
break;
case AF_INET6:
break;
}
if (ret != 0) {
perror("connect");
return (-1);
}
on = 0;
sizeof (on)) < 0) {
perror("setsockopt : TCP_ANONPRIVBIND");
return (-1);
}
return (s);
}
/*
* Bring down the interface specified by the name lifr->lifr_name.
*
* Returns -1 on failure. Returns 0 on success.
*/
static int
{
int ret;
if (ret < 0) {
perror("ioctl: SIOCGLIFFLAGS");
return (-1);
}
/* IFF_OFFLINE was set to start with. Is it still there ? */
return (-1);
}
if (ret < 0) {
perror("ioctl: SIOCSLIFFLAGS");
return (-1);
}
return (0);
}
/*
* Bring up the interface specified by the name lifr->lifr_name.
*
* Returns -1 on failure. Returns 0 on success.
*/
static int
{
int ret;
struct sockaddr_in *addr;
if (ret < 0) {
perror("ioctl: SIOCGLIFADDR");
return (-1);
}
switch (addr->sin_family) {
case AF_INET:
break;
case AF_INET6:
break;
default:
break;
}
if (ret < 0) {
perror("ioctl: SIOCGLIFFLAGS");
return (-1);
}
/*
* Don't affect the state of addresses that failed back.
*
* XXX Link local addresses that are not marked IFF_NOFAILOVER
* will not be brought up. Link local addresses never failover.
* When the interface was offlined, we brought the link local
* address down. We will not bring it up now if IFF_NOFAILOVER
* is not marked. We check for IFF_NOFAILOVER below so that
* we want to maintain the state of all other addresses as it
* was before offline. Normally link local addresses are marked
* IFF_NOFAILOVER and hence this is not an issue. These can
* be fixed in future with RCM and it is beyond the scope
* of if_mpadm to maintain state and do this correctly.
*/
return (0);
/*
* When a data address associated with the physical interface itself
* is failed over (e.g., qfe0, rather than qfe0:1), the kernel must
* fill the ipif data structure for qfe0 with a placeholder entry (the
* "replacement ipif"). Replacement ipif's cannot be brought IFF_UP
* (nor would it make any sense to do so), so we must be careful to
* skip them; thankfully they can be easily identified since they
* all have a zeroed address.
*/
if (zeroaddr)
return (0);
/* IFF_OFFLINE was not set to start with. Is it there ? */
gettext("IFF_OFFLINE set wrongly on %s\n"),
return (-1);
}
if (ret < 0) {
perror("ioctl: SIOCSLIFFLAGS");
return (-1);
}
return (0);
}