xref: /freebsd/lib/libifconfig/libifconfig_media.c (revision 525fe93dc7487a1e63a90f6a2b956abc601963c1)
1 /*
2  * Copyright (c) 1983, 1993
3  *  The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the University nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29 #include <sys/param.h>
30 #include <sys/ioctl.h>
31 #include <sys/socket.h>
32 #include <sys/sysctl.h>
33 #include <sys/time.h>
34 
35 #include <net/if.h>
36 #include <net/if_dl.h>
37 #include <net/if_types.h>
38 #include <net/if_media.h>
39 #include <net/route.h>
40 
41 #include <assert.h>
42 #include <ctype.h>
43 #include <err.h>
44 #include <errno.h>
45 #include <fcntl.h>
46 #include <stdbool.h>
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <string.h>
50 #include <unistd.h>
51 
52 #include "libifconfig.h"
53 #include "libifconfig_internal.h"
54 
55 static const struct ifmedia_description *lookup_media_desc(
56     const struct ifmedia_description *, const char *);
57 static const struct ifmedia_type_to_subtype *get_toptype_ttos(ifmedia_t);
58 
59 #define IFM_OPMODE(x)							 \
60 	((x) & (IFM_IEEE80211_ADHOC | IFM_IEEE80211_HOSTAP |		 \
61 	IFM_IEEE80211_IBSS | IFM_IEEE80211_WDS | IFM_IEEE80211_MONITOR | \
62 	IFM_IEEE80211_MBSS))
63 #define IFM_IEEE80211_STA    0
64 
65 static const struct ifmedia_description
66     ifm_type_descriptions[] =
67     IFM_TYPE_DESCRIPTIONS;
68 
69 static const struct ifmedia_description
70     ifm_subtype_ethernet_descriptions[] =
71     IFM_SUBTYPE_ETHERNET_DESCRIPTIONS;
72 
73 static const struct ifmedia_description
74     ifm_subtype_ethernet_aliases[] =
75     IFM_SUBTYPE_ETHERNET_ALIASES;
76 
77 static const struct ifmedia_description
78     ifm_subtype_ethernet_option_descriptions[] =
79     IFM_SUBTYPE_ETHERNET_OPTION_DESCRIPTIONS;
80 
81 static const struct ifmedia_description
82     ifm_subtype_ieee80211_descriptions[] =
83     IFM_SUBTYPE_IEEE80211_DESCRIPTIONS;
84 
85 static const struct ifmedia_description
86     ifm_subtype_ieee80211_aliases[] =
87     IFM_SUBTYPE_IEEE80211_ALIASES;
88 
89 static const struct ifmedia_description
90     ifm_subtype_ieee80211_option_descriptions[] =
91     IFM_SUBTYPE_IEEE80211_OPTION_DESCRIPTIONS;
92 
93 static const struct ifmedia_description
94     ifm_subtype_ieee80211_mode_descriptions[] =
95     IFM_SUBTYPE_IEEE80211_MODE_DESCRIPTIONS;
96 
97 static const struct ifmedia_description
98     ifm_subtype_ieee80211_mode_aliases[] =
99     IFM_SUBTYPE_IEEE80211_MODE_ALIASES;
100 
101 static const struct ifmedia_description
102     ifm_subtype_atm_descriptions[] =
103     IFM_SUBTYPE_ATM_DESCRIPTIONS;
104 
105 static const struct ifmedia_description
106     ifm_subtype_atm_aliases[] =
107     IFM_SUBTYPE_ATM_ALIASES;
108 
109 static const struct ifmedia_description
110     ifm_subtype_atm_option_descriptions[] =
111     IFM_SUBTYPE_ATM_OPTION_DESCRIPTIONS;
112 
113 static const struct ifmedia_description
114     ifm_subtype_shared_descriptions[] =
115     IFM_SUBTYPE_SHARED_DESCRIPTIONS;
116 
117 static const struct ifmedia_description
118     ifm_subtype_shared_aliases[] =
119     IFM_SUBTYPE_SHARED_ALIASES;
120 
121 static const struct ifmedia_description
122     ifm_shared_option_descriptions[] =
123     IFM_SHARED_OPTION_DESCRIPTIONS;
124 
125 static const struct ifmedia_description
126     ifm_shared_option_aliases[] =
127     IFM_SHARED_OPTION_ALIASES;
128 
129 static const struct ifmedia_description *
130 lookup_media_desc(const struct ifmedia_description *desc, const char *name)
131 {
132 
133 	for (; desc->ifmt_string != NULL; ++desc)
134 		if (strcasecmp(desc->ifmt_string, name) == 0)
135 			return (desc);
136 	return (NULL);
137 }
138 
139 struct ifmedia_type_to_subtype {
140 	struct {
141 		const struct ifmedia_description *desc;
142 		bool alias;
143 	}
144 	subtypes[5];
145 	struct {
146 		const struct ifmedia_description *desc;
147 		bool alias;
148 	}
149 	options[4];
150 	struct {
151 		const struct ifmedia_description *desc;
152 		bool alias;
153 	}
154 	modes[3];
155 };
156 
157 /* must be in the same order as IFM_TYPE_DESCRIPTIONS */
158 static const struct ifmedia_type_to_subtype ifmedia_types_to_subtypes[] =
159 {
160 	{
161 		{
162 			{ &ifm_subtype_shared_descriptions[0],		 0 },
163 			{ &ifm_subtype_shared_aliases[0],		 1 },
164 			{ &ifm_subtype_ethernet_descriptions[0],	 0 },
165 			{ &ifm_subtype_ethernet_aliases[0],		 1 },
166 			{ NULL,						 0 },
167 		},
168 		{
169 			{ &ifm_shared_option_descriptions[0],		 0 },
170 			{ &ifm_shared_option_aliases[0],		 1 },
171 			{ &ifm_subtype_ethernet_option_descriptions[0],	 0 },
172 			{ NULL,						 0 },
173 		},
174 		{
175 			{ NULL,						 0 },
176 		},
177 	},
178 	{
179 		{
180 			{ &ifm_subtype_shared_descriptions[0],		 0 },
181 			{ &ifm_subtype_shared_aliases[0],		 1 },
182 			{ &ifm_subtype_ieee80211_descriptions[0],	 0 },
183 			{ &ifm_subtype_ieee80211_aliases[0],		 1 },
184 			{ NULL,						 0 },
185 		},
186 		{
187 			{ &ifm_shared_option_descriptions[0],		 0 },
188 			{ &ifm_shared_option_aliases[0],		 1 },
189 			{ &ifm_subtype_ieee80211_option_descriptions[0], 0 },
190 			{ NULL,						 0 },
191 		},
192 		{
193 			{ &ifm_subtype_ieee80211_mode_descriptions[0],	 0 },
194 			{ &ifm_subtype_ieee80211_mode_aliases[0],	 0 },
195 			{ NULL,						 0 },
196 		},
197 	},
198 	{
199 		{
200 			{ &ifm_subtype_shared_descriptions[0],		 0 },
201 			{ &ifm_subtype_shared_aliases[0],		 1 },
202 			{ &ifm_subtype_atm_descriptions[0],		 0 },
203 			{ &ifm_subtype_atm_aliases[0],			 1 },
204 			{ NULL,						 0 },
205 		},
206 		{
207 			{ &ifm_shared_option_descriptions[0],		 0 },
208 			{ &ifm_shared_option_aliases[0],		 1 },
209 			{ &ifm_subtype_atm_option_descriptions[0],	 0 },
210 			{ NULL,						 0 },
211 		},
212 		{
213 			{ NULL,						 0 },
214 		},
215 	},
216 };
217 
218 static const struct ifmedia_type_to_subtype *
219 get_toptype_ttos(ifmedia_t media)
220 {
221 	const struct ifmedia_description *desc;
222 	const struct ifmedia_type_to_subtype *ttos;
223 
224 	for (desc = ifm_type_descriptions, ttos = ifmedia_types_to_subtypes;
225 	    desc->ifmt_string != NULL; desc++, ttos++) {
226 		if (IFM_TYPE(media) == desc->ifmt_word)
227 			return (ttos);
228 	}
229 	errno = ENOENT;
230 	return (NULL);
231 }
232 
233 const char *
234 ifconfig_media_get_type(ifmedia_t media)
235 {
236 	const struct ifmedia_description *desc;
237 
238 	for (desc = ifm_type_descriptions; desc->ifmt_string != NULL; ++desc) {
239 		if (IFM_TYPE(media) == desc->ifmt_word)
240 			return (desc->ifmt_string);
241 	}
242 	errno = ENOENT;
243 	return (NULL);
244 }
245 
246 ifmedia_t
247 ifconfig_media_lookup_type(const char *name)
248 {
249 	const struct ifmedia_description *desc;
250 
251 	desc = lookup_media_desc(ifm_type_descriptions, name);
252 	return (desc == NULL ? INVALID_IFMEDIA : desc->ifmt_word);
253 }
254 
255 const char *
256 ifconfig_media_get_subtype(ifmedia_t media)
257 {
258 	const struct ifmedia_description *desc;
259 	const struct ifmedia_type_to_subtype *ttos;
260 
261 	ttos = get_toptype_ttos(media);
262 	if (ttos == NULL) {
263 		errno = EINVAL;
264 		return (NULL);
265 	}
266 
267 	for (size_t i = 0; ttos->subtypes[i].desc != NULL; ++i) {
268 		if (ttos->subtypes[i].alias)
269 			continue;
270 		for (desc = ttos->subtypes[i].desc;
271 		    desc->ifmt_string != NULL; ++desc) {
272 			if (IFM_SUBTYPE(media) == desc->ifmt_word)
273 				return (desc->ifmt_string);
274 		}
275 	}
276 	errno = ENOENT;
277 	return (NULL);
278 }
279 
280 ifmedia_t
281 ifconfig_media_lookup_subtype(ifmedia_t media, const char *name)
282 {
283 	const struct ifmedia_description *desc;
284 	const struct ifmedia_type_to_subtype *ttos;
285 
286 	ttos = get_toptype_ttos(media);
287 	if (ttos == NULL) {
288 		errno = EINVAL;
289 		return (INVALID_IFMEDIA);
290 	}
291 
292 	for (size_t i = 0; ttos->subtypes[i].desc != NULL; ++i) {
293 		desc = lookup_media_desc(ttos->subtypes[i].desc, name);
294 		if (desc != NULL)
295 			return (desc->ifmt_word);
296 	}
297 	errno = ENOENT;
298 	return (INVALID_IFMEDIA);
299 }
300 
301 const char *
302 ifconfig_media_get_mode(ifmedia_t media)
303 {
304 	const struct ifmedia_description *desc;
305 	const struct ifmedia_type_to_subtype *ttos;
306 
307 	ttos = get_toptype_ttos(media);
308 	if (ttos == NULL) {
309 		errno = EINVAL;
310 		return (NULL);
311 	}
312 
313 	for (size_t i = 0; ttos->modes[i].desc != NULL; ++i) {
314 		if (ttos->modes[i].alias)
315 			continue;
316 		for (desc = ttos->modes[i].desc;
317 		    desc->ifmt_string != NULL; ++desc) {
318 			if (IFM_MODE(media) == desc->ifmt_word)
319 				return (desc->ifmt_string);
320 		}
321 	}
322 	errno = ENOENT;
323 	return (NULL);
324 }
325 
326 ifmedia_t
327 ifconfig_media_lookup_mode(ifmedia_t media, const char *name)
328 {
329 	const struct ifmedia_description *desc;
330 	const struct ifmedia_type_to_subtype *ttos;
331 
332 	ttos = get_toptype_ttos(media);
333 	if (ttos == NULL) {
334 		errno = EINVAL;
335 		return (INVALID_IFMEDIA);
336 	}
337 
338 	for (size_t i = 0; ttos->modes[i].desc != NULL; ++i) {
339 		desc = lookup_media_desc(ttos->modes[i].desc, name);
340 		if (desc != NULL)
341 			return (desc->ifmt_word);
342 	}
343 	errno = ENOENT;
344 	return (INVALID_IFMEDIA);
345 }
346 
347 const char **
348 ifconfig_media_get_options(ifmedia_t media)
349 {
350 	const char **options;
351 	const struct ifmedia_description *desc;
352 	const struct ifmedia_type_to_subtype *ttos;
353 	size_t n;
354 
355 	ttos = get_toptype_ttos(media);
356 	if (ttos == NULL) {
357 		errno = EINVAL;
358 		return (NULL);
359 	}
360 
361 	n = 0;
362 	for (size_t i = 0; ttos->options[i].desc != NULL; ++i) {
363 		if (ttos->options[i].alias)
364 			continue;
365 		for (desc = ttos->options[i].desc;
366 		    desc->ifmt_string != NULL; ++desc) {
367 			if ((media & desc->ifmt_word) != 0)
368 				++n;
369 		}
370 	}
371 	if (n == 0) {
372 		errno = ENOENT;
373 		return (NULL);
374 	}
375 
376 	options = calloc(n + 1, sizeof(*options));
377 	if (options == NULL)
378 		return (NULL);
379 
380 	options[n] = NULL;
381 	n = 0;
382 	for (size_t i = 0; ttos->options[i].desc != NULL; ++i) {
383 		if (ttos->options[i].alias)
384 			continue;
385 		for (desc = ttos->options[i].desc;
386 		    desc->ifmt_string != NULL; ++desc) {
387 			if ((media & desc->ifmt_word) != 0) {
388 				options[n] = desc->ifmt_string;
389 				++n;
390 			}
391 		}
392 	}
393 	return (options);
394 }
395 
396 ifmedia_t *
397 ifconfig_media_lookup_options(ifmedia_t media, const char **opts, size_t nopts)
398 {
399 	ifmedia_t *options;
400 	const struct ifmedia_description *desc, *opt;
401 	const struct ifmedia_type_to_subtype *ttos;
402 
403 	assert(opts != NULL);
404 	assert(nopts > 0);
405 
406 	ttos = get_toptype_ttos(media);
407 	if (ttos == NULL) {
408 		errno = EINVAL;
409 		return (NULL);
410 	}
411 
412 	options = calloc(nopts, sizeof(*options));
413 	if (options == NULL)
414 		return (NULL);
415 	(void)memset(options, INVALID_IFMEDIA, nopts * sizeof(ifmedia_t));
416 
417 	for (size_t i = 0; ttos->options[i].desc != NULL; ++i) {
418 		desc = ttos->options[i].desc;
419 		for (size_t j = 0; j < nopts; ++j) {
420 			opt = lookup_media_desc(desc, opts[j]);
421 			if (opt != NULL)
422 				options[j] = opt->ifmt_word;
423 		}
424 	}
425 	return (options);
426 }
427 
428 /***************************************************************************
429 * Above this point, this file is mostly copied from sbin/ifconfig/ifmedia.c
430 ***************************************************************************/
431 
432 /* Internal structure used for allocations and frees */
433 struct _ifconfig_media_status {
434 	struct ifmediareq ifmr;
435 	int medialist[0];
436 };
437 
438 int
439 ifconfig_media_get_mediareq(ifconfig_handle_t *h, const char *name,
440     struct ifmediareq **ifmr)
441 {
442 	struct _ifconfig_media_status *ms, *ms2;
443 	unsigned long cmd = SIOCGIFXMEDIA;
444 
445 	*ifmr = NULL;
446 	ms = calloc(1, sizeof(*ms));
447 	if (ms == NULL) {
448 		h->error.errtype = OTHER;
449 		h->error.errcode = ENOMEM;
450 		return (-1);
451 	}
452 	(void)strlcpy(ms->ifmr.ifm_name, name, sizeof(ms->ifmr.ifm_name));
453 
454 	/*
455 	 * Check if interface supports extended media types.
456 	 */
457 	if (ifconfig_ioctlwrap(h, AF_LOCAL, cmd, &ms->ifmr) < 0) {
458 		cmd = SIOCGIFMEDIA;
459 		if (ifconfig_ioctlwrap(h, AF_LOCAL, cmd, &ms->ifmr) < 0) {
460 			/* Interface doesn't support SIOC{G,S}IFMEDIA.  */
461 			h->error.errtype = OK;
462 			free(ms);
463 			return (-1);
464 		}
465 	}
466 	if (ms->ifmr.ifm_count == 0) {
467 		*ifmr = &ms->ifmr;
468 		return (0);     /* Interface has no media types ?*/
469 	}
470 
471 	ms2 = realloc(ms, sizeof(*ms) + sizeof(int) * ms->ifmr.ifm_count);
472 	if (ms2 == NULL) {
473 		h->error.errtype = OTHER;
474 		h->error.errcode = ENOMEM;
475 		free(ms);
476 		return (-1);
477 	}
478 	ms2->ifmr.ifm_ulist = &ms2->medialist[0];
479 
480 	if (ifconfig_ioctlwrap(h, AF_LOCAL, cmd, &ms2->ifmr) < 0) {
481 		free(ms2);
482 		return (-1);
483 	}
484 
485 	*ifmr = &ms2->ifmr;
486 	return (0);
487 }
488 
489 const char *
490 ifconfig_media_get_status(const struct ifmediareq *ifmr)
491 {
492 	switch (IFM_TYPE(ifmr->ifm_active)) {
493 	case IFM_ETHER:
494 	case IFM_ATM:
495 		if (ifmr->ifm_status & IFM_ACTIVE) {
496 			return ("active");
497 		} else {
498 			return ("no carrier");
499 		}
500 		break;
501 
502 	case IFM_IEEE80211:
503 		if (ifmr->ifm_status & IFM_ACTIVE) {
504 			/* NB: only sta mode associates */
505 			if (IFM_OPMODE(ifmr->ifm_active) == IFM_IEEE80211_STA) {
506 				return ("associated");
507 			} else {
508 				return ("running");
509 			}
510 		} else {
511 			return ("no carrier");
512 		}
513 		break;
514 	default:
515 		return ("");
516 	}
517 }
518 
519 int
520 ifconfig_media_get_downreason(ifconfig_handle_t *h, const char *name,
521     struct ifdownreason *ifdr)
522 {
523 
524 	(void)memset(ifdr, 0, sizeof(*ifdr));
525 	(void)strlcpy(ifdr->ifdr_name, name, sizeof(ifdr->ifdr_name));
526 	return (ifconfig_ioctlwrap(h, AF_LOCAL, SIOCGIFDOWNREASON, ifdr));
527 }
528