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