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 (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <errno.h>
27 #include <ipmp_admin.h>
28 #include <libinetutil.h>
29 #include <locale.h>
30 #include <net/if.h>
31 #include <stdarg.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <unistd.h>
36 #include <sys/socket.h>
37 #include <sys/sockio.h>
38 #include <sys/types.h>
39 
40 typedef	void		offline_func_t(const char *, ipmp_handle_t);
41 
42 static const char	*progname;
43 static int		sioc4fd, sioc6fd;
44 static offline_func_t	do_offline, undo_offline;
45 static boolean_t	set_lifflags(const char *, uint64_t);
46 static boolean_t	is_offline(const char *);
47 static void		warn(const char *, ...);
48 static void		die(const char *, ...);
49 
50 static void
usage(void)51 usage(void)
52 {
53 	(void) fprintf(stderr, gettext("Usage: %s -d | -r <ifname>\n"),
54 	    progname);
55 	exit(EXIT_FAILURE);
56 }
57 
58 static const char *
mpadm_errmsg(uint32_t error)59 mpadm_errmsg(uint32_t error)
60 {
61 	switch (error) {
62 	case IPMP_EUNKIF:
63 		return (gettext("not a physical interface or not in an "
64 		    "IPMP group"));
65 	case IPMP_EMINRED:
66 		return (gettext("no other functioning interfaces are in its "
67 		    "IPMP group"));
68 	default:
69 		return (ipmp_errmsg(error));
70 	}
71 }
72 
73 int
main(int argc,char ** argv)74 main(int argc, char **argv)
75 {
76 	int retval;
77 	ipmp_handle_t handle;
78 	offline_func_t *ofuncp = NULL;
79 	const char *ifname;
80 	int c;
81 
82 	if ((progname = strrchr(argv[0], '/')) != NULL)
83 		progname++;
84 	else
85 		progname = argv[0];
86 
87 	(void) setlocale(LC_ALL, "");
88 	(void) textdomain(TEXT_DOMAIN);
89 
90 	while ((c = getopt(argc, argv, "d:r:")) != EOF) {
91 		switch (c) {
92 		case 'd':
93 			ifname = optarg;
94 			ofuncp = do_offline;
95 			break;
96 		case 'r':
97 			ifname = optarg;
98 			ofuncp = undo_offline;
99 			break;
100 		default:
101 			usage();
102 		}
103 	}
104 
105 	if (ofuncp == NULL)
106 		usage();
107 
108 	/*
109 	 * Create the global V4 and V6 socket ioctl descriptors.
110 	 */
111 	sioc4fd = socket(AF_INET, SOCK_DGRAM, 0);
112 	sioc6fd = socket(AF_INET6, SOCK_DGRAM, 0);
113 	if (sioc4fd == -1 || sioc6fd == -1)
114 		die("cannot create sockets");
115 
116 	if ((retval = ipmp_open(&handle)) != IPMP_SUCCESS)
117 		die("cannot create ipmp handle: %s\n", ipmp_errmsg(retval));
118 
119 	(*ofuncp)(ifname, handle);
120 
121 	ipmp_close(handle);
122 	(void) close(sioc4fd);
123 	(void) close(sioc6fd);
124 
125 	return (EXIT_SUCCESS);
126 }
127 
128 /*
129  * Checks whether IFF_OFFLINE is set on `ifname'.
130  */
131 boolean_t
is_offline(const char * ifname)132 is_offline(const char *ifname)
133 {
134 	struct lifreq lifr = { 0 };
135 
136 	(void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
137 	if (ioctl(sioc4fd, SIOCGLIFFLAGS, &lifr) == -1) {
138 		if (errno != ENXIO ||
139 		    ioctl(sioc6fd, SIOCGLIFFLAGS, &lifr) == -1) {
140 			die("cannot get interface flags on %s", ifname);
141 		}
142 	}
143 
144 	return ((lifr.lifr_flags & IFF_OFFLINE) != 0);
145 }
146 
147 static void
do_offline(const char * ifname,ipmp_handle_t handle)148 do_offline(const char *ifname, ipmp_handle_t handle)
149 {
150 	ifaddrlistx_t *ifaddrp, *ifaddrs;
151 	int retval;
152 
153 	if (is_offline(ifname))
154 		die("interface %s is already offline\n", ifname);
155 
156 	if ((retval = ipmp_offline(handle, ifname, 1)) != IPMP_SUCCESS)
157 		die("cannot offline %s: %s\n", ifname, mpadm_errmsg(retval));
158 
159 	/*
160 	 * Get all the up addresses for `ifname' and bring them down.
161 	 */
162 	if (ifaddrlistx(ifname, IFF_UP, 0, &ifaddrs) == -1)
163 		die("cannot get addresses on %s", ifname);
164 
165 	for (ifaddrp = ifaddrs; ifaddrp != NULL; ifaddrp = ifaddrp->ia_next) {
166 		if (!(ifaddrp->ia_flags & IFF_OFFLINE))
167 			warn("IFF_OFFLINE vanished on %s\n", ifaddrp->ia_name);
168 
169 		if (!set_lifflags(ifaddrp->ia_name,
170 		    ifaddrp->ia_flags & ~IFF_UP))
171 			warn("cannot bring down address on %s",
172 			    ifaddrp->ia_name);
173 	}
174 
175 	ifaddrlistx_free(ifaddrs);
176 }
177 
178 static void
undo_offline(const char * ifname,ipmp_handle_t handle)179 undo_offline(const char *ifname, ipmp_handle_t handle)
180 {
181 	ifaddrlistx_t *ifaddrp, *ifaddrs;
182 	int retval;
183 
184 	if (!is_offline(ifname))
185 		die("interface %s is not offline\n", ifname);
186 
187 	/*
188 	 * Get all the down addresses for `ifname' and bring them up.
189 	 */
190 	if (ifaddrlistx(ifname, 0, IFF_UP, &ifaddrs) == -1)
191 		die("cannot get addresses for %s", ifname);
192 
193 	for (ifaddrp = ifaddrs; ifaddrp != NULL; ifaddrp = ifaddrp->ia_next) {
194 		if (!(ifaddrp->ia_flags & IFF_OFFLINE))
195 			warn("IFF_OFFLINE vanished on %s\n", ifaddrp->ia_name);
196 
197 		if (!set_lifflags(ifaddrp->ia_name, ifaddrp->ia_flags | IFF_UP))
198 			warn("cannot bring up address on %s", ifaddrp->ia_name);
199 	}
200 
201 	ifaddrlistx_free(ifaddrs);
202 
203 	/*
204 	 * Undo the offline.
205 	 */
206 	if ((retval = ipmp_undo_offline(handle, ifname)) != IPMP_SUCCESS) {
207 		die("cannot undo-offline %s: %s\n", ifname,
208 		    mpadm_errmsg(retval));
209 	}
210 
211 	/*
212 	 * Verify whether IFF_OFFLINE is set as a sanity check.
213 	 */
214 	if (is_offline(ifname))
215 		warn("in.mpathd has not cleared IFF_OFFLINE on %s\n", ifname);
216 }
217 
218 /*
219  * Change `lifname' to have `flags' set.  Returns B_TRUE on success.
220  */
221 static boolean_t
set_lifflags(const char * lifname,uint64_t flags)222 set_lifflags(const char *lifname, uint64_t flags)
223 {
224 	struct lifreq 	lifr = { 0 };
225 	int		fd = (flags & IFF_IPV4) ? sioc4fd : sioc6fd;
226 
227 	(void) strlcpy(lifr.lifr_name, lifname, LIFNAMSIZ);
228 	lifr.lifr_flags = flags;
229 
230 	return (ioctl(fd, SIOCSLIFFLAGS, &lifr) >= 0);
231 }
232 
233 /* PRINTFLIKE1 */
234 static void
die(const char * format,...)235 die(const char *format, ...)
236 {
237 	va_list alist;
238 	char *errstr = strerror(errno);
239 
240 	format = gettext(format);
241 	(void) fprintf(stderr, gettext("%s: fatal: "), progname);
242 
243 	va_start(alist, format);
244 	(void) vfprintf(stderr, format, alist);
245 	va_end(alist);
246 
247 	if (strchr(format, '\n') == NULL)
248 		(void) fprintf(stderr, ": %s\n", errstr);
249 
250 	exit(EXIT_FAILURE);
251 }
252 
253 /* PRINTFLIKE1 */
254 static void
warn(const char * format,...)255 warn(const char *format, ...)
256 {
257 	va_list alist;
258 	char *errstr = strerror(errno);
259 
260 	format = gettext(format);
261 	(void) fprintf(stderr, gettext("%s: warning: "), progname);
262 
263 	va_start(alist, format);
264 	(void) vfprintf(stderr, format, alist);
265 	va_end(alist);
266 
267 	if (strchr(format, '\n') == NULL)
268 		(void) fprintf(stderr, ": %s\n", errstr);
269 }
270