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 30static void init_current_media(prop_dictionary_t, prop_dictionary_t); 31static void media_constructor(void) __attribute__((constructor)); 32static int setmedia(prop_dictionary_t, prop_dictionary_t); 33static int setmediainst(prop_dictionary_t, prop_dictionary_t); 34static int setmediamode(prop_dictionary_t, prop_dictionary_t); 35static int setmediaopt(prop_dictionary_t, prop_dictionary_t); 36static 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 */ 45static int media_current; 46static int mediaopt_set; 47static int mediaopt_clear; 48 49static struct usage_func usage; 50 51static const int ifm_status_valid_list[] = IFM_STATUS_VALID_LIST; 52 53static const struct ifmedia_status_description ifm_status_descriptions[] = 54 IFM_STATUS_DESCRIPTIONS; 55 56const struct if_status_description if_status_descriptions[] = 57 LINK_STATE_DESCRIPTIONS; 58 59static struct pstr mediamode = PSTR_INITIALIZER1(&mediamode, "mediamode", 60 setmediamode, "mediamode", false, &command_root.pb_parser); 61 62static struct pinteger mediainst = PINTEGER_INITIALIZER1(&mediainst, 63 "mediainst", 0, IFM_INST_MAX, 10, setmediainst, "mediainst", 64 &command_root.pb_parser); 65 66static struct pstr unmediaopt = PSTR_INITIALIZER1(&unmediaopt, "-mediaopt", 67 unsetmediaopt, "unmediaopt", false, &command_root.pb_parser); 68 69static struct pstr mediaopt = PSTR_INITIALIZER1(&mediaopt, "mediaopt", 70 setmediaopt, "mediaopt", false, &command_root.pb_parser); 71 72static struct pstr media = PSTR_INITIALIZER1(&media, "media", setmedia, "media", 73 false, &command_root.pb_parser); 74 75static 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 96struct pkw kwmedia = PKW_INITIALIZER(&kwmedia, "media keywords", NULL, NULL, 97 mediakw, __arraycount(mediakw), NULL); 98 99__dead static void 100media_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 106void 107init_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 144void 145process_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 171static int 172setmedia(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 211static int 212setmediaopt(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 239static int 240unsetmediaopt(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 271static int 272setmediainst(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 296static int 297setmediamode(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 329void 330print_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 351static void 352print_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 366static void 367print_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 397void 398media_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 479static void 480media_usage(prop_dictionary_t env) 481{ 482 fprintf(stderr, 483 "\t[ media type ] [ mediaopt opts ] [ -mediaopt opts ] " 484 "[ instance minst ]\n"); 485} 486 487static void 488media_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