1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12 /*
13 * Copyright (c) 2017, Joyent, Inc.
14 */
15
16 /*
17 * Private utility to dump transceiver information for each physical datalink.
18 * Something like this should eventually be a part of dladm or similar.
19 */
20
21 #include <stdio.h>
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 #include <fcntl.h>
25 #include <strings.h>
26 #include <errno.h>
27 #include <ctype.h>
28 #include <unistd.h>
29 #include <limits.h>
30 #include <stdarg.h>
31 #include <libgen.h>
32
33 #include <libdladm.h>
34 #include <libdllink.h>
35 #include <sys/dld.h>
36 #include <sys/dld_ioc.h>
37 #include <sys/dls_mgmt.h>
38 #include <libsff.h>
39
40 #define DLTRAN_KIND_LEN 64
41
42 static dladm_handle_t dltran_hdl;
43 static char dltran_dlerrmsg[DLADM_STRSIZE];
44 static const char *dltran_progname;
45 static boolean_t dltran_verbose;
46 static boolean_t dltran_hex;
47 static boolean_t dltran_write;
48 static int dltran_outfd;
49 static int dltran_errors;
50
51 /*
52 * This routine basically assumes that we'll have 16 byte aligned output to
53 * print out the human readable output.
54 */
55 static void
dltran_dump_page(uint8_t * buf,size_t nbytes,uint_t page)56 dltran_dump_page(uint8_t *buf, size_t nbytes, uint_t page)
57 {
58 size_t i;
59 static boolean_t first = B_TRUE;
60
61 if (first) {
62 (void) printf("page %*s 0", 4, "");
63 for (i = 1; i < 16; i++) {
64 if (i % 4 == 0 && i % 16 != 0) {
65 (void) printf(" ");
66 }
67
68 (void) printf("%2x", i);
69 }
70 (void) printf(" v123456789abcdef\n");
71 first = B_FALSE;
72 }
73 for (i = 0; i < nbytes; i++) {
74
75 if (i % 16 == 0) {
76 (void) printf("0x%02x %04x: ", page, i);
77 }
78
79 if (i % 4 == 0 && i % 16 != 0) {
80 (void) printf(" ");
81 }
82
83
84 (void) printf("%02x", buf[i]);
85
86 if (i % 16 == 15) {
87 int j;
88 (void) printf(" ");
89 for (j = i - (i % 16); j <= i; j++) {
90 if (!isprint(buf[j])) {
91 (void) printf(".");
92 } else {
93 (void) printf("%c", buf[j]);
94 }
95 }
96 (void) printf("\n");
97 }
98 }
99 }
100
101 static int
dltran_read_page(datalink_id_t link,uint_t tranid,uint_t page,uint8_t * bufp,size_t * buflen)102 dltran_read_page(datalink_id_t link, uint_t tranid, uint_t page, uint8_t *bufp,
103 size_t *buflen)
104 {
105 dld_ioc_tranio_t dti;
106
107 bzero(bufp, *buflen);
108 bzero(&dti, sizeof (dti));
109
110 dti.dti_linkid = link;
111 dti.dti_tran_id = tranid;
112 dti.dti_page = page;
113 dti.dti_nbytes = *buflen;
114 dti.dti_off = 0;
115 dti.dti_buf = (uintptr_t)(void *)bufp;
116
117 if (ioctl(dladm_dld_fd(dltran_hdl), DLDIOC_READTRAN, &dti) != 0) {
118 (void) fprintf(stderr, "failed to read transceiver page "
119 "0x%2x: %s\n", page, strerror(errno));
120 return (1);
121 }
122
123 *buflen = dti.dti_nbytes;
124 return (0);
125 }
126
127 static boolean_t
dltran_is_8472(uint8_t * buf)128 dltran_is_8472(uint8_t *buf)
129 {
130 switch (buf[0]) {
131 case 0xc:
132 case 0xd:
133 case 0x11:
134 /*
135 * Catch cases that refer explicitly to QSFP and newer.
136 */
137 return (B_FALSE);
138 default:
139 break;
140 }
141
142 /*
143 * Check the byte that indicates compliance with SFF 8472. Use this to
144 * know if we can read page 0xa2 or not.
145 */
146 if (buf[94] == 0)
147 return (B_FALSE);
148
149 return (B_TRUE);
150 }
151
152 static void
dltran_hex_dump(datalink_id_t linkid,uint_t tranid)153 dltran_hex_dump(datalink_id_t linkid, uint_t tranid)
154 {
155 uint8_t buf[256];
156 size_t buflen = sizeof (buf);
157
158 if (dltran_read_page(linkid, tranid, 0xa0, buf, &buflen) != 0) {
159 dltran_errors++;
160 return;
161 }
162
163 dltran_dump_page(buf, buflen, 0xa0);
164
165 if (!dltran_is_8472(buf)) {
166 return;
167 }
168
169 buflen = sizeof (buf);
170 if (dltran_read_page(linkid, tranid, 0xa2, buf, &buflen) != 0) {
171 dltran_errors++;
172 return;
173 }
174
175 dltran_dump_page(buf, buflen, 0xa2);
176 }
177
178 static void
dltran_write_page(datalink_id_t linkid,uint_t tranid)179 dltran_write_page(datalink_id_t linkid, uint_t tranid)
180 {
181 uint8_t buf[256];
182 size_t buflen = sizeof (buf);
183 off_t off;
184
185 if (dltran_read_page(linkid, tranid, 0xa0, buf, &buflen) != 0) {
186 dltran_errors++;
187 return;
188 }
189
190 off = 0;
191 while (buflen > 0) {
192 ssize_t ret;
193
194 ret = write(dltran_outfd, buf + off, buflen);
195 if (ret == -1) {
196 (void) fprintf(stderr, "failed to write data "
197 "to output file: %s\n", strerror(errno));
198 dltran_errors++;
199 return;
200 }
201
202 off += ret;
203 buflen -= ret;
204 }
205 }
206
207 static void
dltran_verbose_dump(datalink_id_t linkid,uint_t tranid)208 dltran_verbose_dump(datalink_id_t linkid, uint_t tranid)
209 {
210 uint8_t buf[256];
211 size_t buflen = sizeof (buf);
212 int ret;
213 nvlist_t *nvl;
214
215 if (dltran_read_page(linkid, tranid, 0xa0, buf, &buflen) != 0) {
216 dltran_errors++;
217 return;
218 }
219
220 ret = libsff_parse(buf, buflen, 0xa0, &nvl);
221 if (ret == 0) {
222 dump_nvlist(nvl, 8);
223 nvlist_free(nvl);
224 } else {
225 fprintf(stderr, "failed to parse sfp data: %s\n",
226 strerror(ret));
227 dltran_errors++;
228 }
229 }
230
231 static int
dltran_dump_transceivers(dladm_handle_t hdl,datalink_id_t linkid,void * arg)232 dltran_dump_transceivers(dladm_handle_t hdl, datalink_id_t linkid, void *arg)
233 {
234 dladm_status_t status;
235 char name[MAXLINKNAMELEN];
236 dld_ioc_gettran_t gt;
237 uint_t count, i, tranid = UINT_MAX;
238 boolean_t tran_found = B_FALSE;
239 uint_t *tranidp = arg;
240
241 if (tranidp != NULL)
242 tranid = *tranidp;
243
244 if ((status = dladm_datalink_id2info(hdl, linkid, NULL, NULL, NULL,
245 name, sizeof (name))) != DLADM_STATUS_OK) {
246 (void) fprintf(stderr, "failed to get datalink name for link "
247 "%d: %s", linkid, dladm_status2str(status,
248 dltran_dlerrmsg));
249 dltran_errors++;
250 return (DLADM_WALK_CONTINUE);
251 }
252
253 bzero(>, sizeof (gt));
254 gt.dgt_linkid = linkid;
255 gt.dgt_tran_id = DLDIOC_GETTRAN_GETNTRAN;
256
257 if (ioctl(dladm_dld_fd(hdl), DLDIOC_GETTRAN, >) != 0) {
258 if (errno != ENOTSUP) {
259 (void) fprintf(stderr, "failed to get transceiver "
260 "count for device %s: %s\n",
261 name, strerror(errno));
262 dltran_errors++;
263 }
264 return (DLADM_WALK_CONTINUE);
265 }
266
267 count = gt.dgt_tran_id;
268 (void) printf("%s: discovered %d transceiver%s\n", name, count,
269 count > 1 ? "s" : "");
270 for (i = 0; i < count; i++) {
271 if (tranid != UINT_MAX && i != tranid)
272 continue;
273 if (tranid != UINT_MAX)
274 tran_found = B_TRUE;
275 bzero(>, sizeof (gt));
276 gt.dgt_linkid = linkid;
277 gt.dgt_tran_id = i;
278
279 if (ioctl(dladm_dld_fd(hdl), DLDIOC_GETTRAN, >) != 0) {
280 (void) fprintf(stderr, "failed to get tran info for "
281 "%s: %s\n", name, strerror(errno));
282 dltran_errors++;
283 return (DLADM_WALK_CONTINUE);
284 }
285
286 if (dltran_hex && !gt.dgt_present)
287 continue;
288 if (!dltran_hex && !dltran_write) {
289 (void) printf("\ttransceiver %d present: %s\n", i,
290 gt.dgt_present ? "yes" : "no");
291 if (!gt.dgt_present)
292 continue;
293 (void) printf("\ttransceiver %d usable: %s\n", i,
294 gt.dgt_usable ? "yes" : "no");
295 }
296
297 if (dltran_verbose) {
298 dltran_verbose_dump(linkid, i);
299 }
300
301 if (dltran_write) {
302 if (!gt.dgt_present) {
303 (void) fprintf(stderr, "warning: no "
304 "transceiver present in port %d, not "
305 "writing\n", i);
306 dltran_errors++;
307 continue;
308 }
309 dltran_write_page(linkid, i);
310 }
311
312 if (dltran_hex) {
313 printf("transceiver %d data:\n", i);
314 dltran_hex_dump(linkid, i);
315 }
316 }
317
318 if (tranid != UINT_MAX && !tran_found) {
319 dltran_errors++;
320 (void) fprintf(stderr, "failed to find transceiver %d on "
321 "link %s\n", tranid, name);
322 }
323
324 return (DLADM_WALK_CONTINUE);
325 }
326
327
328 static void
dltran_usage(const char * fmt,...)329 dltran_usage(const char *fmt, ...)
330 {
331 if (fmt != NULL) {
332 va_list ap;
333
334 (void) fprintf(stderr, "%s: ", dltran_progname);
335 va_start(ap, fmt);
336 (void) vfprintf(stderr, fmt, ap);
337 va_end(ap);
338 }
339
340 (void) fprintf(stderr, "Usage: %s [-x | -v | -w file] [tran]...\n"
341 "\n"
342 "\t-v display all transceiver information\n"
343 "\t-w write transceiver data page 0xa0 to file\n"
344 "\t-x dump raw hexadecimal for transceiver\n",
345 dltran_progname);
346 }
347
348 int
main(int argc,char * argv[])349 main(int argc, char *argv[])
350 {
351 int c;
352 dladm_status_t status;
353 const char *outfile = NULL;
354 uint_t count = 0;
355
356 dltran_progname = basename(argv[0]);
357
358 while ((c = getopt(argc, argv, ":xvw:")) != -1) {
359 switch (c) {
360 case 'v':
361 dltran_verbose = B_TRUE;
362 break;
363 case 'x':
364 dltran_hex = B_TRUE;
365 break;
366 case 'w':
367 dltran_write = B_TRUE;
368 outfile = optarg;
369 break;
370 case ':':
371 dltran_usage("option -%c requires an "
372 "operand\n", optopt);
373 return (2);
374 case '?':
375 default:
376 dltran_usage("unknown option: -%c\n", optopt);
377 return (2);
378 }
379 }
380
381 argc -= optind;
382 argv += optind;
383
384 if (dltran_verbose)
385 count++;
386 if (dltran_hex)
387 count++;
388 if (dltran_write)
389 count++;
390 if (count > 1) {
391 (void) fprintf(stderr, "only one of -v, -w, and -x may be "
392 "specified\n");
393 return (2);
394 }
395
396 if (dltran_write) {
397 if ((dltran_outfd = open(outfile, O_RDWR | O_TRUNC | O_CREAT,
398 0644)) < 0) {
399 (void) fprintf(stderr, "failed to open output file "
400 "%s: %s\n", outfile, strerror(errno));
401 return (1);
402 }
403 }
404
405 if ((status = dladm_open(&dltran_hdl)) != DLADM_STATUS_OK) {
406 (void) fprintf(stderr, "failed to open /dev/dld: %s\n",
407 dladm_status2str(status, dltran_dlerrmsg));
408 return (1);
409 }
410
411 if (argc == 0) {
412 (void) dladm_walk_datalink_id(dltran_dump_transceivers,
413 dltran_hdl, NULL, DATALINK_CLASS_PHYS,
414 DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE);
415 } else {
416 int i;
417 char *c;
418
419 for (i = 0; i < argc; i++) {
420 uint_t tran;
421 uint_t *tranidp = NULL;
422 datalink_id_t linkid;
423
424 if ((c = strrchr(argv[i], '/')) != NULL) {
425 unsigned long u;
426 char *eptr;
427
428 c++;
429 errno = 0;
430 u = strtoul(c, &eptr, 10);
431 if (errno != 0 || *eptr != '\0' ||
432 u >= UINT_MAX) {
433 (void) fprintf(stderr, "failed to "
434 "parse link/transceiver: %s\n",
435 argv[i]);
436 return (1);
437 }
438 c--;
439 *c = '\0';
440 tran = (uint_t)u;
441 tranidp = &tran;
442 }
443
444 if ((status = dladm_name2info(dltran_hdl, argv[i],
445 &linkid, NULL, NULL, NULL)) != DLADM_STATUS_OK) {
446 (void) fprintf(stderr, "failed to get link "
447 "id for link %s: %s\n", argv[i],
448 dladm_status2str(status, dltran_dlerrmsg));
449 return (1);
450 }
451
452 (void) dltran_dump_transceivers(dltran_hdl, linkid,
453 tranidp);
454 }
455 }
456
457 return (dltran_errors != 0 ? 1 : 0);
458 }
459