1#include <sys/cdefs.h>
2#ifndef lint
3__RCSID("$NetBSD: media.c,v 1.5 2010/12/13 17:35:08 pooka Exp $");
4#endif /* not lint */
5
6#include <assert.h>
7#include <err.h>
8#include <errno.h>
9#include <stdio.h>
10#include <stdlib.h>
11#include <string.h>
12#include <util.h>
13
14#include <sys/ioctl.h>
15
16#include <net/if.h>
17#include <net/if_dl.h>
18#include <net/if_media.h>
19
20#include <prop/proplib.h>
21
22#include "env.h"
23#include "extern.h"
24#include "media.h"
25#include "parse.h"
26#include "util.h"
27#include "prog_ops.h"
28
29static void init_current_media(prop_dictionary_t, prop_dictionary_t);
30static void media_constructor(void) __attribute__((constructor));
31static int setmedia(prop_dictionary_t, prop_dictionary_t);
32static int setmediainst(prop_dictionary_t, prop_dictionary_t);
33static int setmediamode(prop_dictionary_t, prop_dictionary_t);
34static int setmediaopt(prop_dictionary_t, prop_dictionary_t);
35static int unsetmediaopt(prop_dictionary_t, prop_dictionary_t);
36
37/*
38 * Media stuff.  Whenever a media command is first performed, the
39 * currently select media is grabbed for this interface.  If `media'
40 * is given, the current media word is modifed.  `mediaopt' commands
41 * only modify the set and clear words.  They then operate on the
42 * current media word later.
43 */
44static int	media_current;
45static int	mediaopt_set;
46static int	mediaopt_clear;
47
48static struct usage_func usage;
49
50static const int ifm_status_valid_list[] = IFM_STATUS_VALID_LIST;
51
52static const struct ifmedia_status_description ifm_status_descriptions[] =
53    IFM_STATUS_DESCRIPTIONS;
54
55static struct pstr mediamode = PSTR_INITIALIZER1(&mediamode, "mediamode",
56    setmediamode, "mediamode", false, &command_root.pb_parser);
57
58static struct pinteger mediainst = PINTEGER_INITIALIZER1(&mediainst,
59    "mediainst", 0, IFM_INST_MAX, 10, setmediainst, "mediainst",
60    &command_root.pb_parser);
61
62static struct pstr unmediaopt = PSTR_INITIALIZER1(&unmediaopt, "-mediaopt",
63    unsetmediaopt, "unmediaopt", false, &command_root.pb_parser);
64
65static struct pstr mediaopt = PSTR_INITIALIZER1(&mediaopt, "mediaopt",
66    setmediaopt, "mediaopt", false, &command_root.pb_parser);
67
68static struct pstr media = PSTR_INITIALIZER1(&media, "media", setmedia, "media",
69    false, &command_root.pb_parser);
70
71static const struct kwinst mediakw[] = {
72	  {.k_word = "instance", .k_key = "anymedia", .k_type = KW_T_BOOL,
73	   .k_bool = true, .k_act = "media", .k_deact = "mediainst",
74	   .k_nextparser = &mediainst.pi_parser}
75	, {.k_word = "inst", .k_key = "anymedia", .k_type = KW_T_BOOL,
76	   .k_bool = true, .k_act = "media", .k_deact = "mediainst",
77	   .k_nextparser = &mediainst.pi_parser}
78	, {.k_word = "media", .k_key = "anymedia", .k_type = KW_T_BOOL,
79	   .k_bool = true, .k_deact = "media", .k_altdeact = "anymedia",
80	   .k_nextparser = &media.ps_parser}
81	, {.k_word = "mediaopt", .k_key = "anymedia", .k_type = KW_T_BOOL,
82	   .k_bool = true, .k_deact = "mediaopt", .k_altdeact = "instance",
83	   .k_nextparser = &mediaopt.ps_parser}
84	, {.k_word = "-mediaopt", .k_key = "anymedia", .k_type = KW_T_BOOL,
85	   .k_bool = true, .k_deact = "unmediaopt", .k_altdeact = "media",
86	   .k_nextparser = &unmediaopt.ps_parser}
87	, {.k_word = "mode", .k_key = "anymedia", .k_type = KW_T_BOOL,
88	   .k_bool = true, .k_deact = "mode",
89	   .k_nextparser = &mediamode.ps_parser}
90};
91
92struct pkw kwmedia = PKW_INITIALIZER(&kwmedia, "media keywords", NULL, NULL,
93    mediakw, __arraycount(mediakw), NULL);
94
95__dead static void
96media_error(int type, const char *val, const char *opt)
97{
98	errx(EXIT_FAILURE, "unknown %s media %s: %s",
99		get_media_type_string(type), opt, val);
100}
101
102void
103init_current_media(prop_dictionary_t env, prop_dictionary_t oenv)
104{
105	const char *ifname;
106	struct ifmediareq ifmr;
107
108	if ((ifname = getifname(env)) == NULL)
109		err(EXIT_FAILURE, "getifname");
110
111	/*
112	 * If we have not yet done so, grab the currently-selected
113	 * media.
114	 */
115
116	if (prop_dictionary_get(env, "initmedia") == NULL) {
117		memset(&ifmr, 0, sizeof(ifmr));
118
119		if (direct_ioctl(env, SIOCGIFMEDIA, &ifmr) == -1) {
120			/*
121			 * If we get E2BIG, the kernel is telling us
122			 * that there are more, so we can ignore it.
123			 */
124			if (errno != E2BIG)
125				err(EXIT_FAILURE, "SIOCGIFMEDIA");
126		}
127
128		if (!prop_dictionary_set_bool(oenv, "initmedia", true)) {
129			err(EXIT_FAILURE, "%s: prop_dictionary_set_bool",
130			    __func__);
131		}
132		media_current = ifmr.ifm_current;
133	}
134
135	/* Sanity. */
136	if (IFM_TYPE(media_current) == 0)
137		errx(EXIT_FAILURE, "%s: no link type?", ifname);
138}
139
140void
141process_media_commands(prop_dictionary_t env)
142{
143	struct ifreq ifr;
144
145	if (prop_dictionary_get(env, "media") == NULL &&
146	    prop_dictionary_get(env, "mediaopt") == NULL &&
147	    prop_dictionary_get(env, "unmediaopt") == NULL &&
148	    prop_dictionary_get(env, "mediamode") == NULL) {
149		/* Nothing to do. */
150		return;
151	}
152
153	/*
154	 * Media already set up, and commands sanity-checked.  Set/clear
155	 * any options, and we're ready to go.
156	 */
157	media_current |= mediaopt_set;
158	media_current &= ~mediaopt_clear;
159
160	memset(&ifr, 0, sizeof(ifr));
161	ifr.ifr_media = media_current;
162
163	if (direct_ioctl(env, SIOCSIFMEDIA, &ifr) == -1)
164		err(EXIT_FAILURE, "SIOCSIFMEDIA");
165}
166
167static int
168setmedia(prop_dictionary_t env, prop_dictionary_t oenv)
169{
170	int type, subtype, inst;
171	prop_data_t data;
172	char *val;
173
174	init_current_media(env, oenv);
175
176	data = (prop_data_t)prop_dictionary_get(env, "media");
177	assert(data != NULL);
178
179	/* Only one media command may be given. */
180	/* Must not come after mode commands */
181	/* Must not come after mediaopt commands */
182
183	/*
184	 * No need to check if `instance' has been issued; setmediainst()
185	 * craps out if `media' has not been specified.
186	 */
187
188	type = IFM_TYPE(media_current);
189	inst = IFM_INST(media_current);
190
191	val = strndup(prop_data_data_nocopy(data), prop_data_size(data));
192	if (val == NULL)
193		return -1;
194
195	/* Look up the subtype. */
196	subtype = get_media_subtype(type, val);
197	if (subtype == -1)
198		media_error(type, val, "subtype");
199
200	/* Build the new current media word. */
201	media_current = IFM_MAKEWORD(type, subtype, 0, inst);
202
203	/* Media will be set after other processing is complete. */
204	return 0;
205}
206
207static int
208setmediaopt(prop_dictionary_t env, prop_dictionary_t oenv)
209{
210	char *invalid;
211	prop_data_t data;
212	char *val;
213
214	init_current_media(env, oenv);
215
216	data = (prop_data_t)prop_dictionary_get(env, "mediaopt");
217	assert(data != NULL);
218
219	/* Can only issue `mediaopt' once. */
220	/* Can't issue `mediaopt' if `instance' has already been issued. */
221
222	val = strndup(prop_data_data_nocopy(data), prop_data_size(data));
223	if (val == NULL)
224		return -1;
225
226	mediaopt_set = get_media_options(media_current, val, &invalid);
227	free(val);
228	if (mediaopt_set == -1)
229		media_error(media_current, invalid, "option");
230
231	/* Media will be set after other processing is complete. */
232	return 0;
233}
234
235static int
236unsetmediaopt(prop_dictionary_t env, prop_dictionary_t oenv)
237{
238	char *invalid, *val;
239	prop_data_t data;
240
241	init_current_media(env, oenv);
242
243	data = (prop_data_t)prop_dictionary_get(env, "unmediaopt");
244	if (data == NULL) {
245		errno = ENOENT;
246		return -1;
247	}
248
249	val = strndup(prop_data_data_nocopy(data), prop_data_size(data));
250	if (val == NULL)
251		return -1;
252
253	/*
254	 * No need to check for A_MEDIAINST, since the test for A_MEDIA
255	 * implicitly checks for A_MEDIAINST.
256	 */
257
258	mediaopt_clear = get_media_options(media_current, val, &invalid);
259	free(val);
260	if (mediaopt_clear == -1)
261		media_error(media_current, invalid, "option");
262
263	/* Media will be set after other processing is complete. */
264	return 0;
265}
266
267static int
268setmediainst(prop_dictionary_t env, prop_dictionary_t oenv)
269{
270	int type, subtype, options;
271	int64_t inst;
272	bool rc;
273
274	init_current_media(env, oenv);
275
276	rc = prop_dictionary_get_int64(env, "mediainst", &inst);
277	assert(rc);
278
279	/* Can only issue `instance' once. */
280	/* Must have already specified `media' */
281
282	type = IFM_TYPE(media_current);
283	subtype = IFM_SUBTYPE(media_current);
284	options = IFM_OPTIONS(media_current);
285
286	media_current = IFM_MAKEWORD(type, subtype, options, inst);
287
288	/* Media will be set after other processing is complete. */
289	return 0;
290}
291
292static int
293setmediamode(prop_dictionary_t env, prop_dictionary_t oenv)
294{
295	int type, subtype, options, inst, mode;
296	prop_data_t data;
297	char *val;
298
299	init_current_media(env, oenv);
300
301	data = (prop_data_t)prop_dictionary_get(env, "mediamode");
302	assert(data != NULL);
303
304	type = IFM_TYPE(media_current);
305	subtype = IFM_SUBTYPE(media_current);
306	options = IFM_OPTIONS(media_current);
307	inst = IFM_INST(media_current);
308
309	val = strndup(prop_data_data_nocopy(data), prop_data_size(data));
310	if (val == NULL)
311		return -1;
312
313	mode = get_media_mode(type, val);
314	if (mode == -1)
315		media_error(type, val, "mode");
316
317	free(val);
318
319	media_current = IFM_MAKEWORD(type, subtype, options, inst) | mode;
320
321	/* Media will be set after other processing is complete. */
322	return 0;
323}
324
325void
326print_media_word(int ifmw, const char *opt_sep)
327{
328	const char *str;
329
330	printf("%s", get_media_subtype_string(ifmw));
331
332	/* Find mode. */
333	if (IFM_MODE(ifmw) != 0) {
334		str = get_media_mode_string(ifmw);
335		if (str != NULL)
336			printf(" mode %s", str);
337	}
338
339	/* Find options. */
340	for (; (str = get_media_option_string(&ifmw)) != NULL; opt_sep = ",")
341		printf("%s%s", opt_sep, str);
342
343	if (IFM_INST(ifmw) != 0)
344		printf(" instance %d", IFM_INST(ifmw));
345}
346
347void
348media_status(prop_dictionary_t env, prop_dictionary_t oenv)
349{
350	struct ifmediareq ifmr;
351	int af, i, s;
352	int *media_list;
353	const char *ifname;
354
355	if ((ifname = getifname(env)) == NULL)
356		err(EXIT_FAILURE, "getifname");
357	if ((af = getaf(env)) == -1)
358		af = AF_UNSPEC;
359
360	/* get out early if the family is unsupported by the kernel */
361	if ((s = getsock(af)) == -1)
362		err(EXIT_FAILURE, "%s: getsock", __func__);
363
364	memset(&ifmr, 0, sizeof(ifmr));
365	estrlcpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name));
366
367	if (prog_ioctl(s, SIOCGIFMEDIA, &ifmr) == -1) {
368		/*
369		 * Interface doesn't support SIOC{G,S}IFMEDIA.
370		 */
371		return;
372	}
373
374	if (ifmr.ifm_count == 0) {
375		warnx("%s: no media types?", ifname);
376		return;
377	}
378
379	media_list = (int *)malloc(ifmr.ifm_count * sizeof(int));
380	if (media_list == NULL)
381		err(EXIT_FAILURE, "malloc");
382	ifmr.ifm_ulist = media_list;
383
384	if (prog_ioctl(s, SIOCGIFMEDIA, &ifmr) == -1)
385		err(EXIT_FAILURE, "SIOCGIFMEDIA");
386
387	printf("\tmedia: %s ", get_media_type_string(ifmr.ifm_current));
388	print_media_word(ifmr.ifm_current, " ");
389	if (ifmr.ifm_active != ifmr.ifm_current) {
390		printf(" (");
391		print_media_word(ifmr.ifm_active, " ");
392		printf(")");
393	}
394	printf("\n");
395
396	if (ifmr.ifm_status & IFM_STATUS_VALID) {
397		const struct ifmedia_status_description *ifms;
398		int bitno, found = 0;
399
400		printf("\tstatus: ");
401		for (bitno = 0; ifm_status_valid_list[bitno] != 0; bitno++) {
402			for (ifms = ifm_status_descriptions;
403			     ifms->ifms_valid != 0; ifms++) {
404				if (ifms->ifms_type !=
405				      IFM_TYPE(ifmr.ifm_current) ||
406				    ifms->ifms_valid !=
407				      ifm_status_valid_list[bitno])
408					continue;
409				printf("%s%s", found ? ", " : "",
410				    IFM_STATUS_DESC(ifms, ifmr.ifm_status));
411				found = 1;
412
413				/*
414				 * For each valid indicator bit, there's
415				 * only one entry for each media type, so
416				 * terminate the inner loop now.
417				 */
418				break;
419			}
420		}
421
422		if (found == 0)
423			printf("unknown");
424		printf("\n");
425	}
426
427	if (get_flag('m')) {
428		int type, printed_type;
429
430		for (type = IFM_NMIN; type <= IFM_NMAX; type += IFM_NMIN) {
431			for (i = 0, printed_type = 0; i < ifmr.ifm_count; i++) {
432				if (IFM_TYPE(media_list[i]) != type)
433					continue;
434				if (printed_type == 0) {
435					printf("\tsupported %s media:\n",
436					    get_media_type_string(type));
437					printed_type = 1;
438				}
439				printf("\t\tmedia ");
440				print_media_word(media_list[i], " mediaopt ");
441				printf("\n");
442			}
443		}
444	}
445
446	free(media_list);
447}
448
449static void
450media_usage(prop_dictionary_t env)
451{
452	fprintf(stderr,
453	    "\t[ media type ] [ mediaopt opts ] [ -mediaopt opts ] "
454	    "[ instance minst ]\n");
455}
456
457static void
458media_constructor(void)
459{
460	if (register_flag('m') != 0)
461		err(EXIT_FAILURE, __func__);
462	usage_func_init(&usage, media_usage);
463	register_usage(&usage);
464}
465