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
51 usage()
52 {
53 	(void) fprintf(stderr, "Usage: %s [-d | -r] <interface>\n", progname);
54 	exit(1);
55 }
56 
57 static const char *
58 mpadm_errmsg(uint32_t error)
59 {
60 	switch (error) {
61 	case IPMP_EUNKIF:
62 		return ("not a physical interface or not in an IPMP group");
63 	case IPMP_EMINRED:
64 		return ("no other functioning interfaces are in its IPMP "
65 		    "group");
66 	default:
67 		return (ipmp_errmsg(error));
68 	}
69 }
70 
71 int
72 main(int argc, char **argv)
73 {
74 	int retval;
75 	ipmp_handle_t handle;
76 	offline_func_t *ofuncp = NULL;
77 	const char *ifname;
78 	int c;
79 
80 	if ((progname = strrchr(argv[0], '/')) != NULL)
81 		progname++;
82 	else
83 		progname = argv[0];
84 
85 	(void) setlocale(LC_ALL, "");
86 	(void) textdomain(TEXT_DOMAIN);
87 
88 	while ((c = getopt(argc, argv, "d:r:")) != EOF) {
89 		switch (c) {
90 		case 'd':
91 			ifname = optarg;
92 			ofuncp = do_offline;
93 			break;
94 		case 'r':
95 			ifname = optarg;
96 			ofuncp = undo_offline;
97 			break;
98 		default :
99 			usage();
100 		}
101 	}
102 
103 	if (ofuncp == NULL)
104 		usage();
105 
106 	/*
107 	 * Create the global V4 and V6 socket ioctl descriptors.
108 	 */
109 	sioc4fd = socket(AF_INET, SOCK_DGRAM, 0);
110 	sioc6fd = socket(AF_INET6, SOCK_DGRAM, 0);
111 	if (sioc4fd == -1 || sioc6fd == -1)
112 		die("cannot create sockets");
113 
114 	if ((retval = ipmp_open(&handle)) != IPMP_SUCCESS)
115 		die("cannot create ipmp handle: %s\n", ipmp_errmsg(retval));
116 
117 	(*ofuncp)(ifname, handle);
118 
119 	ipmp_close(handle);
120 	(void) close(sioc4fd);
121 	(void) close(sioc6fd);
122 
123 	return (EXIT_SUCCESS);
124 }
125 
126 /*
127  * Checks whether IFF_OFFLINE is set on `ifname'.
128  */
129 boolean_t
130 is_offline(const char *ifname)
131 {
132 	struct lifreq lifr = { 0 };
133 
134 	(void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
135 	if (ioctl(sioc4fd, SIOCGLIFFLAGS, &lifr) == -1) {
136 		if (errno != ENXIO ||
137 		    ioctl(sioc6fd, SIOCGLIFFLAGS, &lifr) == -1) {
138 			die("cannot get interface flags on %s", ifname);
139 		}
140 	}
141 
142 	return ((lifr.lifr_flags & IFF_OFFLINE) != 0);
143 }
144 
145 static void
146 do_offline(const char *ifname, ipmp_handle_t handle)
147 {
148 	ifaddrlistx_t *ifaddrp, *ifaddrs;
149 	int retval;
150 
151 	if (is_offline(ifname))
152 		die("interface %s is already offline\n", ifname);
153 
154 	if ((retval = ipmp_offline(handle, ifname, 1)) != IPMP_SUCCESS)
155 		die("cannot offline %s: %s\n", ifname, mpadm_errmsg(retval));
156 
157 	/*
158 	 * Get all the up addresses for `ifname' and bring them down.
159 	 */
160 	if (ifaddrlistx(ifname, IFF_UP, 0, &ifaddrs) == -1)
161 		die("cannot get addresses on %s", ifname);
162 
163 	for (ifaddrp = ifaddrs; ifaddrp != NULL; ifaddrp = ifaddrp->ia_next) {
164 		if (!(ifaddrp->ia_flags & IFF_OFFLINE))
165 			warn("IFF_OFFLINE vanished on %s\n", ifaddrp->ia_name);
166 
167 		if (!set_lifflags(ifaddrp->ia_name,
168 		    ifaddrp->ia_flags & ~IFF_UP))
169 			warn("cannot bring down address on %s\n",
170 			    ifaddrp->ia_name);
171 	}
172 
173 	ifaddrlistx_free(ifaddrs);
174 }
175 
176 static void
177 undo_offline(const char *ifname, ipmp_handle_t handle)
178 {
179 	ifaddrlistx_t *ifaddrp, *ifaddrs;
180 	int retval;
181 
182 	if (!is_offline(ifname))
183 		die("interface %s is not offline\n", ifname);
184 
185 	/*
186 	 * Get all the down addresses for `ifname' and bring them up.
187 	 */
188 	if (ifaddrlistx(ifname, 0, IFF_UP, &ifaddrs) == -1)
189 		die("cannot get addresses for %s", ifname);
190 
191 	for (ifaddrp = ifaddrs; ifaddrp != NULL; ifaddrp = ifaddrp->ia_next) {
192 		if (!(ifaddrp->ia_flags & IFF_OFFLINE))
193 			warn("IFF_OFFLINE vanished on %s\n", ifaddrp->ia_name);
194 
195 		if (!set_lifflags(ifaddrp->ia_name, ifaddrp->ia_flags | IFF_UP))
196 			warn("cannot bring up address on %s\n",
197 			    ifaddrp->ia_name);
198 	}
199 
200 	ifaddrlistx_free(ifaddrs);
201 
202 	/*
203 	 * Undo the offline.
204 	 */
205 	if ((retval = ipmp_undo_offline(handle, ifname)) != IPMP_SUCCESS) {
206 		die("cannot undo-offline %s: %s\n", ifname,
207 		    mpadm_errmsg(retval));
208 	}
209 
210 	/*
211 	 * Verify whether IFF_OFFLINE is set as a sanity check.
212 	 */
213 	if (is_offline(ifname))
214 		warn("in.mpathd has not cleared IFF_OFFLINE on %s\n", ifname);
215 }
216 
217 /*
218  * Change `lifname' to have `flags' set.  Returns B_TRUE on success.
219  */
220 static boolean_t
221 set_lifflags(const char *lifname, uint64_t flags)
222 {
223 	struct lifreq 	lifr = { 0 };
224 	int		fd = (flags & IFF_IPV4) ? sioc4fd : sioc6fd;
225 
226 	(void) strlcpy(lifr.lifr_name, lifname, LIFNAMSIZ);
227 	lifr.lifr_flags = flags;
228 
229 	return (ioctl(fd, SIOCSLIFFLAGS, &lifr) >= 0);
230 }
231 
232 /* PRINTFLIKE1 */
233 static void
234 die(const char *format, ...)
235 {
236 	va_list alist;
237 	char *errstr = strerror(errno);
238 
239 	format = gettext(format);
240 	(void) fprintf(stderr, gettext("%s: fatal: "), progname);
241 
242 	va_start(alist, format);
243 	(void) vfprintf(stderr, format, alist);
244 	va_end(alist);
245 
246 	if (strchr(format, '\n') == NULL)
247 		(void) fprintf(stderr, ": %s\n", errstr);
248 
249 	exit(EXIT_FAILURE);
250 }
251 
252 /* PRINTFLIKE1 */
253 static void
254 warn(const char *format, ...)
255 {
256 	va_list alist;
257 	char *errstr = strerror(errno);
258 
259 	format = gettext(format);
260 	(void) fprintf(stderr, gettext("%s: warning: "), progname);
261 
262 	va_start(alist, format);
263 	(void) vfprintf(stderr, format, alist);
264 	va_end(alist);
265 
266 	if (strchr(format, '\n') == NULL)
267 		(void) fprintf(stderr, ": %s\n", errstr);
268 }
269