#include <sys/param.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/sysctl.h>
#include <sys/time.h>
#include <net/if.h>
#include <net/if_dl.h>
#include <net/if_types.h>
#include <net/if_media.h>
#include <net/route.h>
#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <libifconfig.h>
#include "ifconfig.h"
static void domediaopt(if_ctx *, const char *, bool);
static ifmedia_t get_media_subtype(ifmedia_t, const char *);
static ifmedia_t get_media_mode(ifmedia_t, const char *);
static ifmedia_t get_media_options(ifmedia_t, const char *);
static void print_media(ifmedia_t, bool);
static void print_media_ifconfig(ifmedia_t);
static void
media_status(if_ctx *ctx)
{
struct ifmediareq *ifmr;
if (ifconfig_media_get_mediareq(lifh, ctx->ifname, &ifmr) == -1)
return;
if (ifmr->ifm_count == 0) {
warnx("%s: no media types?", ctx->ifname);
goto free;
}
printf("\tmedia: ");
print_media(ifmr->ifm_current, true);
if (ifmr->ifm_active != ifmr->ifm_current) {
putchar(' ');
putchar('(');
print_media(ifmr->ifm_active, false);
putchar(')');
}
putchar('\n');
if (ifmr->ifm_status & IFM_AVALID) {
struct ifdownreason ifdr;
const char *status;
status = ifconfig_media_get_status(ifmr);
printf("\tstatus: %s", status);
if (strcmp(status, "no carrier") == 0 &&
ifconfig_media_get_downreason(lifh, ctx->ifname, &ifdr) == 0) {
switch (ifdr.ifdr_reason) {
case IFDR_REASON_MSG:
printf(" (%s)", ifdr.ifdr_msg);
break;
case IFDR_REASON_VENDOR:
printf(" (vendor code %d)",
ifdr.ifdr_vendor);
break;
default:
break;
}
}
putchar('\n');
}
if (ctx->args->supmedia) {
printf("\tsupported media:\n");
for (int i = 0; i < ifmr->ifm_count; ++i) {
printf("\t\t");
print_media_ifconfig(ifmr->ifm_ulist[i]);
putchar('\n');
}
}
free:
free(ifmr);
}
struct ifmediareq *
ifmedia_getstate(if_ctx *ctx)
{
static struct ifmediareq *ifmr = NULL;
if (ifmr != NULL)
return (ifmr);
if (ifconfig_media_get_mediareq(lifh, ctx->ifname, &ifmr) == -1)
errc(1, ifconfig_err_errno(lifh),
"%s: ifconfig_media_get_mediareq", ctx->ifname);
if (ifmr->ifm_count == 0)
errx(1, "%s: no media types?", ctx->ifname);
return (ifmr);
}
static void
setifmediacallback(if_ctx *ctx, void *arg)
{
struct ifmediareq *ifmr = (struct ifmediareq *)arg;
static bool did_it = false;
struct ifreq ifr = {};
if (!did_it) {
ifr.ifr_media = ifmr->ifm_current;
if (ioctl_ctx_ifr(ctx, SIOCSIFMEDIA, &ifr) < 0)
err(1, "SIOCSIFMEDIA (media)");
free(ifmr);
did_it = true;
}
}
static void
setmedia(if_ctx *ctx, const char *val, int d __unused)
{
struct ifmediareq *ifmr;
int subtype;
ifmr = ifmedia_getstate(ctx);
subtype = get_media_subtype(ifmr->ifm_ulist[0], val);
ifmr->ifm_current = (ifmr->ifm_current & IFM_IMASK) |
IFM_TYPE(ifmr->ifm_ulist[0]) | subtype;
callback_register(setifmediacallback, (void *)ifmr);
}
static void
setmediaopt(if_ctx *ctx, const char *val, int d __unused)
{
domediaopt(ctx, val, false);
}
static void
unsetmediaopt(if_ctx *ctx, const char *val, int d __unused)
{
domediaopt(ctx, val, true);
}
static void
domediaopt(if_ctx *ctx, const char *val, bool clear)
{
struct ifmediareq *ifmr;
ifmedia_t options;
ifmr = ifmedia_getstate(ctx);
options = get_media_options(ifmr->ifm_ulist[0], val);
if (clear)
ifmr->ifm_current &= ~options;
else {
if (options & IFM_HDX) {
ifmr->ifm_current &= ~IFM_FDX;
options &= ~IFM_HDX;
}
ifmr->ifm_current |= options;
}
callback_register(setifmediacallback, (void *)ifmr);
}
static void
setmediainst(if_ctx *ctx, const char *val, int d __unused)
{
struct ifmediareq *ifmr;
int inst;
ifmr = ifmedia_getstate(ctx);
inst = atoi(val);
if (inst < 0 || inst > (int)IFM_INST_MAX)
errx(1, "invalid media instance: %s", val);
ifmr->ifm_current = (ifmr->ifm_current & ~IFM_IMASK) | inst << IFM_ISHIFT;
callback_register(setifmediacallback, (void *)ifmr);
}
static void
setmediamode(if_ctx *ctx, const char *val, int d __unused)
{
struct ifmediareq *ifmr;
int mode;
ifmr = ifmedia_getstate(ctx);
mode = get_media_mode(ifmr->ifm_ulist[0], val);
ifmr->ifm_current = (ifmr->ifm_current & ~IFM_MMASK) | mode;
callback_register(setifmediacallback, (void *)ifmr);
}
static ifmedia_t
get_media_subtype(ifmedia_t media, const char *val)
{
ifmedia_t subtype;
subtype = ifconfig_media_lookup_subtype(media, val);
if (subtype != INVALID_IFMEDIA)
return (subtype);
switch (errno) {
case EINVAL:
errx(EXIT_FAILURE, "unknown media type 0x%x", media);
case ENOENT:
errx(EXIT_FAILURE, "unknown media subtype: %s", val);
default:
err(EXIT_FAILURE, "ifconfig_media_lookup_subtype");
}
}
static ifmedia_t
get_media_mode(ifmedia_t media, const char *val)
{
ifmedia_t mode;
mode = ifconfig_media_lookup_mode(media, val);
if (mode != INVALID_IFMEDIA)
return (mode);
switch (errno) {
case EINVAL:
errx(EXIT_FAILURE, "unknown media type 0x%x", media);
case ENOENT:
return (INVALID_IFMEDIA);
default:
err(EXIT_FAILURE, "ifconfig_media_lookup_subtype");
}
}
static ifmedia_t
get_media_options(ifmedia_t media, const char *val)
{
ifmedia_t *options;
const char **optnames;
char *opts, *opt;
size_t nopts;
int rval;
opts = strdup(val);
if (opts == NULL)
err(EXIT_FAILURE, "strdup");
nopts = 0;
for (opt = opts; (opt = strtok(opt, ",")) != NULL; opt = NULL)
++nopts;
if (nopts == 0) {
free(opts);
return (0);
}
optnames = calloc(nopts, sizeof(*optnames));
if (optnames == NULL)
err(EXIT_FAILURE, "calloc");
opt = opts;
for (size_t i = 0; i < nopts; ++i) {
optnames[i] = opt;
opt = strchr(opt, '\0') + 1;
}
options = ifconfig_media_lookup_options(media, optnames, nopts);
if (options == NULL)
err(EXIT_FAILURE, "ifconfig_media_lookup_options");
rval = 0;
for (size_t i = 0; i < nopts; ++i) {
if (options[i] == INVALID_IFMEDIA)
errx(EXIT_FAILURE, "unknown option: %s", optnames[i]);
rval |= options[i];
}
free(options);
free(optnames);
free(opts);
return (rval);
}
static void
print_media(ifmedia_t media, bool print_toptype)
{
const char *val, **options;
val = ifconfig_media_get_type(media);
if (val == NULL) {
printf("<unknown type>");
return;
} else if (print_toptype) {
printf("%s", val);
}
val = ifconfig_media_get_subtype(media);
if (val == NULL) {
printf("<unknown subtype>");
return;
}
if (print_toptype)
putchar(' ');
printf("%s", val);
if (print_toptype) {
val = ifconfig_media_get_mode(media);
if (val != NULL && strcasecmp("autoselect", val) != 0)
printf(" mode %s", val);
}
options = ifconfig_media_get_options(media);
if (options != NULL && options[0] != NULL) {
printf(" <%s", options[0]);
for (size_t i = 1; options[i] != NULL; ++i)
printf(",%s", options[i]);
printf(">");
}
free(options);
if (print_toptype && IFM_INST(media) != 0)
printf(" instance %d", IFM_INST(media));
}
static void
print_media_ifconfig(ifmedia_t media)
{
const char *val, **options;
val = ifconfig_media_get_type(media);
if (val == NULL) {
printf("<unknown type>");
return;
}
val = ifconfig_media_get_subtype(media);
if (val == NULL) {
printf("<unknown subtype>");
return;
}
printf("media %s", val);
val = ifconfig_media_get_mode(media);
if (val != NULL)
printf(" mode %s", val);
options = ifconfig_media_get_options(media);
if (options != NULL && options[0] != NULL) {
printf(" mediaopt %s", options[0]);
for (size_t i = 1; options[i] != NULL; ++i)
printf(",%s", options[i]);
}
free(options);
if (IFM_INST(media) != 0)
printf(" instance %d", IFM_INST(media));
}
static struct cmd media_cmds[] = {
DEF_CMD_ARG("media", setmedia),
DEF_CMD_ARG("mode", setmediamode),
DEF_CMD_ARG("mediaopt", setmediaopt),
DEF_CMD_ARG("-mediaopt",unsetmediaopt),
DEF_CMD_ARG("inst", setmediainst),
DEF_CMD_ARG("instance", setmediainst),
};
static struct afswtch af_media = {
.af_name = "af_media",
.af_af = AF_UNSPEC,
.af_other_status = media_status,
};
static __constructor void
ifmedia_ctor(void)
{
for (size_t i = 0; i < nitems(media_cmds); i++)
cmd_register(&media_cmds[i]);
af_register(&af_media);
}