xref: /freebsd/sbin/ifconfig/ifmedia.c (revision 2e3f49888ec8851bafb22011533217487764fdb0)
1 /*	$NetBSD: ifconfig.c,v 1.34 1997/04/21 01:17:58 lukem Exp $	*/
2 
3 /*-
4  * SPDX-License-Identifier: BSD-4-Clause
5  *
6  * Copyright (c) 1997 Jason R. Thorpe.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. All advertising materials mentioning features or use of this software
18  *    must display the following acknowledgement:
19  *      This product includes software developed for the NetBSD Project
20  *	by Jason R. Thorpe.
21  * 4. The name of the author may not be used to endorse or promote products
22  *    derived from this software without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
25  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
26  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
27  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
28  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
29  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
31  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
32  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  */
36 
37 /*
38  * Copyright (c) 1983, 1993
39  *	The Regents of the University of California.  All rights reserved.
40  *
41  * Redistribution and use in source and binary forms, with or without
42  * modification, are permitted provided that the following conditions
43  * are met:
44  * 1. Redistributions of source code must retain the above copyright
45  *    notice, this list of conditions and the following disclaimer.
46  * 2. Redistributions in binary form must reproduce the above copyright
47  *    notice, this list of conditions and the following disclaimer in the
48  *    documentation and/or other materials provided with the distribution.
49  * 4. Neither the name of the University nor the names of its contributors
50  *    may be used to endorse or promote products derived from this software
51  *    without specific prior written permission.
52  *
53  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
54  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
55  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
56  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
57  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
58  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
59  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
60  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
61  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
62  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
63  * SUCH DAMAGE.
64  */
65 
66 #include <sys/param.h>
67 #include <sys/ioctl.h>
68 #include <sys/socket.h>
69 #include <sys/sysctl.h>
70 #include <sys/time.h>
71 
72 #include <net/if.h>
73 #include <net/if_dl.h>
74 #include <net/if_types.h>
75 #include <net/if_media.h>
76 #include <net/route.h>
77 
78 #include <ctype.h>
79 #include <err.h>
80 #include <errno.h>
81 #include <fcntl.h>
82 #include <stdbool.h>
83 #include <stdio.h>
84 #include <stdlib.h>
85 #include <string.h>
86 #include <unistd.h>
87 
88 #include <libifconfig.h>
89 
90 #include "ifconfig.h"
91 
92 static void domediaopt(if_ctx *, const char *, bool);
93 static ifmedia_t get_media_subtype(ifmedia_t, const char *);
94 static ifmedia_t get_media_mode(ifmedia_t, const char *);
95 static ifmedia_t get_media_options(ifmedia_t, const char *);
96 static void print_media(ifmedia_t, bool);
97 static void print_media_ifconfig(ifmedia_t);
98 
99 static void
100 media_status(if_ctx *ctx)
101 {
102 	struct ifmediareq *ifmr;
103 
104 	if (ifconfig_media_get_mediareq(lifh, ctx->ifname, &ifmr) == -1)
105 		return;
106 
107 	if (ifmr->ifm_count == 0) {
108 		warnx("%s: no media types?", ctx->ifname);
109 		goto free;
110 	}
111 
112 	printf("\tmedia: ");
113 	print_media(ifmr->ifm_current, true);
114 	if (ifmr->ifm_active != ifmr->ifm_current) {
115 		putchar(' ');
116 		putchar('(');
117 		print_media(ifmr->ifm_active, false);
118 		putchar(')');
119 	}
120 
121 	putchar('\n');
122 
123 	if (ifmr->ifm_status & IFM_AVALID) {
124 		struct ifdownreason ifdr;
125 		const char *status;
126 
127 		status = ifconfig_media_get_status(ifmr);
128 		printf("\tstatus: %s", status);
129 		if (strcmp(status, "no carrier") == 0 &&
130 		    ifconfig_media_get_downreason(lifh, ctx->ifname, &ifdr) == 0) {
131 			switch (ifdr.ifdr_reason) {
132 			case IFDR_REASON_MSG:
133 				printf(" (%s)", ifdr.ifdr_msg);
134 				break;
135 			case IFDR_REASON_VENDOR:
136 				printf(" (vendor code %d)",
137 				    ifdr.ifdr_vendor);
138 				break;
139 			default:
140 				break;
141 			}
142 		}
143 		putchar('\n');
144 	}
145 
146 	if (ctx->args->supmedia) {
147 		printf("\tsupported media:\n");
148 		for (int i = 0; i < ifmr->ifm_count; ++i) {
149 			printf("\t\t");
150 			print_media_ifconfig(ifmr->ifm_ulist[i]);
151 			putchar('\n');
152 		}
153 	}
154 free:
155 	free(ifmr);
156 }
157 
158 struct ifmediareq *
159 ifmedia_getstate(if_ctx *ctx)
160 {
161 	static struct ifmediareq *ifmr = NULL;
162 
163 	if (ifmr != NULL)
164 		return (ifmr);
165 
166 	if (ifconfig_media_get_mediareq(lifh, ctx->ifname, &ifmr) == -1)
167 		errc(1, ifconfig_err_errno(lifh),
168 		    "%s: ifconfig_media_get_mediareq", ctx->ifname);
169 
170 	if (ifmr->ifm_count == 0)
171 		errx(1, "%s: no media types?", ctx->ifname);
172 
173 	return (ifmr);
174 }
175 
176 static void
177 setifmediacallback(if_ctx *ctx, void *arg)
178 {
179 	struct ifmediareq *ifmr = (struct ifmediareq *)arg;
180 	static bool did_it = false;
181 	struct ifreq ifr = {};
182 
183 	if (!did_it) {
184 		ifr.ifr_media = ifmr->ifm_current;
185 		if (ioctl_ctx_ifr(ctx, SIOCSIFMEDIA, &ifr) < 0)
186 			err(1, "SIOCSIFMEDIA (media)");
187 		free(ifmr);
188 		did_it = true;
189 	}
190 }
191 
192 static void
193 setmedia(if_ctx *ctx, const char *val, int d __unused)
194 {
195 	struct ifmediareq *ifmr;
196 	int subtype;
197 
198 	ifmr = ifmedia_getstate(ctx);
199 
200 	/*
201 	 * We are primarily concerned with the top-level type.
202 	 * However, "current" may be only IFM_NONE, so we just look
203 	 * for the top-level type in the first "supported type"
204 	 * entry.
205 	 *
206 	 * (I'm assuming that all supported media types for a given
207 	 * interface will be the same top-level type..)
208 	 */
209 	subtype = get_media_subtype(ifmr->ifm_ulist[0], val);
210 
211 	ifmr->ifm_current = (ifmr->ifm_current & IFM_IMASK) |
212 	    IFM_TYPE(ifmr->ifm_ulist[0]) | subtype;
213 
214 	callback_register(setifmediacallback, (void *)ifmr);
215 }
216 
217 static void
218 setmediaopt(if_ctx *ctx, const char *val, int d __unused)
219 {
220 
221 	domediaopt(ctx, val, false);
222 }
223 
224 static void
225 unsetmediaopt(if_ctx *ctx, const char *val, int d __unused)
226 {
227 
228 	domediaopt(ctx, val, true);
229 }
230 
231 static void
232 domediaopt(if_ctx *ctx, const char *val, bool clear)
233 {
234 	struct ifmediareq *ifmr;
235 	ifmedia_t options;
236 
237 	ifmr = ifmedia_getstate(ctx);
238 
239 	options = get_media_options(ifmr->ifm_ulist[0], val);
240 
241 	if (clear)
242 		ifmr->ifm_current &= ~options;
243 	else {
244 		if (options & IFM_HDX) {
245 			ifmr->ifm_current &= ~IFM_FDX;
246 			options &= ~IFM_HDX;
247 		}
248 		ifmr->ifm_current |= options;
249 	}
250 	callback_register(setifmediacallback, (void *)ifmr);
251 }
252 
253 static void
254 setmediainst(if_ctx *ctx, const char *val, int d __unused)
255 {
256 	struct ifmediareq *ifmr;
257 	int inst;
258 
259 	ifmr = ifmedia_getstate(ctx);
260 
261 	inst = atoi(val);
262 	if (inst < 0 || inst > (int)IFM_INST_MAX)
263 		errx(1, "invalid media instance: %s", val);
264 
265 	ifmr->ifm_current = (ifmr->ifm_current & ~IFM_IMASK) | inst << IFM_ISHIFT;
266 
267 	callback_register(setifmediacallback, (void *)ifmr);
268 }
269 
270 static void
271 setmediamode(if_ctx *ctx, const char *val, int d __unused)
272 {
273 	struct ifmediareq *ifmr;
274 	int mode;
275 
276 	ifmr = ifmedia_getstate(ctx);
277 
278 	mode = get_media_mode(ifmr->ifm_ulist[0], val);
279 
280 	ifmr->ifm_current = (ifmr->ifm_current & ~IFM_MMASK) | mode;
281 
282 	callback_register(setifmediacallback, (void *)ifmr);
283 }
284 
285 static ifmedia_t
286 get_media_subtype(ifmedia_t media, const char *val)
287 {
288 	ifmedia_t subtype;
289 
290 	subtype = ifconfig_media_lookup_subtype(media, val);
291 	if (subtype != INVALID_IFMEDIA)
292 		return (subtype);
293 	switch (errno) {
294 	case EINVAL:
295 		errx(EXIT_FAILURE, "unknown media type 0x%x", media);
296 	case ENOENT:
297 		errx(EXIT_FAILURE, "unknown media subtype: %s", val);
298 	default:
299 		err(EXIT_FAILURE, "ifconfig_media_lookup_subtype");
300 	}
301 	/*NOTREACHED*/
302 }
303 
304 static ifmedia_t
305 get_media_mode(ifmedia_t media, const char *val)
306 {
307 	ifmedia_t mode;
308 
309 	mode = ifconfig_media_lookup_mode(media, val);
310 	if (mode != INVALID_IFMEDIA)
311 		return (mode);
312 	switch (errno) {
313 	case EINVAL:
314 		errx(EXIT_FAILURE, "unknown media type 0x%x", media);
315 	case ENOENT:
316 		return (INVALID_IFMEDIA);
317 	default:
318 		err(EXIT_FAILURE, "ifconfig_media_lookup_subtype");
319 	}
320 	/*NOTREACHED*/
321 }
322 
323 static ifmedia_t
324 get_media_options(ifmedia_t media, const char *val)
325 {
326 	ifmedia_t *options;
327 	const char **optnames;
328 	char *opts, *opt;
329 	size_t nopts;
330 	int rval;
331 
332 	/*
333 	 * We muck with the string, so copy it.
334 	 */
335 	opts = strdup(val);
336 	if (opts == NULL)
337 		err(EXIT_FAILURE, "strdup");
338 
339 	/*
340 	 * Split the comma-delimited list into separate strings.
341 	 */
342 	nopts = 0;
343 	for (opt = opts; (opt = strtok(opt, ",")) != NULL; opt = NULL)
344 		++nopts;
345 	if (nopts == 0) {
346 		free(opts);
347 		return (0);
348 	}
349 	optnames = calloc(nopts, sizeof(*optnames));
350 	if (optnames == NULL)
351 		err(EXIT_FAILURE, "calloc");
352 	opt = opts;
353 	for (size_t i = 0; i < nopts; ++i) {
354 		optnames[i] = opt;
355 		opt = strchr(opt, '\0') + 1;
356 	}
357 
358 	/*
359 	 * Look up the options in the user-provided list.
360 	 */
361 	options = ifconfig_media_lookup_options(media, optnames, nopts);
362 	if (options == NULL)
363 		err(EXIT_FAILURE, "ifconfig_media_lookup_options");
364 	rval = 0;
365 	for (size_t i = 0; i < nopts; ++i) {
366 		if (options[i] == INVALID_IFMEDIA)
367 			errx(EXIT_FAILURE, "unknown option: %s", optnames[i]);
368 		rval |= options[i];
369 	}
370 	free(options);
371 	free(optnames);
372 	free(opts);
373 	return (rval);
374 }
375 
376 static void
377 print_media(ifmedia_t media, bool print_toptype)
378 {
379 	const char *val, **options;
380 
381 	val = ifconfig_media_get_type(media);
382 	if (val == NULL) {
383 		printf("<unknown type>");
384 		return;
385 	} else if (print_toptype) {
386 		printf("%s", val);
387 	}
388 
389 	val = ifconfig_media_get_subtype(media);
390 	if (val == NULL) {
391 		printf("<unknown subtype>");
392 		return;
393 	}
394 
395 	if (print_toptype)
396 		putchar(' ');
397 
398 	printf("%s", val);
399 
400 	if (print_toptype) {
401 		val = ifconfig_media_get_mode(media);
402 		if (val != NULL && strcasecmp("autoselect", val) != 0)
403 			printf(" mode %s", val);
404 	}
405 
406 	options = ifconfig_media_get_options(media);
407 	if (options != NULL && options[0] != NULL) {
408 		printf(" <%s", options[0]);
409 		for (size_t i = 1; options[i] != NULL; ++i)
410 			printf(",%s", options[i]);
411 		printf(">");
412 	}
413 	free(options);
414 
415 	if (print_toptype && IFM_INST(media) != 0)
416 		printf(" instance %d", IFM_INST(media));
417 }
418 
419 static void
420 print_media_ifconfig(ifmedia_t media)
421 {
422 	const char *val, **options;
423 
424 	val = ifconfig_media_get_type(media);
425 	if (val == NULL) {
426 		printf("<unknown type>");
427 		return;
428 	}
429 
430 	/*
431 	 * Don't print the top-level type; it's not like we can
432 	 * change it, or anything.
433 	 */
434 
435 	val = ifconfig_media_get_subtype(media);
436 	if (val == NULL) {
437 		printf("<unknown subtype>");
438 		return;
439 	}
440 
441 	printf("media %s", val);
442 
443 	val = ifconfig_media_get_mode(media);
444 	if (val != NULL)
445 		printf(" mode %s", val);
446 
447 	options = ifconfig_media_get_options(media);
448 	if (options != NULL && options[0] != NULL) {
449 		printf(" mediaopt %s", options[0]);
450 		for (size_t i = 1; options[i] != NULL; ++i)
451 			printf(",%s", options[i]);
452 	}
453 	free(options);
454 
455 	if (IFM_INST(media) != 0)
456 		printf(" instance %d", IFM_INST(media));
457 }
458 
459 /**********************************************************************
460  * ...until here.
461  **********************************************************************/
462 
463 static struct cmd media_cmds[] = {
464 	DEF_CMD_ARG("media",	setmedia),
465 	DEF_CMD_ARG("mode",	setmediamode),
466 	DEF_CMD_ARG("mediaopt",	setmediaopt),
467 	DEF_CMD_ARG("-mediaopt",unsetmediaopt),
468 	DEF_CMD_ARG("inst",	setmediainst),
469 	DEF_CMD_ARG("instance",	setmediainst),
470 };
471 static struct afswtch af_media = {
472 	.af_name	= "af_media",
473 	.af_af		= AF_UNSPEC,
474 	.af_other_status = media_status,
475 };
476 
477 static __constructor void
478 ifmedia_ctor(void)
479 {
480 	for (size_t i = 0; i < nitems(media_cmds);  i++)
481 		cmd_register(&media_cmds[i]);
482 	af_register(&af_media);
483 }
484