xref: /illumos-gate/usr/src/lib/libdladm/common/usage.c (revision 82a2fc47)
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 /*
23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include <fcntl.h>
28 #include <stdlib.h>
29 #include <strings.h>
30 #include <exacct.h>
31 #include <net/if.h>
32 #include <libdladm.h>
33 
34 #define	TIMEBUFLEN	20
35 #define	GBIT		1000000000
36 #define	MBIT		1000000
37 #define	KBIT		1000
38 
39 #define	NET_RESET_TOT(tbytes, ttime, tibytes, tobytes, step) {	\
40 	(step) = 1;						\
41 	(tbytes) = 0;						\
42 	(ttime) = 0;						\
43 	(tibytes) = 0;						\
44 	(tobytes) = 0;						\
45 	}
46 
47 /* Flow/Link Descriptor */
48 typedef struct net_desc_s {
49 	char		net_desc_name[LIFNAMSIZ];
50 	char		net_desc_devname[LIFNAMSIZ];
51 	uchar_t		net_desc_ehost[ETHERADDRL];
52 	uchar_t		net_desc_edest[ETHERADDRL];
53 	ushort_t	net_desc_vlan_tpid;
54 	ushort_t	net_desc_vlan_tci;
55 	ushort_t	net_desc_sap;
56 	ushort_t	net_desc_cpuid;
57 	ushort_t	net_desc_priority;
58 	uint64_t	net_desc_bw_limit;
59 	in6_addr_t	net_desc_saddr;
60 	in6_addr_t	net_desc_daddr;
61 	boolean_t	net_desc_isv4;
62 	in_port_t	net_desc_sport;
63 	in_port_t	net_desc_dport;
64 	uint8_t		net_desc_protocol;
65 	uint8_t		net_desc_dsfield;
66 	boolean_t	net_desc_newrec;
67 } net_desc_t;
68 
69 /* Time structure: Year, Month, Day, Hour, Min, Sec */
70 typedef struct net_time_s {
71 	int	net_time_yr;
72 	int	net_time_mon;
73 	int	net_time_day;
74 	int	net_time_hr;
75 	int	net_time_min;
76 	int	net_time_sec;
77 } net_time_t;
78 
79 /* Flow/Link Stats */
80 typedef struct net_stat_s {
81 	char			net_stat_name[LIFNAMSIZ];
82 	uint64_t		net_stat_ibytes;
83 	uint64_t		net_stat_obytes;
84 	uint64_t		net_stat_ipackets;
85 	uint64_t		net_stat_opackets;
86 	uint64_t		net_stat_ierrors;
87 	uint64_t		net_stat_oerrors;
88 	uint64_t		net_stat_tibytes;
89 	uint64_t		net_stat_tobytes;
90 	uint64_t		net_stat_tipackets;
91 	uint64_t		net_stat_topackets;
92 	uint64_t		net_stat_tierrors;
93 	uint64_t		net_stat_toerrors;
94 	uint64_t		net_stat_ctime;
95 	uint64_t		net_stat_tdiff;
96 	net_time_t		net_stat_time;
97 	struct net_stat_s	*net_stat_next;
98 	net_desc_t		*net_stat_desc;
99 	boolean_t		net_stat_isref;
100 } net_stat_t;
101 
102 /* Used to create the [gnu]plot file */
103 typedef struct net_plot_entry_s {
104 	char		*net_pe_name;
105 	uint64_t	net_pe_tottime;
106 	uint64_t	net_pe_totbytes;
107 	uint64_t	net_pe_totibytes;
108 	uint64_t	net_pe_totobytes;
109 	uint64_t	net_pe_lasttime;
110 } net_plot_entry_t;
111 
112 /* Stats entry */
113 typedef struct net_entry_s {
114 	net_desc_t		*net_entry_desc;
115 	net_stat_t		*net_entry_shead;
116 	net_stat_t		*net_entry_stail;
117 	int			net_entry_scount;
118 	net_stat_t		*net_entry_sref;
119 	net_stat_t		*net_entry_tstats;
120 	uint64_t		net_entry_ttime;
121 	struct net_entry_s	*net_entry_next;
122 } net_entry_t;
123 
124 /* Time sorted list */
125 typedef struct net_time_entry_s {
126 	net_stat_t	*my_time_stat;
127 	struct net_time_entry_s *net_time_entry_next;
128 	struct net_time_entry_s *net_time_entry_prev;
129 } net_time_entry_t;
130 
131 /* The parsed table */
132 typedef	struct net_table_s {
133 	/* List of stats */
134 	net_entry_t		*net_table_head;
135 	net_entry_t		*net_table_tail;
136 	int			net_entries;
137 
138 	/*
139 	 * Optimization I : List sorted by time, i.e:
140 	 * Time		Resource	..
141 	 * -------------------------------
142 	 * 11.15.10	bge0
143 	 * 11.15.10	ce0
144 	 * 11.15.10	vnic1
145 	 * 11.15.15	bge0
146 	 * 11.15.15	ce0
147 	 * 11.15.15	vnic1
148 	 */
149 	net_time_entry_t	*net_time_head;
150 	net_time_entry_t	*net_time_tail;
151 
152 	/*
153 	 * Optimization II : List sorted by resources
154 	 * Time		Resource	..
155 	 * -------------------------------
156 	 * 11.15.10	bge0
157 	 * 11.15.15	bge0
158 	 * 11.15.10	ce0
159 	 * 11.15.15	ce0
160 	 * 11.15.10	vnic1
161 	 * 11.15.15	vnic1
162 	 */
163 	net_time_entry_t	*net_ctime_head;
164 	net_time_entry_t	*net_ctime_tail;
165 
166 	/* Common to both the above (sorted) lists. */
167 	int			net_time_entries;
168 } net_table_t;
169 
170 #define	NET_DATE_GREATER	0
171 #define	NET_DATE_LESSER		1
172 #define	NET_DATE_EQUAL		2
173 
174 #define	NET_TIME_GREATER	0
175 #define	NET_TIME_LESSER		1
176 #define	NET_TIME_EQUAL		2
177 
178 #ifndef _LP64
179 #define	FMT_UINT64	"%-15llu"
180 #else
181 #define	FMT_UINT64	"%-15lu"
182 #endif
183 
184 /*
185  * Given a timebuf of the form M/D/Y,H:M:S break it into individual elements.
186  */
187 static void
188 dissect_time(char *tbuf, net_time_t *nt)
189 {
190 	char	*d;
191 	char	*t;
192 	char	*dd;
193 	char	*h;
194 	char	*endp;
195 
196 	if (tbuf == NULL || nt == NULL)
197 		return;
198 
199 	d = strtok(tbuf, ",");	/* Date */
200 	t = strtok(NULL, ",");	/* Time */
201 
202 	/* Month */
203 	dd = strtok(d, "/");
204 	if (dd == NULL)
205 		return;
206 	nt->net_time_mon = strtol(dd, &endp, 10);
207 
208 	/* Day */
209 	dd = strtok(NULL, "/");
210 	if (dd == NULL)
211 		return;
212 	nt->net_time_day = strtol(dd, &endp, 10);
213 
214 	/* Year */
215 	dd = strtok(NULL, "/");
216 	if (dd == NULL)
217 		return;
218 	nt->net_time_yr = strtol(dd, &endp, 10);
219 	if (strlen(dd) <= 2)
220 		nt->net_time_yr += 2000;
221 
222 	if (t == NULL)
223 		return;
224 
225 	/* Hour */
226 	h = strtok(t, ":");
227 	if (h == NULL)
228 		return;
229 	nt->net_time_hr = strtol(h, &endp, 10);
230 
231 	/* Min */
232 	h = strtok(NULL, ":");
233 	if (h == NULL)
234 		return;
235 	nt->net_time_min = strtol(h, &endp, 10);
236 
237 	/* Sec */
238 	h = strtok(NULL, ":");
239 	if (h == NULL)
240 		return;
241 	nt->net_time_sec = strtol(h, &endp, 10);
242 }
243 
244 /* Get a stat item from an object in the exacct file */
245 static void
246 add_stat_item(ea_object_t *o, net_stat_t *ns)
247 {
248 	switch (o->eo_catalog & EXT_TYPE_MASK) {
249 	case EXT_STRING:
250 		if ((o->eo_catalog & EXD_DATA_MASK) == EXD_NET_STATS_NAME) {
251 			(void) strncpy(ns->net_stat_name, o->eo_item.ei_string,
252 			    strlen(o->eo_item.ei_string));
253 		}
254 		break;
255 	case EXT_UINT64:
256 		if ((o->eo_catalog & EXD_DATA_MASK) == EXD_NET_STATS_CURTIME) {
257 			time_t	_time;
258 			char	timebuf[TIMEBUFLEN];
259 
260 			ns->net_stat_ctime = o->eo_item.ei_uint64;
261 			_time = ns->net_stat_ctime;
262 			(void) strftime(timebuf, sizeof (timebuf),
263 			    "%m/%d/%Y,%T\n", localtime(&_time));
264 			dissect_time(timebuf, &ns->net_stat_time);
265 		} else if ((o->eo_catalog & EXD_DATA_MASK) ==
266 		    EXD_NET_STATS_IBYTES) {
267 			ns->net_stat_ibytes = o->eo_item.ei_uint64;
268 		} else if ((o->eo_catalog & EXD_DATA_MASK) ==
269 		    EXD_NET_STATS_OBYTES) {
270 			ns->net_stat_obytes = o->eo_item.ei_uint64;
271 		} else if ((o->eo_catalog & EXD_DATA_MASK) ==
272 		    EXD_NET_STATS_IPKTS) {
273 			ns->net_stat_ipackets = o->eo_item.ei_uint64;
274 		} else if ((o->eo_catalog & EXD_DATA_MASK) ==
275 		    EXD_NET_STATS_OPKTS) {
276 			ns->net_stat_opackets = o->eo_item.ei_uint64;
277 		} else if ((o->eo_catalog & EXD_DATA_MASK) ==
278 		    EXD_NET_STATS_IERRPKTS) {
279 			ns->net_stat_ierrors = o->eo_item.ei_uint64;
280 		} else if ((o->eo_catalog & EXD_DATA_MASK) ==
281 		    EXD_NET_STATS_OERRPKTS) {
282 			ns->net_stat_oerrors = o->eo_item.ei_uint64;
283 		}
284 		break;
285 	default:
286 		break;
287 	}
288 }
289 
290 /* Get a description item from an object in the exacct file */
291 static void
292 add_desc_item(ea_object_t *o, net_desc_t *nd)
293 {
294 	switch (o->eo_catalog & EXT_TYPE_MASK) {
295 	case EXT_STRING:
296 		if ((o->eo_catalog & EXD_DATA_MASK) == EXD_NET_DESC_NAME) {
297 			(void) strncpy(nd->net_desc_name, o->eo_item.ei_string,
298 			    strlen(o->eo_item.ei_string));
299 		} else if ((o->eo_catalog & EXD_DATA_MASK) ==
300 		    EXD_NET_DESC_DEVNAME) {
301 			(void) strncpy(nd->net_desc_devname,
302 			    o->eo_item.ei_string, strlen(o->eo_item.ei_string));
303 		}
304 		break;
305 	case EXT_UINT8:
306 		if ((o->eo_catalog & EXD_DATA_MASK) == EXD_NET_DESC_PROTOCOL) {
307 			nd->net_desc_protocol = o->eo_item.ei_uint8;
308 		} else if ((o->eo_catalog & EXD_DATA_MASK) ==
309 		    EXD_NET_DESC_DSFIELD) {
310 			nd->net_desc_dsfield = o->eo_item.ei_uint8;
311 		}
312 		break;
313 	case EXT_UINT16:
314 		if ((o->eo_catalog & EXD_DATA_MASK) == EXD_NET_DESC_SPORT) {
315 			nd->net_desc_sport = o->eo_item.ei_uint16;
316 		} else if ((o->eo_catalog & EXD_DATA_MASK) ==
317 		    EXD_NET_DESC_DPORT) {
318 			nd->net_desc_dport = o->eo_item.ei_uint16;
319 		} else if ((o->eo_catalog & EXD_DATA_MASK) ==
320 		    EXD_NET_DESC_SAP) {
321 			nd->net_desc_sap = o->eo_item.ei_uint16;
322 		} else if ((o->eo_catalog & EXD_DATA_MASK) ==
323 		    EXD_NET_DESC_VLAN_TPID) {
324 			nd->net_desc_vlan_tpid = o->eo_item.ei_uint16;
325 		} else if ((o->eo_catalog & EXD_DATA_MASK) ==
326 		    EXD_NET_DESC_VLAN_TCI) {
327 			nd->net_desc_vlan_tci = o->eo_item.ei_uint16;
328 		} else if ((o->eo_catalog & EXD_DATA_MASK) ==
329 		    EXD_NET_DESC_PRIORITY) {
330 			nd->net_desc_priority = o->eo_item.ei_uint16;
331 		}
332 		break;
333 	case EXT_UINT32:
334 		if ((o->eo_catalog & EXD_DATA_MASK) == EXD_NET_DESC_V4SADDR ||
335 		    (o->eo_catalog & EXD_DATA_MASK) == EXD_NET_DESC_V4DADDR) {
336 				struct in_addr	addr;
337 
338 				addr.s_addr = htonl(o->eo_item.ei_uint32);
339 
340 				if ((o->eo_catalog & EXD_DATA_MASK) ==
341 				    EXD_NET_DESC_V4SADDR) {
342 					IN6_INADDR_TO_V4MAPPED(&addr,
343 					    &nd->net_desc_saddr);
344 				} else {
345 					IN6_INADDR_TO_V4MAPPED(&addr,
346 					    &nd->net_desc_daddr);
347 				}
348 		}
349 		break;
350 	case EXT_UINT64:
351 		if ((o->eo_catalog & EXD_DATA_MASK) == EXD_NET_DESC_BWLIMIT)
352 			nd->net_desc_bw_limit = o->eo_item.ei_uint64;
353 		break;
354 	case EXT_RAW:
355 		if ((o->eo_catalog & EXD_DATA_MASK) == EXD_NET_DESC_V6SADDR ||
356 		    (o->eo_catalog & EXD_DATA_MASK) == EXD_NET_DESC_V6DADDR) {
357 			in6_addr_t	addr;
358 
359 			addr = *(in6_addr_t *)o->eo_item.ei_raw;
360 			if ((o->eo_catalog & EXD_DATA_MASK) ==
361 			    EXD_NET_DESC_V6SADDR) {
362 				nd->net_desc_saddr = addr;
363 			} else {
364 				nd->net_desc_daddr = addr;
365 			}
366 		} else if ((o->eo_catalog & EXD_DATA_MASK) ==
367 		    EXD_NET_DESC_EHOST) {
368 			bcopy((uchar_t *)o->eo_item.ei_raw, nd->net_desc_ehost,
369 			    ETHERADDRL);
370 		} else if ((o->eo_catalog & EXD_DATA_MASK) ==
371 		    EXD_NET_DESC_EDEST) {
372 			bcopy((uchar_t *)o->eo_item.ei_raw, nd->net_desc_edest,
373 			    ETHERADDRL);
374 		}
375 		break;
376 	default:
377 		break;
378 	}
379 }
380 
381 /* Add a description item to the table */
382 static dladm_status_t
383 add_desc_to_tbl(net_table_t *net_table, net_desc_t *nd)
384 {
385 	net_entry_t	*ne;
386 
387 	if ((ne = calloc(1, sizeof (net_entry_t))) == NULL)
388 		return (DLADM_STATUS_NOMEM);
389 
390 	if ((ne->net_entry_tstats = calloc(1, sizeof (net_stat_t))) == NULL) {
391 		free(ne);
392 		return (DLADM_STATUS_NOMEM);
393 	}
394 
395 	ne->net_entry_desc = nd;
396 	ne->net_entry_shead = NULL;
397 	ne->net_entry_stail = NULL;
398 	ne->net_entry_scount = 0;
399 
400 	if (net_table->net_table_head == NULL) {
401 		net_table->net_table_head = ne;
402 		net_table->net_table_tail = ne;
403 	} else {
404 		net_table->net_table_tail->net_entry_next = ne;
405 		net_table->net_table_tail = ne;
406 	}
407 	net_table->net_entries++;
408 	return (DLADM_STATUS_OK);
409 }
410 
411 /* Compare dates and return if t1 is equal, greater or lesser than t2 */
412 static int
413 compare_date(net_time_t *t1, net_time_t *t2)
414 {
415 	if (t1->net_time_yr == t2->net_time_yr &&
416 	    t1->net_time_mon == t2->net_time_mon &&
417 	    t1->net_time_day == t2->net_time_day) {
418 		return (NET_DATE_EQUAL);
419 	}
420 	if (t1->net_time_yr > t2->net_time_yr ||
421 	    (t1->net_time_yr == t2->net_time_yr &&
422 	    t1->net_time_mon > t2->net_time_mon) ||
423 	    (t1->net_time_yr == t2->net_time_yr &&
424 	    t1->net_time_mon == t2->net_time_mon &&
425 	    t1->net_time_day > t2->net_time_day)) {
426 		return (NET_DATE_GREATER);
427 	}
428 	return (NET_DATE_LESSER);
429 }
430 
431 /* Compare times and return if t1 is equal, greater or lesser than t2 */
432 static int
433 compare_time(net_time_t *t1, net_time_t *t2)
434 {
435 	int	cd;
436 
437 	cd = compare_date(t1, t2);
438 
439 	if (cd == NET_DATE_GREATER) {
440 		return (NET_TIME_GREATER);
441 	} else if (cd == NET_DATE_LESSER) {
442 		return (NET_TIME_LESSER);
443 	} else {
444 		if (t1->net_time_hr == t2->net_time_hr &&
445 		    t1->net_time_min == t2->net_time_min &&
446 		    t1->net_time_sec == t2->net_time_sec) {
447 			return (NET_TIME_EQUAL);
448 		}
449 		if (t1->net_time_hr > t2->net_time_hr ||
450 		    (t1->net_time_hr == t2->net_time_hr &&
451 		    t1->net_time_min > t2->net_time_min) ||
452 		    (t1->net_time_hr == t2->net_time_hr &&
453 		    t1->net_time_min == t2->net_time_min &&
454 		    t1->net_time_sec > t2->net_time_sec)) {
455 			return (NET_TIME_GREATER);
456 		}
457 	}
458 	return (NET_TIME_LESSER);
459 }
460 
461 /*
462  * Given a start and end time and start and end entries check if the
463  * times are within the range, and adjust, if needed.
464  */
465 static dladm_status_t
466 chk_time_bound(net_time_t *s, net_time_t *e,  net_time_t *sns,
467     net_time_t *ens)
468 {
469 	if (s != NULL && e != NULL) {
470 		if (compare_time(s, e) == NET_TIME_GREATER)
471 			return (DLADM_STATUS_BADTIMEVAL);
472 	}
473 	if (s != NULL) {
474 		if (compare_time(s, sns) == NET_TIME_LESSER) {
475 			s->net_time_yr = sns->net_time_yr;
476 			s->net_time_mon = sns->net_time_mon;
477 			s->net_time_day = sns->net_time_day;
478 			s->net_time_hr = sns->net_time_hr;
479 			s->net_time_min = sns->net_time_min;
480 			s->net_time_sec = sns->net_time_sec;
481 		}
482 	}
483 	if (e != NULL) {
484 		if (compare_time(e, ens) == NET_TIME_GREATER) {
485 			e->net_time_yr = ens->net_time_yr;
486 			e->net_time_mon = ens->net_time_mon;
487 			e->net_time_day = ens->net_time_day;
488 			e->net_time_hr = ens->net_time_hr;
489 			e->net_time_min = ens->net_time_min;
490 			e->net_time_sec = ens->net_time_sec;
491 		}
492 	}
493 	return (DLADM_STATUS_OK);
494 }
495 
496 /*
497  * Given a start and end time (strings), convert them into net_time_t
498  * and also check for the range given the head and tail of the list.
499  * If stime is lower then head or etime is greated than tail, adjust.
500  */
501 static dladm_status_t
502 get_time_range(net_time_entry_t *head, net_time_entry_t *tail,
503     net_time_t *st, net_time_t *et, char *stime, char *etime)
504 {
505 	bzero(st, sizeof (net_time_t));
506 	bzero(et, sizeof (net_time_t));
507 
508 	if (stime == NULL && etime == NULL)
509 		return (0);
510 
511 	if (stime != NULL)
512 		dissect_time(stime, st);
513 	if (etime != NULL)
514 		dissect_time(etime, et);
515 
516 	if (stime != NULL || etime != NULL) {
517 		return (chk_time_bound(stime == NULL ? NULL : st,
518 		    etime == NULL ? NULL : et,
519 		    &head->my_time_stat->net_stat_time,
520 		    &tail->my_time_stat->net_stat_time));
521 	}
522 	return (0);
523 }
524 
525 /*
526  * Walk the list from a given starting point and return when we find
527  * an entry that is greater or equal to st. lasttime will point to the
528  * previous time entry.
529  */
530 static void
531 get_starting_point(net_time_entry_t *head, net_time_entry_t **start,
532     net_time_t *st, char *stime, uint64_t *lasttime)
533 {
534 	net_time_entry_t	*next = head;
535 
536 	if (head == NULL) {
537 		*start = NULL;
538 		return;
539 	}
540 	if (stime == NULL) {
541 		*start = head;
542 		*lasttime = head->my_time_stat->net_stat_ctime;
543 		return;
544 	}
545 	*start = NULL;
546 	while (next != NULL) {
547 		if (compare_time(st,
548 		    &next->my_time_stat->net_stat_time) != NET_TIME_LESSER) {
549 			*lasttime = next->my_time_stat->net_stat_ctime;
550 			next = next->net_time_entry_next;
551 			continue;
552 		}
553 		*start = next;
554 		break;
555 	}
556 }
557 
558 /*
559  * Point entry (pe) functions
560  */
561 /* Clear all the counters. Done after the contents are written to the file */
562 static void
563 clear_pe(net_plot_entry_t *pe, int entries, int *pentries)
564 {
565 	int	count;
566 
567 	for (count = 0; count < entries; count++) {
568 		pe[count].net_pe_totbytes = 0;
569 		pe[count].net_pe_totibytes = 0;
570 		pe[count].net_pe_totobytes = 0;
571 		pe[count].net_pe_tottime = 0;
572 	}
573 	*pentries = 0;
574 }
575 
576 /* Update an entry in the point entry table */
577 static void
578 update_pe(net_plot_entry_t *pe, net_stat_t *nns, int nentries,
579     int *pentries, uint64_t lasttime)
580 {
581 	int	count;
582 
583 	for (count = 0; count < nentries; count++) {
584 		if (strcmp(pe[count].net_pe_name, nns->net_stat_name) == 0)
585 			break;
586 	}
587 	if (count == nentries)
588 		return;
589 
590 	if (pe[count].net_pe_totbytes == 0)
591 		pe[count].net_pe_lasttime = lasttime;
592 
593 	pe[count].net_pe_totbytes += nns->net_stat_ibytes +
594 	    nns->net_stat_obytes;
595 	pe[count].net_pe_tottime += nns->net_stat_tdiff;
596 	pe[count].net_pe_totibytes += nns->net_stat_ibytes;
597 	pe[count].net_pe_totobytes += nns->net_stat_obytes;
598 	(*pentries)++;
599 }
600 
601 /* Flush the contents of the point entry table to the file. */
602 static void
603 add_pe_to_file(int (*fn)(dladm_usage_t *, void *), net_plot_entry_t *pe,
604     net_stat_t *ns, int entries, void *arg)
605 {
606 	int		count;
607 	dladm_usage_t	usage;
608 	uint64_t	tottime;
609 
610 	bcopy(&ns->net_stat_ctime, &usage.du_etime, sizeof (usage.du_etime));
611 	for (count = 0; count < entries; count++) {
612 		bcopy(pe[count].net_pe_name, &usage.du_name,
613 		    sizeof (usage.du_name));
614 		bcopy(&pe[count].net_pe_lasttime, &usage.du_stime,
615 		    sizeof (usage.du_stime));
616 		usage.du_rbytes = pe[count].net_pe_totibytes;
617 		usage.du_obytes = pe[count].net_pe_totobytes;
618 		tottime = pe[count].net_pe_tottime;
619 		usage.du_bandwidth = (tottime > 0) ?
620 		    ((pe[count].net_pe_totbytes * 8) / tottime) : 0;
621 		usage.du_last = (count == entries-1);
622 		fn(&usage, arg);
623 	}
624 }
625 
626 /*
627  * Net entry functions
628  */
629 static net_entry_t *
630 get_ne_from_table(net_table_t *net_table, char *name)
631 {
632 	int		count;
633 	net_desc_t	*nd;
634 	net_entry_t	*ne = net_table->net_table_head;
635 
636 	for (count = 0; count < net_table->net_entries; count++) {
637 		nd = ne->net_entry_desc;
638 		if (strcmp(name, nd->net_desc_name) == 0)
639 			return (ne);
640 		ne = ne->net_entry_next;
641 	}
642 	return (NULL);
643 }
644 
645 /*  Get the entry for the descriptor, if it exists */
646 static net_desc_t *
647 get_ndesc(net_table_t *net_table, net_desc_t *nd)
648 {
649 	int		count;
650 	net_desc_t	*nd1;
651 	net_entry_t	*ne = net_table->net_table_head;
652 
653 	for (count = 0; count < net_table->net_entries; count++) {
654 		nd1 = ne->net_entry_desc;
655 		if (strcmp(nd1->net_desc_name, nd->net_desc_name) == 0 &&
656 		    strcmp(nd1->net_desc_devname, nd->net_desc_devname) == 0 &&
657 		    bcmp(nd1->net_desc_ehost, nd->net_desc_ehost,
658 		    ETHERADDRL) == 0 &&
659 		    bcmp(nd1->net_desc_edest, nd->net_desc_edest,
660 		    ETHERADDRL) == 0 &&
661 		    nd1->net_desc_vlan_tpid == nd->net_desc_vlan_tpid &&
662 		    nd1->net_desc_vlan_tci == nd->net_desc_vlan_tci &&
663 		    nd1->net_desc_sap == nd->net_desc_sap &&
664 		    nd1->net_desc_cpuid == nd->net_desc_cpuid &&
665 		    nd1->net_desc_priority == nd->net_desc_priority &&
666 		    nd1->net_desc_bw_limit == nd->net_desc_bw_limit &&
667 		    nd1->net_desc_sport == nd->net_desc_sport &&
668 		    nd1->net_desc_dport == nd->net_desc_dport &&
669 		    nd1->net_desc_protocol == nd->net_desc_protocol &&
670 		    nd1->net_desc_dsfield == nd->net_desc_dsfield &&
671 		    IN6_ARE_ADDR_EQUAL(&nd1->net_desc_saddr,
672 		    &nd->net_desc_saddr) &&
673 		    IN6_ARE_ADDR_EQUAL(&nd1->net_desc_daddr,
674 		    &nd->net_desc_daddr)) {
675 			return (nd1);
676 		}
677 		ne = ne->net_entry_next;
678 	}
679 	return (NULL);
680 }
681 
682 /*
683  * Update the stat entries. The stats in the file are cumulative, so in order
684  * to have increments, we maintain a reference stat entry, which contains
685  * the stats when the record was first written and a total stat entry, which
686  * maintains the running count. When we want to add a stat entry, if it
687  * the reference stat entry, we don't come here. For subsequent entries,
688  * we get the increment by subtracting the current value from the reference
689  * stat and the total stat.
690  */
691 static void
692 update_stats(net_stat_t *ns1, net_entry_t *ne, net_stat_t *ref)
693 {
694 
695 	/* get the increment */
696 	ns1->net_stat_ibytes -= (ref->net_stat_ibytes + ref->net_stat_tibytes);
697 	ns1->net_stat_obytes -= (ref->net_stat_obytes + ref->net_stat_tobytes);
698 	ns1->net_stat_ipackets -= (ref->net_stat_ipackets +
699 	    ref->net_stat_tipackets);
700 	ns1->net_stat_opackets -= (ref->net_stat_opackets +
701 	    ref->net_stat_topackets);
702 	ns1->net_stat_ierrors -= (ref->net_stat_ierrors +
703 	    ref->net_stat_tierrors);
704 	ns1->net_stat_oerrors -= (ref->net_stat_oerrors +
705 	    ref->net_stat_toerrors);
706 
707 	/* update total bytes */
708 	ref->net_stat_tibytes += ns1->net_stat_ibytes;
709 	ref->net_stat_tobytes += ns1->net_stat_obytes;
710 	ref->net_stat_tipackets += ns1->net_stat_ipackets;
711 	ref->net_stat_topackets += ns1->net_stat_opackets;
712 	ref->net_stat_tierrors += ns1->net_stat_ierrors;
713 	ref->net_stat_toerrors  += ns1->net_stat_oerrors;
714 
715 	ne->net_entry_tstats->net_stat_ibytes += ns1->net_stat_ibytes;
716 	ne->net_entry_tstats->net_stat_obytes += ns1->net_stat_obytes;
717 	ne->net_entry_tstats->net_stat_ipackets += ns1->net_stat_ipackets;
718 	ne->net_entry_tstats->net_stat_opackets += ns1->net_stat_opackets;
719 	ne->net_entry_tstats->net_stat_ierrors += ns1->net_stat_ierrors;
720 	ne->net_entry_tstats->net_stat_oerrors += ns1->net_stat_oerrors;
721 }
722 
723 /* Add the stat entry into the table */
724 static dladm_status_t
725 add_stat_to_tbl(net_table_t *net_table, net_stat_t *ns)
726 {
727 	net_entry_t	*ne;
728 
729 	ne = get_ne_from_table(net_table, ns->net_stat_name);
730 	if (ne == NULL)
731 		return (DLADM_STATUS_NOMEM);
732 
733 	/* Ptr to flow desc */
734 	ns->net_stat_desc = ne->net_entry_desc;
735 	if (ns->net_stat_desc->net_desc_newrec) {
736 		ns->net_stat_desc->net_desc_newrec = B_FALSE;
737 		ns->net_stat_isref = B_TRUE;
738 		ne->net_entry_sref = ns;
739 	} else if (ns->net_stat_ibytes < ne->net_entry_sref->net_stat_tibytes ||
740 	    (ns->net_stat_obytes < ne->net_entry_sref->net_stat_tobytes)) {
741 		ns->net_stat_isref = B_TRUE;
742 		ne->net_entry_sref = ns;
743 	} else {
744 		ns->net_stat_isref = B_FALSE;
745 		update_stats(ns, ne, ne->net_entry_sref);
746 	}
747 	if (ne->net_entry_shead == NULL) {
748 		ne->net_entry_shead = ns;
749 		ne->net_entry_stail = ns;
750 	} else {
751 		if (!ns->net_stat_isref) {
752 			ne->net_entry_ttime += (ns->net_stat_ctime -
753 			    ne->net_entry_stail->net_stat_ctime);
754 			ns->net_stat_tdiff = ns->net_stat_ctime -
755 			    ne->net_entry_stail->net_stat_ctime;
756 		}
757 		ne->net_entry_stail->net_stat_next = ns;
758 		ne->net_entry_stail = ns;
759 	}
760 
761 	ne->net_entry_scount++;
762 	return (DLADM_STATUS_OK);
763 }
764 
765 /* Add a flow/link descriptor record to the table */
766 static dladm_status_t
767 add_desc(net_table_t *net_table, ea_file_t *ef, int nobjs)
768 {
769 	net_desc_t	*nd;
770 	net_desc_t	*dnd;
771 	int		count;
772 	ea_object_t	scratch;
773 
774 	if ((nd = calloc(1, sizeof (net_desc_t))) == NULL)
775 		return (DLADM_STATUS_NOMEM);
776 	nd->net_desc_newrec = B_TRUE;
777 
778 	for (count = 0; count < nobjs; count++) {
779 		if (ea_get_object(ef, &scratch) == -1) {
780 			free(nd);
781 			return (DLADM_STATUS_NOMEM);
782 		}
783 		add_desc_item(&scratch, nd);
784 	}
785 	if ((dnd = get_ndesc(net_table, nd)) != NULL) {
786 		dnd->net_desc_newrec = B_TRUE;
787 		free(nd);
788 		return (DLADM_STATUS_OK);
789 	}
790 	if (add_desc_to_tbl(net_table, nd) != 0) {
791 		free(nd);
792 		return (DLADM_STATUS_NOMEM);
793 	}
794 	return (DLADM_STATUS_OK);
795 }
796 
797 /* Make an entry into the time sorted list */
798 static void
799 addto_time_list(net_table_t *net_table, net_time_entry_t *nt,
800     net_time_entry_t *ntc)
801 {
802 	net_stat_t		*ns = nt->my_time_stat;
803 	net_stat_t		*ns1;
804 	net_time_entry_t	*end;
805 	net_time_t		*t1;
806 	int			count;
807 
808 	t1 = &ns->net_stat_time;
809 
810 	net_table->net_time_entries++;
811 
812 	if (net_table->net_time_head == NULL) {
813 		net_table->net_time_head = nt;
814 		net_table->net_time_tail = nt;
815 	} else {
816 		net_table->net_time_tail->net_time_entry_next = nt;
817 		nt->net_time_entry_prev = net_table->net_time_tail;
818 		net_table->net_time_tail = nt;
819 	}
820 
821 	if (net_table->net_ctime_head == NULL) {
822 		net_table->net_ctime_head = ntc;
823 		net_table->net_ctime_tail = ntc;
824 	} else {
825 		end = net_table->net_ctime_tail;
826 		count = 0;
827 		while (count < net_table->net_time_entries - 1) {
828 			ns1 = end->my_time_stat;
829 			/* Just add it to the tail */
830 			if (compare_date(t1, &ns1->net_stat_time) ==
831 			    NET_DATE_GREATER) {
832 				break;
833 			}
834 			if (strcmp(ns1->net_stat_name, ns->net_stat_name) ==
835 			    0) {
836 				ntc->net_time_entry_next =
837 				    end->net_time_entry_next;
838 				if (end->net_time_entry_next != NULL) {
839 					end->net_time_entry_next->
840 					    net_time_entry_prev = ntc;
841 				} else {
842 					net_table->net_ctime_tail = ntc;
843 				}
844 				end->net_time_entry_next = ntc;
845 				ntc->net_time_entry_prev = end;
846 				return;
847 			}
848 			count++;
849 			end = end->net_time_entry_prev;
850 		}
851 		net_table->net_ctime_tail->net_time_entry_next = ntc;
852 		ntc->net_time_entry_prev = net_table->net_ctime_tail;
853 		net_table->net_ctime_tail = ntc;
854 	}
855 }
856 
857 /* Add stat entry into the lists */
858 static dladm_status_t
859 add_stats(net_table_t *net_table, ea_file_t *ef, int nobjs)
860 {
861 	net_stat_t		*ns;
862 	int			count;
863 	ea_object_t		scratch;
864 	net_time_entry_t	*nt;
865 	net_time_entry_t	*ntc;
866 
867 	if ((ns = calloc(1, sizeof (net_stat_t))) == NULL)
868 		return (DLADM_STATUS_NOMEM);
869 
870 	if ((nt = calloc(1, sizeof (net_time_entry_t))) == NULL) {
871 		free(ns);
872 		return (DLADM_STATUS_NOMEM);
873 	}
874 	if ((ntc = calloc(1, sizeof (net_time_entry_t))) == NULL) {
875 		free(ns);
876 		free(nt);
877 		return (DLADM_STATUS_NOMEM);
878 	}
879 
880 	nt->my_time_stat = ns;
881 	ntc->my_time_stat = ns;
882 
883 	for (count = 0; count < nobjs; count++) {
884 		if (ea_get_object(ef, &scratch) == -1) {
885 			free(ns);
886 			free(nt);
887 			free(ntc);
888 			return (DLADM_STATUS_NOMEM);
889 		}
890 		add_stat_item(&scratch, ns);
891 	}
892 	if (add_stat_to_tbl(net_table, ns) != 0) {
893 		free(ns);
894 		free(nt);
895 		free(ntc);
896 		return (DLADM_STATUS_NOMEM);
897 	}
898 	addto_time_list(net_table, nt, ntc);
899 	return (DLADM_STATUS_OK);
900 }
901 
902 /* Free the entire table */
903 static void
904 free_logtable(net_table_t *net_table)
905 {
906 	net_entry_t		*head;
907 	net_entry_t		*next;
908 	net_stat_t		*ns;
909 	net_stat_t		*ns1;
910 	net_time_entry_t	*thead;
911 	net_time_entry_t	*tnext;
912 
913 	thead = net_table->net_time_head;
914 	while (thead != NULL) {
915 		thead->my_time_stat = NULL;
916 		tnext = thead->net_time_entry_next;
917 		thead->net_time_entry_next = NULL;
918 		thead->net_time_entry_prev = NULL;
919 		free(thead);
920 		thead = tnext;
921 	}
922 	net_table->net_time_head = NULL;
923 	net_table->net_time_tail = NULL;
924 
925 	thead = net_table->net_ctime_head;
926 	while (thead != NULL) {
927 		thead->my_time_stat = NULL;
928 		tnext = thead->net_time_entry_next;
929 		thead->net_time_entry_next = NULL;
930 		thead->net_time_entry_prev = NULL;
931 		free(thead);
932 		thead = tnext;
933 	}
934 	net_table->net_ctime_head = NULL;
935 	net_table->net_ctime_tail = NULL;
936 
937 	net_table->net_time_entries = 0;
938 
939 	head = net_table->net_table_head;
940 	while (head != NULL) {
941 		next = head->net_entry_next;
942 		head->net_entry_next = NULL;
943 		ns = head->net_entry_shead;
944 		while (ns != NULL) {
945 			ns1 = ns->net_stat_next;
946 			free(ns);
947 			ns = ns1;
948 		}
949 		head->net_entry_scount = 0;
950 		head->net_entry_sref = NULL;
951 		free(head->net_entry_desc);
952 		free(head->net_entry_tstats);
953 		free(head);
954 		head = next;
955 	}
956 	net_table->net_table_head = NULL;
957 	net_table->net_table_tail = NULL;
958 	net_table->net_time_entries = 0;
959 	free(net_table);
960 }
961 
962 /* Parse the exacct file, and return the parsed table. */
963 static void *
964 parse_logfile(char *file, int logtype, dladm_status_t *status)
965 {
966 	ea_file_t	ef;
967 	ea_object_t	scratch;
968 	net_table_t	*net_table;
969 
970 	*status = DLADM_STATUS_OK;
971 	if ((net_table = calloc(1, sizeof (net_table_t))) == NULL) {
972 		*status = DLADM_STATUS_NOMEM;
973 		return (NULL);
974 	}
975 	if (ea_open(&ef, file, NULL, 0, O_RDONLY, 0) == -1) {
976 		*status = DLADM_STATUS_BADARG;
977 		free(net_table);
978 		return (NULL);
979 	}
980 	bzero(&scratch, sizeof (ea_object_t));
981 	while (ea_get_object(&ef, &scratch) != -1) {
982 		if (scratch.eo_type != EO_GROUP) {
983 			(void) ea_free_item(&scratch, EUP_ALLOC);
984 			(void) bzero(&scratch, sizeof (ea_object_t));
985 			continue;
986 		}
987 		/* Read Link Desc/Stat records */
988 		if (logtype == DLADM_LOGTYPE_FLOW) {
989 			/* Flow Descriptor */
990 			if ((scratch.eo_catalog &
991 			    EXD_DATA_MASK) == EXD_GROUP_NET_FLOW_DESC) {
992 				(void) add_desc(net_table, &ef,
993 				    scratch.eo_group.eg_nobjs - 1);
994 			/* Flow Stats */
995 			} else if ((scratch.eo_catalog &
996 			    EXD_DATA_MASK) == EXD_GROUP_NET_FLOW_STATS) {
997 				(void) add_stats(net_table, &ef,
998 				    scratch.eo_group.eg_nobjs - 1);
999 			}
1000 		} else if (logtype == DLADM_LOGTYPE_LINK) {
1001 			/* Link Descriptor */
1002 			if ((scratch.eo_catalog &
1003 			    EXD_DATA_MASK) == EXD_GROUP_NET_LINK_DESC) {
1004 				(void) add_desc(net_table, &ef,
1005 				    scratch.eo_group.eg_nobjs - 1);
1006 			/* Link Stats */
1007 			} else if ((scratch.eo_catalog &
1008 			    EXD_DATA_MASK) == EXD_GROUP_NET_LINK_STATS) {
1009 				(void) add_stats(net_table, &ef,
1010 				    scratch.eo_group.eg_nobjs - 1);
1011 			}
1012 		} else {
1013 			if (((scratch.eo_catalog & EXD_DATA_MASK) ==
1014 			    EXD_GROUP_NET_LINK_DESC) || ((scratch.eo_catalog &
1015 			    EXD_DATA_MASK) == EXD_GROUP_NET_FLOW_DESC)) {
1016 				(void) add_desc(net_table, &ef,
1017 				    scratch.eo_group.eg_nobjs - 1);
1018 			} else if (((scratch.eo_catalog & EXD_DATA_MASK) ==
1019 			    EXD_GROUP_NET_LINK_STATS) || ((scratch.eo_catalog &
1020 			    EXD_DATA_MASK) == EXD_GROUP_NET_FLOW_STATS)) {
1021 				(void) add_stats(net_table, &ef,
1022 				    scratch.eo_group.eg_nobjs - 1);
1023 			}
1024 		}
1025 		(void) ea_free_item(&scratch, EUP_ALLOC);
1026 		(void) bzero(&scratch, sizeof (ea_object_t));
1027 	}
1028 
1029 	(void) ea_close(&ef);
1030 	return ((void *)net_table);
1031 }
1032 
1033 /*
1034  * Walk the ctime list.  This is used when looking for usage records
1035  * based on a "resource" name.
1036  */
1037 dladm_status_t
1038 dladm_walk_usage_res(int (*fn)(dladm_usage_t *, void *), int logtype,
1039     char *logfile, char *resource, char *stime, char *etime, void *arg)
1040 {
1041 	net_table_t		*net_table;
1042 	net_time_t		st, et;
1043 	net_time_entry_t	*start;
1044 	net_stat_t		*ns = NULL;
1045 	net_stat_t		*nns;
1046 	uint64_t		tot_time = 0;
1047 	uint64_t		last_time;
1048 	uint64_t		tot_bytes = 0;
1049 	uint64_t		tot_ibytes = 0;
1050 	uint64_t		tot_obytes = 0;
1051 	boolean_t		gotstart = B_FALSE;
1052 	dladm_status_t		status;
1053 	dladm_usage_t		usage;
1054 	int			step = 1;
1055 
1056 	/* Parse the log file */
1057 	net_table = parse_logfile(logfile, logtype, &status);
1058 	if (net_table == NULL)
1059 		return (status);
1060 
1061 	if (net_table->net_entries == 0)
1062 		return (DLADM_STATUS_OK);
1063 	start = net_table->net_ctime_head;
1064 
1065 	/* Time range */
1066 	status = get_time_range(net_table->net_ctime_head,
1067 	    net_table->net_ctime_tail, &st, &et, stime, etime);
1068 	if (status != DLADM_STATUS_OK)
1069 		return (status);
1070 
1071 	while (start != NULL) {
1072 		nns = start->my_time_stat;
1073 
1074 		/* Get to the resource we are interested in */
1075 		if (strcmp(resource, nns->net_stat_name) != 0) {
1076 			start = start->net_time_entry_next;
1077 			continue;
1078 		}
1079 
1080 		/* Find the first record */
1081 		if (!gotstart) {
1082 			get_starting_point(start, &start, &st, stime,
1083 			    &last_time);
1084 			if (start == NULL)
1085 				break;
1086 			nns = start->my_time_stat;
1087 			gotstart = B_TRUE;
1088 		}
1089 
1090 		/* Write one entry and return if we are out of the range */
1091 		if (etime != NULL && compare_time(&nns->net_stat_time, &et)
1092 		    == NET_TIME_GREATER) {
1093 			if (tot_bytes != 0) {
1094 				bcopy(ns->net_stat_name, &usage.du_name,
1095 				    sizeof (usage.du_name));
1096 				bcopy(&last_time, &usage.du_stime,
1097 				    sizeof (usage.du_stime));
1098 				bcopy(&ns->net_stat_ctime, &usage.du_etime,
1099 				    sizeof (usage.du_etime));
1100 				usage.du_rbytes = tot_ibytes;
1101 				usage.du_obytes = tot_obytes;
1102 				usage.du_bandwidth = tot_bytes*8/tot_time;
1103 				usage.du_last = B_TRUE;
1104 				fn(&usage, arg);
1105 			}
1106 			return (DLADM_STATUS_OK);
1107 		}
1108 
1109 		/*
1110 		 * If this is a reference entry, just print what we have
1111 		 * and proceed.
1112 		 */
1113 		if (nns->net_stat_isref) {
1114 			if (tot_bytes != 0) {
1115 				bcopy(&nns->net_stat_name, &usage.du_name,
1116 				    sizeof (usage.du_name));
1117 				bcopy(&nns->net_stat_ctime, &usage.du_stime,
1118 				    sizeof (usage.du_stime));
1119 				usage.du_rbytes = tot_ibytes;
1120 				usage.du_obytes = tot_obytes;
1121 				usage.du_bandwidth = tot_bytes*8/tot_time;
1122 				usage.du_last = B_TRUE;
1123 				fn(&usage, arg);
1124 				NET_RESET_TOT(tot_bytes, tot_time, tot_ibytes,
1125 				    tot_obytes, step);
1126 			}
1127 			last_time = nns->net_stat_ctime;
1128 			start = start->net_time_entry_next;
1129 			continue;
1130 		}
1131 
1132 		ns = nns;
1133 		if (--step == 0) {
1134 			tot_bytes += ns->net_stat_ibytes + ns->net_stat_obytes;
1135 			tot_ibytes += ns->net_stat_ibytes;
1136 			tot_obytes += ns->net_stat_obytes;
1137 			tot_time += ns->net_stat_tdiff;
1138 			bcopy(&ns->net_stat_name, &usage.du_name,
1139 			    sizeof (usage.du_name));
1140 			bcopy(&last_time, &usage.du_stime,
1141 			    sizeof (usage.du_stime));
1142 			bcopy(&ns->net_stat_ctime, &usage.du_etime,
1143 			    sizeof (usage.du_etime));
1144 			usage.du_rbytes = tot_ibytes;
1145 			usage.du_obytes = tot_obytes;
1146 			usage.du_bandwidth = tot_bytes*8/tot_time;
1147 			usage.du_last = B_TRUE;
1148 			fn(&usage, arg);
1149 
1150 			NET_RESET_TOT(tot_bytes, tot_time, tot_ibytes,
1151 			    tot_obytes, step);
1152 			last_time = ns->net_stat_ctime;
1153 		} else {
1154 			tot_bytes += ns->net_stat_ibytes + ns->net_stat_obytes;
1155 			tot_ibytes += ns->net_stat_ibytes;
1156 			tot_obytes += ns->net_stat_obytes;
1157 			tot_time += ns->net_stat_tdiff;
1158 		}
1159 		start = start->net_time_entry_next;
1160 	}
1161 
1162 	if (tot_bytes != 0) {
1163 		bcopy(&ns->net_stat_name, &usage.du_name,
1164 		    sizeof (usage.du_name));
1165 		bcopy(&last_time, &usage.du_stime,
1166 		    sizeof (usage.du_stime));
1167 		bcopy(&ns->net_stat_ctime, &usage.du_etime,
1168 		    sizeof (usage.du_etime));
1169 		usage.du_rbytes = tot_ibytes;
1170 		usage.du_obytes = tot_obytes;
1171 		usage.du_bandwidth = tot_bytes*8/tot_time;
1172 		usage.du_last = B_TRUE;
1173 		fn(&usage, arg);
1174 	}
1175 
1176 	free_logtable(net_table);
1177 	return (status);
1178 }
1179 
1180 /*
1181  * Walk the time sorted list if a resource is not specified.
1182  */
1183 dladm_status_t
1184 dladm_walk_usage_time(int (*fn)(dladm_usage_t *, void *), int logtype,
1185     char *logfile, char *stime, char *etime, void *arg)
1186 {
1187 	net_table_t		*net_table;
1188 	net_time_entry_t	*start;
1189 	net_stat_t		*ns = NULL, *nns;
1190 	net_time_t		st, et, *t1;
1191 	net_desc_t		*nd;
1192 	net_entry_t		*ne;
1193 	net_plot_entry_t	*pe;
1194 	int			count;
1195 	int			step = 1;
1196 	int			nentries = 0, pentries = 0;
1197 	uint64_t		last_time;
1198 	dladm_status_t		status;
1199 
1200 	/* Parse the log file */
1201 	net_table = parse_logfile(logfile, logtype, &status);
1202 	if (net_table == NULL)
1203 		return (status);
1204 
1205 	if (net_table->net_entries == 0)
1206 		return (DLADM_STATUS_OK);
1207 	start = net_table->net_time_head;
1208 
1209 	/* Find the first and last records and starting point */
1210 	status = get_time_range(net_table->net_time_head,
1211 	    net_table->net_time_tail, &st, &et, stime, etime);
1212 	if (status != DLADM_STATUS_OK)
1213 		return (status);
1214 	get_starting_point(start, &start, &st, stime, &last_time);
1215 	/*
1216 	 * Could assert to be non-null, since get_time_range()
1217 	 * would have adjusted.
1218 	 */
1219 	if (start == NULL)
1220 		return (DLADM_STATUS_BADTIMEVAL);
1221 
1222 	/*
1223 	 * Collect entries for all resources in a time slot before
1224 	 * writing to the file.
1225 	 */
1226 	nentries = net_table->net_entries;
1227 
1228 	pe = malloc(sizeof (net_plot_entry_t) * net_table->net_entries + 1);
1229 	if (pe == NULL)
1230 		return (DLADM_STATUS_NOMEM);
1231 
1232 	ne = net_table->net_table_head;
1233 	for (count = 0; count < nentries; count++) {
1234 		nd = ne->net_entry_desc;
1235 		pe[count].net_pe_name = nd->net_desc_name;
1236 		ne = ne->net_entry_next;
1237 	}
1238 
1239 	clear_pe(pe, nentries, &pentries);
1240 
1241 	/* Write header to file */
1242 	/* add_pe_to_file(fn, pe, ns, nentries, arg); */
1243 
1244 	t1 = &start->my_time_stat->net_stat_time;
1245 
1246 	while (start != NULL) {
1247 
1248 		nns = start->my_time_stat;
1249 		/*
1250 		 * We have crossed the time boundary, check if we need to
1251 		 * print out now.
1252 		 */
1253 		if (compare_time(&nns->net_stat_time, t1) ==
1254 		    NET_TIME_GREATER) {
1255 			/* return if we are out of the range */
1256 			if (etime != NULL &&
1257 			    compare_time(&nns->net_stat_time, &et) ==
1258 			    NET_TIME_GREATER) {
1259 				if (pentries > 0) {
1260 					add_pe_to_file(fn, pe, ns, nentries,
1261 					    arg);
1262 					clear_pe(pe, nentries, &pentries);
1263 				}
1264 				free(pe);
1265 				return (DLADM_STATUS_OK);
1266 			}
1267 			/* update the stats from the ns. */
1268 			t1 = &nns->net_stat_time;
1269 			last_time = ns->net_stat_ctime;
1270 			if (--step == 0) {
1271 				if (pentries > 0) {
1272 					add_pe_to_file(fn, pe, ns, nentries,
1273 					    arg);
1274 					clear_pe(pe, nentries, &pentries);
1275 				}
1276 				step = 1;
1277 			}
1278 		}
1279 
1280 		/*
1281 		 * if this is a reference entry, just print what we have
1282 		 * for this resource and proceed. We will end up writing
1283 		 * the stats for all the entries when we hit a ref element,
1284 		 * which means 'steps' for some might not be accurate, but
1285 		 * that is fine, the alternative is to write only the
1286 		 * resource for which we hit a reference entry.
1287 		 */
1288 		if (nns->net_stat_isref) {
1289 			if (pentries > 0) {
1290 				add_pe_to_file(fn, pe, ns, nentries, arg);
1291 				clear_pe(pe, nentries, &pentries);
1292 			}
1293 			step = 1;
1294 		} else {
1295 			update_pe(pe, nns, nentries, &pentries, last_time);
1296 		}
1297 		ns = nns;
1298 		start = start->net_time_entry_next;
1299 	}
1300 
1301 	if (pentries > 0)
1302 		add_pe_to_file(fn, pe, ns, nentries, arg);
1303 
1304 	free(pe);
1305 	free_logtable(net_table);
1306 
1307 	return (DLADM_STATUS_OK);
1308 }
1309 
1310 dladm_status_t
1311 dladm_usage_summary(int (*fn)(dladm_usage_t *, void *), int logtype,
1312     char *logfile, void *arg)
1313 {
1314 	net_table_t		*net_table;
1315 	net_entry_t		*ne;
1316 	net_desc_t		*nd;
1317 	net_stat_t		*ns;
1318 	int			count;
1319 	dladm_usage_t		usage;
1320 	dladm_status_t		status;
1321 
1322 	/* Parse the log file */
1323 	net_table = parse_logfile(logfile, logtype, &status);
1324 	if (net_table == NULL)
1325 		return (status);
1326 
1327 	if (net_table->net_entries == 0)
1328 		return (DLADM_STATUS_OK);
1329 
1330 	ne = net_table->net_table_head;
1331 	for (count = 0; count < net_table->net_entries; count++) {
1332 		ns = ne->net_entry_tstats;
1333 		nd = ne->net_entry_desc;
1334 
1335 		if (ns->net_stat_ibytes + ns->net_stat_obytes == 0)
1336 			continue;
1337 		bcopy(&nd->net_desc_name, &usage.du_name,
1338 		    sizeof (usage.du_name));
1339 		usage.du_duration = ne->net_entry_ttime;
1340 		usage.du_ipackets = ns->net_stat_ipackets;
1341 		usage.du_rbytes = ns->net_stat_ibytes;
1342 		usage.du_opackets = ns->net_stat_opackets;
1343 		usage.du_obytes = ns->net_stat_obytes;
1344 		usage.du_bandwidth =
1345 		    (ns->net_stat_ibytes + ns->net_stat_obytes) * 8 /
1346 		    usage.du_duration;
1347 		usage.du_last = (count == net_table->net_entries-1);
1348 		fn(&usage, arg);
1349 
1350 		ne = ne->net_entry_next;
1351 	}
1352 
1353 	free_logtable(net_table);
1354 	return (DLADM_STATUS_OK);
1355 }
1356 
1357 /*
1358  * Walk the ctime list and display the dates of the records.
1359  */
1360 dladm_status_t
1361 dladm_usage_dates(int (*fn)(dladm_usage_t *, void *), int logtype,
1362     char *logfile, char *resource, void *arg)
1363 {
1364 	net_table_t		*net_table;
1365 	net_time_entry_t	*start;
1366 	net_stat_t		*nns;
1367 	net_time_t		st;
1368 	net_time_t		*lasttime = NULL;
1369 	uint64_t		last_time;
1370 	boolean_t		gotstart = B_FALSE;
1371 	dladm_status_t		status;
1372 	dladm_usage_t		usage;
1373 
1374 	/* Parse the log file */
1375 	net_table = parse_logfile(logfile, logtype, &status);
1376 	if (net_table == NULL)
1377 		return (status);
1378 
1379 	if (net_table->net_entries == 0)
1380 		return (DLADM_STATUS_OK);
1381 
1382 	start = net_table->net_ctime_head;
1383 
1384 	while (start != NULL) {
1385 		nns = start->my_time_stat;
1386 
1387 		/* get to the resource we are interested in */
1388 		if (resource != NULL) {
1389 			if (strcmp(resource, nns->net_stat_name) != 0) {
1390 				start = start->net_time_entry_next;
1391 				continue;
1392 			}
1393 		}
1394 
1395 		/* get the starting point in the logfile */
1396 		if (!gotstart) {
1397 			get_starting_point(start, &start, &st, NULL,
1398 			    &last_time);
1399 			if (start == NULL)
1400 				break;
1401 			nns = start->my_time_stat;
1402 			gotstart = B_TRUE;
1403 		}
1404 
1405 		if (lasttime == NULL ||
1406 		    compare_date(&nns->net_stat_time, lasttime) ==
1407 		    NET_DATE_GREATER) {
1408 			bzero(&usage, sizeof (dladm_usage_t));
1409 			(void) strlcpy(usage.du_name, nns->net_stat_name,
1410 			    sizeof (usage.du_name));
1411 			bcopy(&nns->net_stat_ctime, &usage.du_stime,
1412 			    sizeof (usage.du_stime));
1413 			fn(&usage, arg);
1414 			lasttime = &nns->net_stat_time;
1415 		}
1416 
1417 		start = start->net_time_entry_next;
1418 		continue;
1419 	}
1420 
1421 	free_logtable(net_table);
1422 	return (status);
1423 }
1424