Home | History | Annotate | Download | only in common
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 /*
     22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 #include <stdio.h>
     27 #include <stdlib.h>
     28 #include <strings.h>
     29 #include <err.h>
     30 #include <errno.h>
     31 #include <fcntl.h>
     32 #include <kstat.h>
     33 #include <limits.h>
     34 #include <unistd.h>
     35 #include <signal.h>
     36 #include <sys/dld.h>
     37 #include <sys/ddi.h>
     38 
     39 #include <libdllink.h>
     40 #include <libdlflow.h>
     41 #include <libdlstat.h>
     42 
     43 /*
     44  * x86 <sys/regs> ERR conflicts with <curses.h> ERR.
     45  * Include curses.h last.
     46  */
     47 #if	defined(ERR)
     48 #undef  ERR
     49 #endif
     50 #include <curses.h>
     51 
     52 struct flowlist {
     53 	char		flowname[MAXFLOWNAMELEN];
     54 	char		linkname[MAXLINKNAMELEN];
     55 	datalink_id_t	linkid;
     56 	int		fd;
     57 	uint64_t	ifspeed;
     58 	boolean_t	first;
     59 	boolean_t	display;
     60 	pktsum_t 	prevstats;
     61 	pktsum_t	diffstats;
     62 };
     63 
     64 static	int	maxx, maxy, redraw = 0;
     65 static	volatile uint_t handle_resize = 0, handle_break = 0;
     66 
     67 pktsum_t		totalstats;
     68 struct flowlist		*stattable = NULL;
     69 static int		statentry = -1, maxstatentries = 0;
     70 
     71 #define	STATGROWSIZE	16
     72 
     73 /*
     74  * Search for flowlist entry in stattable which matches
     75  * the flowname and linkide.  If no match is found, use
     76  * next available slot.  If no slots are available,
     77  * reallocate table with  more slots.
     78  *
     79  * Return: *flowlist of matching flow
     80  *         NULL if realloc fails
     81  */
     82 
     83 static struct flowlist *
     84 findstat(const char *flowname, datalink_id_t linkid)
     85 {
     86 	int match = 0;
     87 	struct flowlist *flist;
     88 
     89 	/* Look for match in the stattable */
     90 	for (match = 0, flist = stattable;
     91 	    match <= statentry;
     92 	    match++, flist++) {
     93 
     94 		if (flist == NULL)
     95 			break;
     96 		/* match the flowname */
     97 		if (flowname != NULL) {
     98 			if (strncmp(flowname, flist->flowname, MAXFLOWNAMELEN)
     99 			    == NULL)
    100 				return (flist);
    101 		/* match the linkid */
    102 		} else {
    103 			if (linkid == flist->linkid)
    104 				return (flist);
    105 		}
    106 	}
    107 
    108 	/*
    109 	 * No match found in the table.  Store statistics in the next slot.
    110 	 * If necessary, make room for this entry.
    111 	 */
    112 	statentry++;
    113 	if ((maxstatentries == 0) || (maxstatentries == statentry)) {
    114 		maxstatentries += STATGROWSIZE;
    115 		stattable = realloc(stattable,
    116 		    maxstatentries * sizeof (struct flowlist));
    117 		if (stattable == NULL) {
    118 			perror("realloc");
    119 			return (struct flowlist *)(NULL);
    120 		}
    121 	}
    122 	flist = &stattable[statentry];
    123 	bzero(flist, sizeof (struct flowlist));
    124 
    125 	if (flowname != NULL)
    126 		(void) strncpy(flist->flowname, flowname, MAXFLOWNAMELEN);
    127 	flist->linkid = linkid;
    128 	flist->fd = INT32_MAX;
    129 
    130 	return (flist);
    131 }
    132 
    133 /*ARGSUSED*/
    134 static void
    135 print_flow_stats(dladm_handle_t handle, struct flowlist *flist)
    136 {
    137 	struct flowlist *fcurr;
    138 	double ikbs, okbs;
    139 	double ipks, opks;
    140 	double dlt;
    141 	int fcount;
    142 	static boolean_t first = B_TRUE;
    143 
    144 	if (first) {
    145 		first = B_FALSE;
    146 		(void) printw("please wait...\n");
    147 		return;
    148 	}
    149 
    150 	for (fcount = 0, fcurr = flist;
    151 	    fcount <= statentry;
    152 	    fcount++, fcurr++) {
    153 		if (fcurr->flowname && fcurr->display) {
    154 			dlt = (double)fcurr->diffstats.snaptime/(double)NANOSEC;
    155 			ikbs = fcurr->diffstats.rbytes * 8 / dlt / 1024;
    156 			okbs = fcurr->diffstats.obytes * 8 / dlt / 1024;
    157 			ipks = fcurr->diffstats.ipackets / dlt;
    158 			opks = fcurr->diffstats.opackets / dlt;
    159 			(void) printw("%-15.15s", fcurr->flowname);
    160 			(void) printw("%-10.10s", fcurr->linkname);
    161 			(void) printw("%9.2f %9.2f %9.2f %9.2f ",
    162 			    ikbs, okbs, ipks, opks);
    163 			(void) printw("\n");
    164 		}
    165 	}
    166 }
    167 
    168 /*ARGSUSED*/
    169 static int
    170 flow_kstats(dladm_handle_t handle, dladm_flow_attr_t *attr, void *arg)
    171 {
    172 	kstat_ctl_t 	*kcp = (kstat_ctl_t *)arg;
    173 	kstat_t		*ksp;
    174 	struct flowlist	*flist;
    175 	pktsum_t	currstats, *prevstats, *diffstats;
    176 
    177 	flist = findstat(attr->fa_flowname, attr->fa_linkid);
    178 	if (flist == NULL)
    179 		return (DLADM_WALK_CONTINUE);
    180 
    181 	flist->display = B_FALSE;
    182 	prevstats = &flist->prevstats;
    183 	diffstats = &flist->diffstats;
    184 
    185 	(void) dladm_datalink_id2info(handle, attr->fa_linkid, NULL, NULL,
    186 	    NULL, flist->linkname, sizeof (flist->linkname));
    187 
    188 
    189 	/* lookup kstat entry */
    190 	ksp = dladm_kstat_lookup(kcp, NULL, -1, attr->fa_flowname, "flow");
    191 
    192 	if (ksp == NULL)
    193 		return (DLADM_WALK_CONTINUE);
    194 
    195 	/* read packet and byte stats */
    196 	dladm_get_stats(kcp, ksp, &currstats);
    197 
    198 	if (flist->ifspeed == 0)
    199 		(void) dladm_kstat_value(ksp, "ifspeed", KSTAT_DATA_UINT64,
    200 		    &flist->ifspeed);
    201 
    202 	if (flist->first) {
    203 		flist->first = B_FALSE;
    204 	} else {
    205 		dladm_stats_diff(diffstats, &currstats, prevstats);
    206 		if (diffstats->snaptime == 0)
    207 			return (DLADM_WALK_CONTINUE);
    208 		dladm_stats_total(&totalstats, diffstats, &totalstats);
    209 	}
    210 
    211 	bcopy(&currstats, prevstats, sizeof (pktsum_t));
    212 	flist->display = B_TRUE;
    213 
    214 	return (DLADM_WALK_CONTINUE);
    215 }
    216 
    217 /*ARGSUSED*/
    218 static void
    219 print_link_stats(dladm_handle_t handle, struct flowlist *flist)
    220 {
    221 	struct flowlist *fcurr;
    222 	double ikbs, okbs;
    223 	double ipks, opks;
    224 	double util;
    225 	double dlt;
    226 	int fcount;
    227 	static boolean_t first = B_TRUE;
    228 
    229 	if (first) {
    230 		first = B_FALSE;
    231 		(void) printw("please wait...\n");
    232 		return;
    233 	}
    234 
    235 	for (fcount = 0, fcurr = flist;
    236 	    fcount <= statentry;
    237 	    fcount++, fcurr++) {
    238 		if ((fcurr->linkid != DATALINK_INVALID_LINKID) &&
    239 		    fcurr->display)  {
    240 			dlt = (double)fcurr->diffstats.snaptime/(double)NANOSEC;
    241 			ikbs = (double)fcurr->diffstats.rbytes * 8 / dlt / 1024;
    242 			okbs = (double)fcurr->diffstats.obytes * 8 / dlt / 1024;
    243 			ipks = (double)fcurr->diffstats.ipackets / dlt;
    244 			opks = (double)fcurr->diffstats.opackets / dlt;
    245 			(void) printw("%-10.10s", fcurr->linkname);
    246 			(void) printw("%9.2f %9.2f %9.2f %9.2f ",
    247 			    ikbs, okbs, ipks, opks);
    248 			if (fcurr->ifspeed != 0)
    249 				util = ((ikbs + okbs) * 1024) *
    250 				    100/ fcurr->ifspeed;
    251 			else
    252 				util = (double)0;
    253 			(void) attron(A_BOLD);
    254 			(void) printw("    %6.2f", util);
    255 			(void) attroff(A_BOLD);
    256 			(void) printw("\n");
    257 		}
    258 	}
    259 }
    260 
    261 /*
    262  * This function is called through the dladm_walk_datalink_id() walker and
    263  * calls the dladm_walk_flow() walker.
    264  */
    265 
    266 /*ARGSUSED*/
    267 static int
    268 link_flowstats(dladm_handle_t handle, datalink_id_t linkid, void *arg)
    269 {
    270 	dladm_status_t	status;
    271 
    272 	status = dladm_walk_flow(flow_kstats, handle, linkid, arg, B_FALSE);
    273 	if (status == DLADM_STATUS_OK)
    274 		return (DLADM_WALK_CONTINUE);
    275 	else
    276 		return (DLADM_WALK_TERMINATE);
    277 }
    278 
    279 /*ARGSUSED*/
    280 static int
    281 link_kstats(dladm_handle_t handle, datalink_id_t linkid, void *arg)
    282 {
    283 	kstat_ctl_t		*kcp = (kstat_ctl_t *)arg;
    284 	struct flowlist		*flist;
    285 	pktsum_t		currstats, *prevstats, *diffstats;
    286 	datalink_class_t	class;
    287 	kstat_t			*ksp;
    288 	char			dnlink[MAXPATHLEN];
    289 
    290 	/* find the flist entry */
    291 	flist = findstat(NULL, linkid);
    292 	flist->display = B_FALSE;
    293 	if (flist != NULL) {
    294 		prevstats = &flist->prevstats;
    295 		diffstats = &flist->diffstats;
    296 	} else {
    297 		return (DLADM_WALK_CONTINUE);
    298 	}
    299 
    300 	if (dladm_datalink_id2info(handle, linkid, NULL, &class, NULL,
    301 	    flist->linkname, sizeof (flist->linkname)) != DLADM_STATUS_OK)
    302 		return (DLADM_WALK_CONTINUE);
    303 
    304 	if (flist->fd == INT32_MAX) {
    305 		if (class == DATALINK_CLASS_PHYS) {
    306 			(void) snprintf(dnlink, MAXPATHLEN, "/dev/net/%s",
    307 			    flist->linkname);
    308 			if ((flist->fd = open(dnlink, O_RDWR)) < 0)
    309 				return (DLADM_WALK_CONTINUE);
    310 		} else {
    311 			flist->fd = -1;
    312 		}
    313 		(void) kstat_chain_update(kcp);
    314 	}
    315 
    316 	/* lookup kstat entry */
    317 	ksp = dladm_kstat_lookup(kcp, NULL, -1, flist->linkname, "net");
    318 
    319 	if (ksp == NULL)
    320 		return (DLADM_WALK_CONTINUE);
    321 
    322 	/* read packet and byte stats */
    323 	dladm_get_stats(kcp, ksp, &currstats);
    324 
    325 	if (flist->ifspeed == 0)
    326 		(void) dladm_kstat_value(ksp, "ifspeed", KSTAT_DATA_UINT64,
    327 		    &flist->ifspeed);
    328 
    329 	if (flist->first) {
    330 		flist->first = B_FALSE;
    331 	} else {
    332 		dladm_stats_diff(diffstats, &currstats, prevstats);
    333 		if (diffstats->snaptime == 0)
    334 			return (DLADM_WALK_CONTINUE);
    335 	}
    336 
    337 	bcopy(&currstats, prevstats, sizeof (*prevstats));
    338 	flist->display = B_TRUE;
    339 
    340 	return (DLADM_WALK_CONTINUE);
    341 }
    342 
    343 static void
    344 closedevnet()
    345 {
    346 	int index = 0;
    347 	struct flowlist *flist;
    348 
    349 	/* Close all open /dev/net/ files */
    350 	for (flist = stattable; index <= maxstatentries; index++, flist++) {
    351 		if (flist->linkid == DATALINK_INVALID_LINKID)
    352 			break;
    353 		if (flist->fd != -1 && flist->fd != INT32_MAX)
    354 			(void) close(flist->fd);
    355 	}
    356 }
    357 
    358 /*ARGSUSED*/
    359 static void
    360 sig_break(int s)
    361 {
    362 	handle_break = 1;
    363 }
    364 
    365 /*ARGSUSED*/
    366 static void
    367 sig_resize(int s)
    368 {
    369 	handle_resize = 1;
    370 }
    371 
    372 static void
    373 curses_init()
    374 {
    375 	maxx = maxx;	/* lint */
    376 	maxy = maxy;	/* lint */
    377 
    378 	/* Install signal handlers */
    379 	(void) signal(SIGINT,  sig_break);
    380 	(void) signal(SIGQUIT, sig_break);
    381 	(void) signal(SIGTERM, sig_break);
    382 	(void) signal(SIGWINCH, sig_resize);
    383 
    384 	/* Initialize ncurses */
    385 	(void) initscr();
    386 	(void) cbreak();
    387 	(void) noecho();
    388 	(void) curs_set(0);
    389 	timeout(0);
    390 	getmaxyx(stdscr, maxy, maxx);
    391 }
    392 
    393 static void
    394 curses_fin()
    395 {
    396 	(void) printw("\n");
    397 	(void) curs_set(1);
    398 	(void) nocbreak();
    399 	(void) endwin();
    400 
    401 	free(stattable);
    402 }
    403 
    404 static void
    405 stat_report(dladm_handle_t handle, kstat_ctl_t *kcp,  datalink_id_t linkid,
    406     const char *flowname, int opt)
    407 {
    408 
    409 	double dlt, ikbs, okbs, ipks, opks;
    410 
    411 	struct flowlist *fstable = stattable;
    412 
    413 	if ((opt != LINK_REPORT) && (opt != FLOW_REPORT))
    414 		return;
    415 
    416 	/* Handle window resizes */
    417 	if (handle_resize) {
    418 		(void) endwin();
    419 		(void) initscr();
    420 		(void) cbreak();
    421 		(void) noecho();
    422 		(void) curs_set(0);
    423 		timeout(0);
    424 		getmaxyx(stdscr, maxy, maxx);
    425 		redraw = 1;
    426 		handle_resize = 0;
    427 	}
    428 
    429 	/* Print title */
    430 	(void) erase();
    431 	(void) attron(A_BOLD);
    432 	(void) move(0, 0);
    433 	if (opt == FLOW_REPORT)
    434 		(void) printw("%-15.15s", "Flow");
    435 	(void) printw("%-10.10s", "Link");
    436 	(void) printw("%9.9s %9.9s %9.9s %9.9s ",
    437 	    "iKb/s", "oKb/s", "iPk/s", "oPk/s");
    438 	if (opt == LINK_REPORT)
    439 		(void) printw("    %6.6s", "%Util");
    440 	(void) printw("\n");
    441 	(void) attroff(A_BOLD);
    442 
    443 	(void) move(2, 0);
    444 
    445 	/* Print stats for each link or flow */
    446 	bzero(&totalstats, sizeof (totalstats));
    447 	if (opt == LINK_REPORT) {
    448 		/* Display all links */
    449 		if (linkid == DATALINK_ALL_LINKID) {
    450 			(void) dladm_walk_datalink_id(link_kstats, handle,
    451 			    (void *)kcp, DATALINK_CLASS_ALL,
    452 			    DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE);
    453 		/* Display 1 link */
    454 		} else {
    455 			(void) link_kstats(handle, linkid, kcp);
    456 		}
    457 		print_link_stats(handle, fstable);
    458 
    459 	} else if (opt == FLOW_REPORT) {
    460 		/* Display 1 flow */
    461 		if (flowname != NULL) {
    462 			dladm_flow_attr_t fattr;
    463 			if (dladm_flow_info(handle, flowname, &fattr) !=
    464 			    DLADM_STATUS_OK)
    465 				return;
    466 			(void) flow_kstats(handle, &fattr, kcp);
    467 		/* Display all flows on all links */
    468 		} else if (linkid == DATALINK_ALL_LINKID) {
    469 			(void) dladm_walk_datalink_id(link_flowstats, handle,
    470 			    (void *)kcp, DATALINK_CLASS_ALL,
    471 			    DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE);
    472 		/* Display all flows on a link */
    473 		} else if (linkid != DATALINK_INVALID_LINKID) {
    474 			(void) dladm_walk_flow(flow_kstats, handle, linkid, kcp,
    475 			    B_FALSE);
    476 		}
    477 		print_flow_stats(handle, fstable);
    478 
    479 		/* Print totals */
    480 		(void) attron(A_BOLD);
    481 		dlt = (double)totalstats.snaptime / (double)NANOSEC;
    482 		ikbs = totalstats.rbytes / dlt / 1024;
    483 		okbs = totalstats.obytes / dlt / 1024;
    484 		ipks = totalstats.ipackets / dlt;
    485 		opks = totalstats.opackets / dlt;
    486 		(void) printw("\n%-25.25s", "Totals");
    487 		(void) printw("%9.2f %9.2f %9.2f %9.2f ",
    488 		    ikbs, okbs, ipks, opks);
    489 		(void) attroff(A_BOLD);
    490 	}
    491 
    492 	if (redraw)
    493 		(void) clearok(stdscr, 1);
    494 
    495 	if (refresh() == ERR)
    496 		return;
    497 
    498 	if (redraw) {
    499 		(void) clearok(stdscr, 0);
    500 		redraw = 0;
    501 	}
    502 }
    503 
    504 /* Exported functions */
    505 
    506 /*
    507  * Continuously display link or flow statstics using a libcurses
    508  * based display.
    509  */
    510 
    511 void
    512 dladm_continuous(dladm_handle_t handle, datalink_id_t linkid,
    513     const char *flowname, int interval, int opt)
    514 {
    515 	kstat_ctl_t *kcp;
    516 
    517 	if ((kcp = kstat_open()) == NULL) {
    518 		warn("kstat open operation failed");
    519 		return;
    520 	}
    521 
    522 	curses_init();
    523 
    524 	for (;;) {
    525 
    526 		if (handle_break)
    527 			break;
    528 
    529 		stat_report(handle, kcp, linkid, flowname, opt);
    530 
    531 		(void) sleep(max(1, interval));
    532 	}
    533 
    534 	closedevnet();
    535 	curses_fin();
    536 	(void) kstat_close(kcp);
    537 }
    538 
    539 /*
    540  * dladm_kstat_lookup() is a modified version of kstat_lookup which
    541  * adds the class as a selector.
    542  */
    543 
    544 kstat_t *
    545 dladm_kstat_lookup(kstat_ctl_t *kcp, const char *module, int instance,
    546     const char *name, const char *class)
    547 {
    548 	kstat_t *ksp = NULL;
    549 
    550 	for (ksp = kcp->kc_chain; ksp != NULL; ksp = ksp->ks_next) {
    551 		if ((module == NULL || strcmp(ksp->ks_module, module) == 0) &&
    552 		    (instance == -1 || ksp->ks_instance == instance) &&
    553 		    (name == NULL || strcmp(ksp->ks_name, name) == 0) &&
    554 		    (class == NULL || strcmp(ksp->ks_class, class) == 0))
    555 			return (ksp);
    556 	}
    557 
    558 	errno = ENOENT;
    559 	return (NULL);
    560 }
    561 
    562 /*
    563  * dladm_get_stats() populates the supplied pktsum_t structure with
    564  * the input and output  packet and byte kstats from the kstat_t
    565  * found with dladm_kstat_lookup.
    566  */
    567 void
    568 dladm_get_stats(kstat_ctl_t *kcp, kstat_t *ksp, pktsum_t *stats)
    569 {
    570 
    571 	if (kstat_read(kcp, ksp, NULL) == -1)
    572 		return;
    573 
    574 	stats->snaptime = gethrtime();
    575 
    576 	if (dladm_kstat_value(ksp, "ipackets64", KSTAT_DATA_UINT64,
    577 	    &stats->ipackets) < 0) {
    578 		if (dladm_kstat_value(ksp, "ipackets", KSTAT_DATA_UINT64,
    579 		    &stats->ipackets) < 0)
    580 			return;
    581 	}
    582 
    583 	if (dladm_kstat_value(ksp, "opackets64", KSTAT_DATA_UINT64,
    584 	    &stats->opackets) < 0) {
    585 		if (dladm_kstat_value(ksp, "opackets", KSTAT_DATA_UINT64,
    586 		    &stats->opackets) < 0)
    587 			return;
    588 	}
    589 
    590 	if (dladm_kstat_value(ksp, "rbytes64", KSTAT_DATA_UINT64,
    591 	    &stats->rbytes) < 0) {
    592 		if (dladm_kstat_value(ksp, "rbytes", KSTAT_DATA_UINT64,
    593 		    &stats->rbytes) < 0)
    594 			return;
    595 	}
    596 
    597 	if (dladm_kstat_value(ksp, "obytes64", KSTAT_DATA_UINT64,
    598 	    &stats->obytes) < 0) {
    599 		if (dladm_kstat_value(ksp, "obytes", KSTAT_DATA_UINT64,
    600 		    &stats->obytes) < 0)
    601 			return;
    602 	}
    603 
    604 	if (dladm_kstat_value(ksp, "ierrors", KSTAT_DATA_UINT32,
    605 	    &stats->ierrors) < 0) {
    606 		if (dladm_kstat_value(ksp, "ierrors", KSTAT_DATA_UINT64,
    607 		    &stats->ierrors) < 0)
    608 		return;
    609 	}
    610 
    611 	if (dladm_kstat_value(ksp, "oerrors", KSTAT_DATA_UINT32,
    612 	    &stats->oerrors) < 0) {
    613 		if (dladm_kstat_value(ksp, "oerrors", KSTAT_DATA_UINT64,
    614 		    &stats->oerrors) < 0)
    615 			return;
    616 	}
    617 }
    618 
    619 int
    620 dladm_kstat_value(kstat_t *ksp, const char *name, uint8_t type, void *buf)
    621 {
    622 	kstat_named_t	*knp;
    623 
    624 	if ((knp = kstat_data_lookup(ksp, (char *)name)) == NULL)
    625 		return (-1);
    626 
    627 	if (knp->data_type != type)
    628 		return (-1);
    629 
    630 	switch (type) {
    631 	case KSTAT_DATA_UINT64:
    632 		*(uint64_t *)buf = knp->value.ui64;
    633 		break;
    634 	case KSTAT_DATA_UINT32:
    635 		*(uint32_t *)buf = knp->value.ui32;
    636 		break;
    637 	default:
    638 		return (-1);
    639 	}
    640 
    641 	return (0);
    642 }
    643 
    644 dladm_status_t
    645 dladm_get_single_mac_stat(dladm_handle_t handle, datalink_id_t linkid,
    646     const char *name, uint8_t type, void *val)
    647 {
    648 	kstat_ctl_t	*kcp;
    649 	char		module[DLPI_LINKNAME_MAX];
    650 	uint_t		instance;
    651 	char 		link[DLPI_LINKNAME_MAX];
    652 	dladm_status_t	status;
    653 	uint32_t	flags, media;
    654 	kstat_t		*ksp;
    655 	dladm_phys_attr_t dpap;
    656 
    657 	if ((status = dladm_datalink_id2info(handle, linkid, &flags, NULL,
    658 	    &media, link, DLPI_LINKNAME_MAX)) != DLADM_STATUS_OK)
    659 		return (status);
    660 
    661 	if (media != DL_ETHER)
    662 		return (DLADM_STATUS_LINKINVAL);
    663 
    664 	status = dladm_phys_info(handle, linkid, &dpap, DLADM_OPT_PERSIST);
    665 
    666 	if (status != DLADM_STATUS_OK)
    667 		return (status);
    668 
    669 	status = dladm_parselink(dpap.dp_dev, module, &instance);
    670 
    671 	if (status != DLADM_STATUS_OK)
    672 		return (status);
    673 
    674 	if ((kcp = kstat_open()) == NULL) {
    675 		warn("kstat_open operation failed");
    676 		return (-1);
    677 	}
    678 
    679 	/*
    680 	 * The kstat query could fail if the underlying MAC
    681 	 * driver was already detached.
    682 	 */
    683 	if ((ksp = kstat_lookup(kcp, module, instance, "mac")) == NULL &&
    684 	    (ksp = kstat_lookup(kcp, module, instance, NULL)) == NULL)
    685 		goto bail;
    686 
    687 	if (kstat_read(kcp, ksp, NULL) == -1)
    688 		goto bail;
    689 
    690 	if (dladm_kstat_value(ksp, name, type, val) < 0)
    691 		goto bail;
    692 
    693 	(void) kstat_close(kcp);
    694 	return (DLADM_STATUS_OK);
    695 
    696 bail:
    697 	(void) kstat_close(kcp);
    698 	return (dladm_errno2status(errno));
    699 }
    700 
    701 /* Compute sum of 2 pktsums (s1 = s2 + s3) */
    702 void
    703 dladm_stats_total(pktsum_t *s1, pktsum_t *s2, pktsum_t *s3)
    704 {
    705 	s1->rbytes    = s2->rbytes    + s3->rbytes;
    706 	s1->ipackets  = s2->ipackets  + s3->ipackets;
    707 	s1->ierrors   = s2->ierrors   + s3->ierrors;
    708 	s1->obytes    = s2->obytes    + s3->obytes;
    709 	s1->opackets  = s2->opackets  + s3->opackets;
    710 	s1->oerrors   = s2->oerrors   + s3->oerrors;
    711 	s1->snaptime  = s2->snaptime;
    712 }
    713 
    714 #define	DIFF_STAT(s2, s3) ((s2) > (s3) ? (s2 - s3) : 0)
    715 
    716 
    717 /* Compute differences between 2 pktsums (s1 = s2 - s3) */
    718 void
    719 dladm_stats_diff(pktsum_t *s1, pktsum_t *s2, pktsum_t *s3)
    720 {
    721 	s1->rbytes    = DIFF_STAT(s2->rbytes,   s3->rbytes);
    722 	s1->ipackets  = DIFF_STAT(s2->ipackets, s3->ipackets);
    723 	s1->ierrors   = DIFF_STAT(s2->ierrors,  s3->ierrors);
    724 	s1->obytes    = DIFF_STAT(s2->obytes,   s3->obytes);
    725 	s1->opackets  = DIFF_STAT(s2->opackets, s3->opackets);
    726 	s1->oerrors   = DIFF_STAT(s2->oerrors,  s3->oerrors);
    727 	s1->snaptime  = DIFF_STAT(s2->snaptime, s3->snaptime);
    728 }
    729