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