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
92static void domediaopt(if_ctx *, const char *, bool);
93static ifmedia_t get_media_subtype(ifmedia_t, const char *);
94static ifmedia_t get_media_mode(ifmedia_t, const char *);
95static ifmedia_t get_media_options(ifmedia_t, const char *);
96static void print_media(ifmedia_t, bool);
97static void print_media_ifconfig(ifmedia_t);
98
99static void
100media_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	}
154free:
155	free(ifmr);
156}
157
158struct ifmediareq *
159ifmedia_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
176static void
177setifmediacallback(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
192static void
193setmedia(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
217static void
218setmediaopt(if_ctx *ctx, const char *val, int d __unused)
219{
220
221	domediaopt(ctx, val, false);
222}
223
224static void
225unsetmediaopt(if_ctx *ctx, const char *val, int d __unused)
226{
227
228	domediaopt(ctx, val, true);
229}
230
231static void
232domediaopt(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
253static void
254setmediainst(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
270static void
271setmediamode(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
285static ifmedia_t
286get_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
304static ifmedia_t
305get_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
323static ifmedia_t
324get_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
376static void
377print_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
419static void
420print_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
463static 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};
471static struct afswtch af_media = {
472	.af_name	= "af_media",
473	.af_af		= AF_UNSPEC,
474	.af_other_status = media_status,
475};
476
477static __constructor void
478ifmedia_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