diff options
Diffstat (limited to 'usr/src/cmd/dlutil')
| -rw-r--r-- | usr/src/cmd/dlutil/Makefile | 40 | ||||
| -rw-r--r-- | usr/src/cmd/dlutil/dlled.c | 285 | ||||
| -rw-r--r-- | usr/src/cmd/dlutil/dlrecv.c | 237 | ||||
| -rw-r--r-- | usr/src/cmd/dlutil/dlsend.c | 163 | ||||
| -rw-r--r-- | usr/src/cmd/dlutil/dlsend.h | 45 | ||||
| -rw-r--r-- | usr/src/cmd/dlutil/dltraninfo.c | 356 |
6 files changed, 1126 insertions, 0 deletions
diff --git a/usr/src/cmd/dlutil/Makefile b/usr/src/cmd/dlutil/Makefile new file mode 100644 index 0000000000..49b6933cc7 --- /dev/null +++ b/usr/src/cmd/dlutil/Makefile @@ -0,0 +1,40 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2017, Joyent, Inc. +# + +PROG= dltraninfo dlsend dlrecv dlled + +include ../Makefile.cmd + +ROOTCMDDIR = $(ROOTLIB)/dl +CFLAGS += $(CCVERBOSE) + +dltraninfo := LDLIBS += -ldladm +dlled := LDLIBS += -ldladm +dlsend := LDLIBS += -ldlpi -lsocket -lmd +dlrecv := LDLIBS += -ldlpi + +ROOTLIBDLFILES = $(PROG:%=$(ROOTLIB)/dl/%) + +.KEEP_STATE: + +all: $(PROG) + +install: all $(ROOTCMD) + +clean: + +lint: lint_PROG + +include ../Makefile.targ diff --git a/usr/src/cmd/dlutil/dlled.c b/usr/src/cmd/dlutil/dlled.c new file mode 100644 index 0000000000..65f2cdc6f7 --- /dev/null +++ b/usr/src/cmd/dlutil/dlled.c @@ -0,0 +1,285 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2017, Joyent, Inc. + */ + +/* + * Private utility to get and set LED information on NICs. This should really + * all be integrated into FM. Until we have figured out that plumbing, this + * allows us to have a little something that we can use to drive work. + */ + +#include <unistd.h> +#include <stdio.h> +#include <stdarg.h> +#include <libgen.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <strings.h> + +#include <libdladm.h> +#include <libdllink.h> +#include <sys/mac.h> +#include <sys/dld.h> +#include <sys/dld_ioc.h> + +static const char *dlled_progname; +static dladm_handle_t dlled_hdl; +static char dlled_dlerrmsg[DLADM_STRSIZE]; + +typedef struct dlled_led_map { + const char *dlm_name; + mac_led_mode_t dlm_bits; +} dlled_led_map_t; + +static dlled_led_map_t dlled_map[] = { + { "default", MAC_LED_DEFAULT }, + { "off", MAC_LED_OFF }, + { "on", MAC_LED_ON }, + { "ident", MAC_LED_IDENT } +}; + +#define DLLED_MAP_NENTRIES \ + (sizeof (dlled_map) / sizeof (dlled_led_map_t)) + +static void +dlled_usage(const char *fmt, ...) +{ + if (fmt != NULL) { + va_list ap; + + (void) fprintf(stderr, "%s: ", dlled_progname); + va_start(ap, fmt); + (void) vfprintf(stderr, fmt, ap); + va_end(ap); + } + + (void) fprintf(stderr, "Usage: %s [-s mode] [link]\n" + "\n" + "\t-s mode set LED to mode\n", + dlled_progname); +} + +static mac_led_mode_t +dlled_parse_mode(const char *orig) +{ + char *mode; + char *part; + mac_led_mode_t m = 0; + + mode = strdup(orig); + if (orig == NULL) { + fprintf(stderr, "failed to allocate memory to dup led " + "mode: %s\n", strerror(errno)); + exit(1); + } + + part = strtok(mode, ","); + while (part != NULL) { + int i; + + for (i = 0; i < DLLED_MAP_NENTRIES; i++) { + if (strcmp(dlled_map[i].dlm_name, part) == 0) { + m |= dlled_map[i].dlm_bits; + break; + } + } + + if (i == DLLED_MAP_NENTRIES) { + fprintf(stderr, "unknown LED mode: %s\n", part); + exit(1); + } + + part = strtok(NULL, ","); + } + + free(mode); + if (m == 0) { + fprintf(stderr, "failed to parse %s: no valid modes " + "specified\n", orig); + exit(1); + } + + return (m); +} + +static void +dlled_mode2str(mac_led_mode_t mode, char *buf, size_t len) +{ + int i; + boolean_t first = B_TRUE; + mac_led_mode_t orig = mode; + + for (i = 0; i < DLLED_MAP_NENTRIES; i++) { + if ((mode & dlled_map[i].dlm_bits) != 0) { + if (first) { + first = B_FALSE; + } else { + (void) strlcat(buf, ",", len); + } + (void) strlcat(buf, dlled_map[i].dlm_name, len); + mode &= ~dlled_map[i].dlm_bits; + } + } + + if (mode != 0) { + (void) snprintf(buf, len, "unknown mode: 0x%x\n", orig); + } +} + + +static int +dlled_set(const char *link, mac_led_mode_t mode) +{ + datalink_id_t linkid; + dladm_status_t status; + dld_ioc_led_t dil; + + if ((status = dladm_name2info(dlled_hdl, link, &linkid, NULL, NULL, + NULL)) != DLADM_STATUS_OK) { + (void) fprintf(stderr, "failed to get link " + "id for link %s: %s\n", link, + dladm_status2str(status, dlled_dlerrmsg)); + return (1); + } + + bzero(&dil, sizeof (dil)); + dil.dil_linkid = linkid; + dil.dil_active = mode; + + if (ioctl(dladm_dld_fd(dlled_hdl), DLDIOC_SETLED, &dil) != 0) { + (void) fprintf(stderr, "failed to set LED on " + "device %s: %s\n", link, strerror(errno)); + return (1); + } + + return (0); +} + +static int +dlled_get_led(dladm_handle_t hdl, datalink_id_t linkid, void *arg) +{ + dladm_status_t status; + char name[MAXLINKNAMELEN]; + char supported[128], active[128]; + dld_ioc_led_t dil; + + if ((status = dladm_datalink_id2info(hdl, linkid, NULL, NULL, NULL, + name, sizeof (name))) != DLADM_STATUS_OK) { + (void) fprintf(stderr, "failed to get datalink name for link " + "%d: %s", linkid, dladm_status2str(status, + dlled_dlerrmsg)); + return (DLADM_WALK_CONTINUE); + } + + + + bzero(&dil, sizeof (dil)); + dil.dil_linkid = linkid; + + if (ioctl(dladm_dld_fd(hdl), DLDIOC_GETLED, &dil) != 0) { + (void) fprintf(stderr, "failed to get LED information for " + "device %s: %s\n", name, strerror(errno)); + return (DLADM_WALK_CONTINUE); + } + + active[0] = '\0'; + supported[0] = '\0'; + dlled_mode2str(dil.dil_active, active, sizeof (active)); + dlled_mode2str(dil.dil_supported, supported, sizeof (supported)); + + printf("%-20s %-12s %s\n", name, active, supported); + + return (DLADM_WALK_CONTINUE); +} + +int +main(int argc, char *argv[]) +{ + int c, ret; + boolean_t opt_s = B_FALSE; + mac_led_mode_t set_mode = 0; + dladm_status_t status; + + dlled_progname = basename(argv[0]); + + while ((c = getopt(argc, argv, ":s:")) != -1) { + switch (c) { + case 's': + opt_s = B_TRUE; + set_mode = dlled_parse_mode(optarg); + break; + case ':': + dlled_usage("option -%c requires an operand\n", optopt); + return (2); + case '?': + default: + dlled_usage("unknown option: -%c\n", optopt); + return (2); + } + } + + argc -= optind; + argv += optind; + + if (opt_s && argc > 1) { + dlled_usage("-s only operates on a single datalink\n"); + return (2); + } + + if (opt_s && argc <= 0) { + dlled_usage("-s requires a datalink\n"); + return (2); + } + + if ((status = dladm_open(&dlled_hdl)) != DLADM_STATUS_OK) { + (void) fprintf(stderr, "failed to open /dev/dld: %s\n", + dladm_status2str(status, dlled_dlerrmsg)); + return (1); + } + + if (opt_s) { + return (dlled_set(argv[0], set_mode)); + } + + (void) printf("%-20s %-12s %s\n", "LINK", "ACTIVE", "SUPPORTED"); + + ret = 0; + if (argc == 0) { + (void) dladm_walk_datalink_id(dlled_get_led, dlled_hdl, NULL, + DATALINK_CLASS_PHYS, DATALINK_ANY_MEDIATYPE, + DLADM_OPT_ACTIVE); + } else { + int i, dlret; + datalink_id_t linkid; + + for (i = 0; i < argc; i++) { + if ((status = dladm_name2info(dlled_hdl, argv[i], + &linkid, NULL, NULL, NULL)) != DLADM_STATUS_OK) { + (void) fprintf(stderr, "failed to get link " + "id for link %s: %s\n", link, + dladm_status2str(status, dlled_dlerrmsg)); + return (1); + } + + dlret = dlled_get_led(dlled_hdl, linkid, NULL); + if (dlret != DLADM_WALK_CONTINUE) { + ret = 1; + break; + } + } + } + + return (ret); +} diff --git a/usr/src/cmd/dlutil/dlrecv.c b/usr/src/cmd/dlutil/dlrecv.c new file mode 100644 index 0000000000..4464baced9 --- /dev/null +++ b/usr/src/cmd/dlutil/dlrecv.c @@ -0,0 +1,237 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2017, Joyent, Inc. + */ + +/* + * Receive a raw Ethernet frame from dlsend. + */ + +#include <stdio.h> +#include <errno.h> +#include <strings.h> +#include <unistd.h> +#include <stdarg.h> +#include <libgen.h> +#include <limits.h> +#include <stdlib.h> +#include <unistd.h> +#include <netdb.h> +#include <libdlpi.h> +#include <stddef.h> +#include <stdint.h> +#include <endian.h> +#include <ctype.h> + +#include "dlsend.h" + + +static uint_t dlrecv_sap = DLSEND_SAP; +static const char *dlrecv_prog; + +static void +dlrecv_warn(const char *fmt, ...) +{ + va_list ap; + + (void) fprintf(stderr, "%s: ", dlrecv_prog); + va_start(ap, fmt); + (void) vfprintf(stderr, fmt, ap); + va_end(ap); +} + +static void +dlrecv_usage(const char *fmt, ...) +{ + if (fmt != NULL) { + va_list ap; + + (void) fprintf(stderr, "%s: ", dlrecv_prog); + va_start(ap, fmt); + (void) vfprintf(stderr, fmt, ap); + va_end(ap); + } + + (void) fprintf(stderr, "Usage: %s [-s sap] device\n" + "\t-s sap\tspecify SAP to send on\n", + dlrecv_prog); +} + +static boolean_t +dlrecv_isvalid(dlsend_msg_t *msg) +{ + uint_t i; + boolean_t nul; + + nul = B_FALSE; + for (i = 0; i < sizeof (msg->dm_host); i++) { + if (!isprint(msg->dm_host[i]) && + msg->dm_host[i] != '\0') { + dlrecv_warn("Encountered bad byte in dm_host[%d]\n", + i); + return (B_FALSE); + } + + if (msg->dm_host[i] == '\0') + nul = B_TRUE; + } + + if (!nul) { + dlrecv_warn("Missing NUL in dm_host\n"); + return (B_FALSE); + } + + nul = B_FALSE; + for (i = 0; i < sizeof (msg->dm_mesg); i++) { + if (!isprint(msg->dm_mesg[i]) && + msg->dm_mesg[i] != '\0') { + dlrecv_warn("Encountered bad byte in dm_mesg[%d]\n", + i); + return (B_FALSE); + } + + if (msg->dm_mesg[i] == '\0') + nul = B_TRUE; + } + + if (!nul) { + dlrecv_warn("Missing NUL in dm_mesg\n"); + return (B_FALSE); + } + + if (strcmp(msg->dm_mesg, DLSEND_MSG) != 0) { + dlrecv_warn("Missing expected message (%s)\n", DLSEND_MSG); + return (B_FALSE); + } + + return (B_TRUE); +} + +static void +dlrecv_print(dlsend_msg_t *msg, dlpi_recvinfo_t *rinfo, boolean_t invalid) +{ + uint_t i; + + printf("Received %s from ", invalid ? "invalid message" : "Elbereth"); + + for (i = 0; i < rinfo->dri_destaddrlen; i++) { + (void) printf("%02x", rinfo->dri_destaddr[i]); + if (i + 1 != rinfo->dri_destaddrlen) + (void) putchar(':'); + } + + if (invalid) { + return; + } + + printf(" seq=%" PRIu64 " host=%s\n", betoh64(msg->dm_count), + msg->dm_host); +} + +int +main(int argc, char *argv[]) +{ + int c, ret; + char *eptr; + unsigned long sap; + uint_t bind_sap; + dlpi_handle_t dh; + + dlrecv_prog = basename(argv[0]); + + while ((c = getopt(argc, argv, ":s:")) != -1) { + switch (c) { + case 's': + errno = 0; + sap = strtoul(optarg, &eptr, 10); + if (errno != 0 || sap == 0 || sap >= UINT16_MAX || + *eptr != '\0') { + dlrecv_usage("Invalid value for sap (-s): %s\n", + optarg); + return (2); + } + dlrecv_sap = sap; + break; + case ':': + dlrecv_usage("Option -%c requires an operand\n", + optopt); + return (2); + case '?': + dlrecv_usage("Unknown option: -%c\n", optopt); + return (2); + } + } + + argc -= optind; + argv += optind; + + if (argc != 1) { + dlrecv_usage("missing required operands\n"); + return (2); + } + + if ((ret = dlpi_open(argv[0], &dh, 0)) != DLPI_SUCCESS) { + dlrecv_warn("failed to open %s: %s\n", argv[0], + dlpi_strerror(ret)); + exit(1); + } + + if ((ret = dlpi_bind(dh, dlrecv_sap, &bind_sap)) != DLPI_SUCCESS) { + dlrecv_warn("failed to bind to sap 0x%x: %s\n", dlrecv_sap, + dlpi_strerror(ret)); + exit(1); + } + + if (bind_sap != dlrecv_sap) { + dlrecv_warn("failed to bind to requested sap 0x%x, bound to " + "0x%x\n", dlrecv_sap, bind_sap); + exit(1); + } + + for (;;) { + dlpi_recvinfo_t rinfo; + dlsend_msg_t msg; + size_t msglen; + boolean_t invalid = B_FALSE; + + msglen = sizeof (msg); + ret = dlpi_recv(dh, NULL, NULL, &msg, &msglen, -1, &rinfo); + if (ret != DLPI_SUCCESS) { + dlrecv_warn("failed to receive data: %s\n", + dlpi_strerror(ret)); + continue; + } + + if (msglen != rinfo.dri_totmsglen) { + dlrecv_warn("message truncated: expected %ld bytes, " + "got %ld\n", sizeof (dlsend_msg_t), + rinfo.dri_totmsglen); + invalid = B_TRUE; + } + + if (msglen != sizeof (msg)) { + dlrecv_warn("message too short: expected %ld bytes, " + "got %ld\n", sizeof (dlsend_msg_t), + msglen); + invalid = B_TRUE; + } + + if (!invalid) { + invalid = !dlrecv_isvalid(&msg); + } + + dlrecv_print(&msg, &rinfo, invalid); + } + + return (0); +} diff --git a/usr/src/cmd/dlutil/dlsend.c b/usr/src/cmd/dlutil/dlsend.c new file mode 100644 index 0000000000..e77d157f53 --- /dev/null +++ b/usr/src/cmd/dlutil/dlsend.c @@ -0,0 +1,163 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2017, Joyent, Inc. + */ + +/* + * Send a raw Ethernet frame once a second to a specified MAC address. + */ + +#include <stdio.h> +#include <errno.h> +#include <strings.h> +#include <unistd.h> +#include <stdarg.h> +#include <libgen.h> +#include <limits.h> +#include <stdlib.h> +#include <unistd.h> +#include <netdb.h> +#include <libdlpi.h> +#include <stddef.h> +#include <stdint.h> +#include <endian.h> + +#include "dlsend.h" + +static uint_t dlsend_sap = DLSEND_SAP; +static const char *dlsend_msg = DLSEND_MSG; +static const char *dlsend_prog; + +static void +dlsend_warn(const char *fmt, ...) +{ + va_list ap; + + (void) fprintf(stderr, "%s: ", dlsend_prog); + va_start(ap, fmt); + (void) vfprintf(stderr, fmt, ap); + va_end(ap); +} + +static void +dlsend_usage(const char *fmt, ...) +{ + if (fmt != NULL) { + va_list ap; + + (void) fprintf(stderr, "%s: ", dlsend_prog); + va_start(ap, fmt); + (void) vfprintf(stderr, fmt, ap); + va_end(ap); + } + + (void) fprintf(stderr, "Usage: %s [-s sap] device target-mac\n" + "\t-s sap\tspecify SAP to send on\n", + dlsend_prog); +} + +int +main(int argc, char *argv[]) +{ + int c, maclen, ret; + unsigned long sap; + char *eptr; + uchar_t *mac; + char host[MAXHOSTNAMELEN]; + uint_t bind_sap; + dlpi_handle_t dh; + uint64_t count; + + dlsend_prog = basename(argv[0]); + + while ((c = getopt(argc, argv, ":s:")) != -1) { + switch (c) { + case 's': + errno = 0; + sap = strtoul(optarg, &eptr, 10); + if (errno != 0 || sap == 0 || sap >= UINT16_MAX || + *eptr != '\0') { + dlsend_usage("Invalid value for sap (-s): %s\n", + optarg); + return (2); + } + dlsend_sap = sap; + break; + case ':': + dlsend_usage("Option -%c requires an operand\n", + optopt); + return (2); + case '?': + dlsend_usage("Unknown option: -%c\n", optopt); + return (2); + } + } + + argc -= optind; + argv += optind; + + if (argc != 2) { + dlsend_usage("missing required operands\n"); + return (2); + } + + if ((mac = _link_aton(argv[1], &maclen)) == NULL) { + dlsend_warn("failed to convert target address %s\n", argv[1]); + return (1); + } + + if (gethostname(host, sizeof (host)) != 0) { + dlsend_warn("failed to obtain the system hostname: %s\n", + strerror(errno)); + (void) strlcpy(host, "<unknown host>", sizeof (host)); + } + + if ((ret = dlpi_open(argv[0], &dh, 0)) != DLPI_SUCCESS) { + dlsend_warn("failed to open %s: %s\n", argv[0], + dlpi_strerror(ret)); + exit(1); + } + + if ((ret = dlpi_bind(dh, dlsend_sap, &bind_sap)) != DLPI_SUCCESS) { + dlsend_warn("failed to bind to sap 0x%x: %s\n", dlsend_sap, + dlpi_strerror(ret)); + exit(1); + } + + if (bind_sap != dlsend_sap) { + dlsend_warn("failed to bind to requested sap 0x%x, bound to " + "0x%x\n", dlsend_sap, bind_sap); + exit(1); + } + + count = 0; + for (;;) { + dlsend_msg_t msg; + + count++; + bzero(&msg, sizeof (msg)); + msg.dm_count = htobe64(count); + (void) strlcpy(msg.dm_host, host, sizeof (msg.dm_host)); + (void) strlcpy(msg.dm_mesg, dlsend_msg, sizeof (msg.dm_mesg)); + ret = dlpi_send(dh, mac, maclen, &msg, sizeof (msg), NULL); + if (ret != DLPI_SUCCESS) { + dlsend_warn("failed to send message: %s\n", + dlpi_strerror(ret)); + exit(1); + } + + sleep(1); + } + + return (0); +} diff --git a/usr/src/cmd/dlutil/dlsend.h b/usr/src/cmd/dlutil/dlsend.h new file mode 100644 index 0000000000..5674ff0b78 --- /dev/null +++ b/usr/src/cmd/dlutil/dlsend.h @@ -0,0 +1,45 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2017, Joyent, Inc. + */ + +#ifndef _DLSEND_H +#define _DLSEND_H + +/* + * A common header file for things that dlsend and dlrecv need. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * We need to pick an arbitrary Ethertype to squat on for the purposes of this + * testing program. As such we use one with a recongizable string. If someone + * comes along and uses this, then we should get off of it. + */ +#define DLSEND_SAP 0xdeed +#define DLSEND_MSG "A Elbereth Gilthoniel" + +typedef struct dlsend_msg { + uint64_t dm_count; + char dm_host[MAXHOSTNAMELEN]; + char dm_mesg[32]; +} dlsend_msg_t; + +#ifdef __cplusplus +} +#endif + +#endif /* _DLSEND_H */ diff --git a/usr/src/cmd/dlutil/dltraninfo.c b/usr/src/cmd/dlutil/dltraninfo.c new file mode 100644 index 0000000000..2e6f7ee2e1 --- /dev/null +++ b/usr/src/cmd/dlutil/dltraninfo.c @@ -0,0 +1,356 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2017, Joyent, Inc. + */ + +/* + * Private utility to dump transceiver information for each physical datalink. + * Something like this should eventually be a part of dladm or similar. + */ + +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <strings.h> +#include <errno.h> +#include <ctype.h> +#include <unistd.h> +#include <limits.h> +#include <stdarg.h> +#include <libgen.h> + +#include <libdladm.h> +#include <libdllink.h> +#include <sys/dld.h> +#include <sys/dld_ioc.h> +#include <sys/dls_mgmt.h> + +#define DLTRAN_KIND_LEN 64 + +static dladm_handle_t dltran_hdl; +static char dltran_dlerrmsg[DLADM_STRSIZE]; +static char **dltran_links; +static int dltran_nlinks; /* array size */ +static int dltran_clinks; /* current count */ +static boolean_t dltran_tranid_set; +static int dltran_tranid; +static const char *dltran_progname; + +/* ARGSUSED */ +static int +dltran_dump_transceivers(dladm_handle_t hdl, datalink_id_t linkid, void *arg) +{ + dladm_status_t status; + char name[MAXLINKNAMELEN]; + dld_ioc_gettran_t gt; + uint_t count, i; + + if ((status = dladm_datalink_id2info(hdl, linkid, NULL, NULL, NULL, + name, sizeof (name))) != DLADM_STATUS_OK) { + (void) fprintf(stderr, "failed to get datalink name for link " + "%d: %s", linkid, dladm_status2str(status, + dltran_dlerrmsg)); + return (DLADM_WALK_CONTINUE); + } + + if (dltran_nlinks != NULL) { + for (i = 0; i < dltran_clinks; i++) { + if (strcmp(dltran_links[i], name) == 0) + break; + } + if (i == dltran_clinks) + return (DLADM_WALK_CONTINUE); + } + + bzero(>, sizeof (gt)); + gt.dgt_linkid = linkid; + gt.dgt_tran_id = DLDIOC_GETTRAN_GETNTRAN; + + if (ioctl(dladm_dld_fd(hdl), DLDIOC_GETTRAN, >) != 0) { + (void) fprintf(stderr, "failed to get transceiver count " + "for device %s: %s\n", + name, strerror(errno)); + return (DLADM_WALK_CONTINUE); + } + + + count = gt.dgt_tran_id; + (void) printf("%s: discovered %d transceivers\n", name, count); + for (i = 0; i < count; i++) { + if (dltran_tranid_set && i != dltran_tranid) + continue; + bzero(>, sizeof (gt)); + gt.dgt_linkid = linkid; + gt.dgt_tran_id = i; + + if (ioctl(dladm_dld_fd(hdl), DLDIOC_GETTRAN, >) != 0) { + (void) fprintf(stderr, "failed to get tran info for " + "%s: %s\n", name, strerror(errno)); + return (DLADM_WALK_CONTINUE); + } + + (void) printf("\ttransceiver %d present: %s\n", i, + gt.dgt_present ? "yes" : "no"); + if (!gt.dgt_present) + continue; + (void) printf("\ttransceiver %d usable: %s\n", i, + gt.dgt_usable ? "yes" : "no"); + } + + return (DLADM_WALK_CONTINUE); +} + +/* + * This routine basically assumes that we'll have 16 byte aligned output to + * print out the human readable output. + */ +static void +dltran_dump_page(uint8_t *buf, size_t nbytes, uint_t page) +{ + size_t i; + static boolean_t first = B_TRUE; + + if (first) { + (void) printf("page %*s 0", 4, ""); + for (i = 1; i < 16; i++) { + if (i % 4 == 0 && i % 16 != 0) { + (void) printf(" "); + } + + (void) printf("%2x", i); + } + (void) printf(" v123456789abcdef\n"); + first = B_FALSE; + } + for (i = 0; i < nbytes; i++) { + + if (i % 16 == 0) { + (void) printf("0x%02x %04x: ", page, i); + } + + if (i % 4 == 0 && i % 16 != 0) { + (void) printf(" "); + } + + + (void) printf("%02x", buf[i]); + + if (i % 16 == 15) { + int j; + (void) printf(" "); + for (j = i - (i % 16); j <= i; j++) { + if (!isprint(buf[j])) { + (void) printf("."); + } else { + (void) printf("%c", buf[j]); + } + } + (void) printf("\n"); + } + } +} + +/* + * We always read 256 bytes even though only the first 128 bytes are sometimes + * significant on a given page and others are reserved. + */ +static int +dltran_read_page(datalink_id_t link, uint_t tranid, uint_t page, uint8_t *bufp, + size_t buflen) +{ + dld_ioc_tranio_t dti; + + bzero(bufp, buflen); + bzero(&dti, sizeof (dti)); + + dti.dti_linkid = link; + dti.dti_tran_id = tranid; + dti.dti_page = page; + dti.dti_nbytes = buflen; + dti.dti_off = 0; + dti.dti_buf = (uintptr_t)(void *)bufp; + + if (ioctl(dladm_dld_fd(dltran_hdl), DLDIOC_READTRAN, &dti) != 0) { + (void) fprintf(stderr, "failed to read transceiver page " + "0x%2x: %s\n", page, strerror(errno)); + return (1); + } + + dltran_dump_page(bufp, dti.dti_nbytes, page); + + return (0); +} + +static boolean_t +dltran_is_8472(uint8_t *buf) +{ + switch (buf[0]) { + case 0xc: + case 0xd: + case 0x11: + /* + * Catch cases that refer explicitly to QSFP and newer. + */ + return (B_FALSE); + default: + break; + } + + /* + * Check the byte that indicates compliance with SFF 8472. Use this to + * know if we can read page 0xa2 or not. + */ + if (buf[94] == 0) + return (B_FALSE); + + return (B_TRUE); +} + +static int +dltran_read_link(const char *link) +{ + dladm_status_t status; + datalink_id_t linkid; + dld_ioc_gettran_t gt; + uint8_t buf[256]; + int ret; + + if ((status = dladm_name2info(dltran_hdl, link, &linkid, NULL, NULL, + NULL)) != DLADM_STATUS_OK) { + (void) fprintf(stderr, "failed to get link id for link " + "%s: %s\n", link, + dladm_status2str(status, dltran_dlerrmsg)); + return (1); + } + + gt.dgt_linkid = linkid; + gt.dgt_tran_id = dltran_tranid; + + if (ioctl(dladm_dld_fd(dltran_hdl), DLDIOC_GETTRAN, >) != 0) { + (void) fprintf(stderr, "failed to get transceiver information " + "for %s: %s\n", link, strerror(errno)); + return (1); + } + + if ((ret = dltran_read_page(linkid, dltran_tranid, 0xa0, buf, + sizeof (buf))) != 0) { + return (ret); + } + + if (!dltran_is_8472(buf)) { + return (0); + } + + return (dltran_read_page(linkid, dltran_tranid, 0xa2, buf, + sizeof (buf))); +} + +static void +dltran_usage(const char *fmt, ...) +{ + if (fmt != NULL) { + va_list ap; + + (void) fprintf(stderr, "%s: ", dltran_progname); + va_start(ap, fmt); + (void) vfprintf(stderr, fmt, ap); + va_end(ap); + } + + (void) fprintf(stderr, "Usage: %s [-i id] [-l link]... [-r]\n" + "\n" + "\t-i id specify a transceiver id to operate on\n" + "\t-l link specify a data link to operate on\n" + "\t-r read transceiver page\n", + dltran_progname); +} + +int +main(int argc, char *argv[]) +{ + int c; + char *eptr; + long l; + dladm_status_t status; + boolean_t do_read = B_FALSE; + + dltran_progname = basename(argv[0]); + + while ((c = getopt(argc, argv, ":hi:l:r")) != -1) { + switch (c) { + case 'i': + errno = 0; + l = strtol(optarg, &eptr, 10); + if (errno != 0 || *eptr != '\0' || l < 0 || + l > INT_MAX) { + (void) fprintf(stderr, "invalid value for -i: " + "%s\n", optarg); + return (2); + } + dltran_tranid = (int)l; + dltran_tranid_set = B_TRUE; + break; + case 'l': + if (dltran_nlinks == dltran_clinks) { + char **p; + dltran_nlinks += 8; + + p = realloc(dltran_links, + sizeof (char **) * dltran_nlinks); + if (p == NULL) { + (void) fprintf(stderr, "failed to " + "allocate space for %d links: %s\n", + dltran_nlinks, strerror(errno)); + return (1); + } + dltran_links = p; + } + dltran_links[dltran_clinks++] = optarg; + break; + case 'r': + do_read = B_TRUE; + break; + case ':': + dltran_usage("option -%c requires an " + "operand\n", optopt); + return (2); + case '?': + default: + dltran_usage("unknown option: -%c\n", optopt); + return (2); + } + } + + if (do_read && dltran_clinks != 1) { + (void) fprintf(stderr, "-r requires exactly one link " + "specified with -l\n"); + return (2); + } + + if ((status = dladm_open(&dltran_hdl)) != DLADM_STATUS_OK) { + (void) fprintf(stderr, "failed to open /dev/dld: %s\n", + dladm_status2str(status, dltran_dlerrmsg)); + return (1); + } + + if (do_read) { + return (dltran_read_link(dltran_links[0])); + } + + (void) dladm_walk_datalink_id(dltran_dump_transceivers, dltran_hdl, + NULL, DATALINK_CLASS_PHYS, DATALINK_ANY_MEDIATYPE, + DLADM_OPT_ACTIVE); + + return (0); +} |
