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