summaryrefslogtreecommitdiff
path: root/usr/src/cmd/dlutil
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/cmd/dlutil')
-rw-r--r--usr/src/cmd/dlutil/Makefile40
-rw-r--r--usr/src/cmd/dlutil/dlled.c285
-rw-r--r--usr/src/cmd/dlutil/dlrecv.c237
-rw-r--r--usr/src/cmd/dlutil/dlsend.c163
-rw-r--r--usr/src/cmd/dlutil/dlsend.h45
-rw-r--r--usr/src/cmd/dlutil/dltraninfo.c356
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(&gt, sizeof (gt));
+ gt.dgt_linkid = linkid;
+ gt.dgt_tran_id = DLDIOC_GETTRAN_GETNTRAN;
+
+ if (ioctl(dladm_dld_fd(hdl), DLDIOC_GETTRAN, &gt) != 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(&gt, sizeof (gt));
+ gt.dgt_linkid = linkid;
+ gt.dgt_tran_id = i;
+
+ if (ioctl(dladm_dld_fd(hdl), DLDIOC_GETTRAN, &gt) != 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, &gt) != 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);
+}