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 */
49typedef 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 */
71typedef 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 */
81typedef 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 */
104typedef 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 */
114typedef 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 */
126typedef 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 */
133typedef	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 */
188static void
189dissect_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 */
246static void
247add_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 */
292static void
293add_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 */
383static dladm_status_t
384add_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 */
413static int
414compare_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 */
433static int
434compare_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 */
466static dladm_status_t
467chk_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 */
502static dladm_status_t
503get_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 */
531static void
532get_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 */
563static void
564clear_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 */
578static void
579update_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. */
603static void
604add_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 */
630static net_entry_t *
631get_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 */
647static net_desc_t *
648get_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 */
692static void
693update_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 */
725static dladm_status_t
726add_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 */
767static dladm_status_t
768add_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 */
799static void
800addto_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 */
859static dladm_status_t
860add_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 */
904static void
905free_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. */
964static void *
965parse_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 */
1038dladm_status_t
1039dladm_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 */
1184dladm_status_t
1185dladm_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
1311dladm_status_t
1312dladm_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 */
1363dladm_status_t
1364dladm_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