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