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.
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.
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]
23 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
32 #include <sys/ethernet.h>
36 #define GBIT 1000000000
40 #define NET_RESET_TOT(tbytes, ttime, tibytes, tobytes, step) { \
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
;
70 /* Time structure: Year, Month, Day, Hour, Min, Sec */
71 typedef struct net_time_s
{
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
;
103 /* Used to create the [gnu]plot file */
104 typedef struct net_plot_entry_s
{
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
;
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
;
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
;
132 /* The parsed table */
133 typedef struct net_table_s
{
135 net_entry_t
*net_table_head
;
136 net_entry_t
*net_table_tail
;
140 * Optimization I : List sorted by time, i.e:
142 * -------------------------------
150 net_time_entry_t
*net_time_head
;
151 net_time_entry_t
*net_time_tail
;
154 * Optimization II : List sorted by resources
156 * -------------------------------
164 net_time_entry_t
*net_ctime_head
;
165 net_time_entry_t
*net_ctime_tail
;
167 /* Common to both the above (sorted) lists. */
168 int net_time_entries
;
171 #define NET_DATE_GREATER 0
172 #define NET_DATE_LESSER 1
173 #define NET_DATE_EQUAL 2
175 #define NET_TIME_GREATER 0
176 #define NET_TIME_LESSER 1
177 #define NET_TIME_EQUAL 2
180 #define FMT_UINT64 "%-15llu"
182 #define FMT_UINT64 "%-15lu"
186 * Given a timebuf of the form M/D/Y,H:M:S break it into individual elements.
189 dissect_time(char *tbuf
, net_time_t
*nt
)
197 if (tbuf
== NULL
|| nt
== NULL
)
200 d
= strtok(tbuf
, ","); /* Date */
201 t
= strtok(NULL
, ","); /* Time */
207 nt
->net_time_mon
= strtol(dd
, &endp
, 10);
210 dd
= strtok(NULL
, "/");
213 nt
->net_time_day
= strtol(dd
, &endp
, 10);
216 dd
= strtok(NULL
, "/");
219 nt
->net_time_yr
= strtol(dd
, &endp
, 10);
221 nt
->net_time_yr
+= 2000;
230 nt
->net_time_hr
= strtol(h
, &endp
, 10);
233 h
= strtok(NULL
, ":");
236 nt
->net_time_min
= strtol(h
, &endp
, 10);
239 h
= strtok(NULL
, ":");
242 nt
->net_time_sec
= strtol(h
, &endp
, 10);
245 /* Get a stat item from an object in the exacct file */
247 add_stat_item(ea_object_t
*o
, net_stat_t
*ns
)
249 switch (o
->eo_catalog
& EXT_TYPE_MASK
) {
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
));
257 if ((o
->eo_catalog
& EXD_DATA_MASK
) == EXD_NET_STATS_CURTIME
) {
259 char timebuf
[TIMEBUFLEN
];
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
;
291 /* Get a description item from an object in the exacct file */
293 add_desc_item(ea_object_t
*o
, net_desc_t
*nd
)
295 switch (o
->eo_catalog
& EXT_TYPE_MASK
) {
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
));
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
;
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
) ==
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
;
335 if ((o
->eo_catalog
& EXD_DATA_MASK
) == EXD_NET_DESC_V4SADDR
||
336 (o
->eo_catalog
& EXD_DATA_MASK
) == EXD_NET_DESC_V4DADDR
) {
339 addr
.s_addr
= htonl(o
->eo_item
.ei_uint32
);
341 if ((o
->eo_catalog
& EXD_DATA_MASK
) ==
342 EXD_NET_DESC_V4SADDR
) {
343 IN6_INADDR_TO_V4MAPPED(&addr
,
344 &nd
->net_desc_saddr
);
346 IN6_INADDR_TO_V4MAPPED(&addr
,
347 &nd
->net_desc_daddr
);
352 if ((o
->eo_catalog
& EXD_DATA_MASK
) == EXD_NET_DESC_BWLIMIT
)
353 nd
->net_desc_bw_limit
= o
->eo_item
.ei_uint64
;
356 if ((o
->eo_catalog
& EXD_DATA_MASK
) == EXD_NET_DESC_V6SADDR
||
357 (o
->eo_catalog
& EXD_DATA_MASK
) == EXD_NET_DESC_V6DADDR
) {
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
;
365 nd
->net_desc_daddr
= addr
;
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
,
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
,
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
)
388 if ((ne
= calloc(1, sizeof (net_entry_t
))) == NULL
)
389 return (DLADM_STATUS_NOMEM
);
391 if ((ne
->net_entry_tstats
= calloc(1, sizeof (net_stat_t
))) == NULL
) {
393 return (DLADM_STATUS_NOMEM
);
396 ne
->net_entry_desc
= nd
;
397 ne
->net_entry_shead
= NULL
;
398 ne
->net_entry_stail
= NULL
;
399 ne
->net_entry_scount
= 0;
401 if (net_table
->net_table_head
== NULL
) {
402 net_table
->net_table_head
= ne
;
403 net_table
->net_table_tail
= ne
;
405 net_table
->net_table_tail
->net_entry_next
= ne
;
406 net_table
->net_table_tail
= ne
;
408 net_table
->net_entries
++;
409 return (DLADM_STATUS_OK
);
412 /* Compare dates and return if t1 is equal, greater or lesser than t2 */
414 compare_date(net_time_t
*t1
, net_time_t
*t2
)
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
);
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
);
429 return (NET_DATE_LESSER
);
432 /* Compare times and return if t1 is equal, greater or lesser than t2 */
434 compare_time(net_time_t
*t1
, net_time_t
*t2
)
438 cd
= compare_date(t1
, t2
);
440 if (cd
== NET_DATE_GREATER
) {
441 return (NET_TIME_GREATER
);
442 } else if (cd
== NET_DATE_LESSER
) {
443 return (NET_TIME_LESSER
);
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
);
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
);
459 return (NET_TIME_LESSER
);
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.
466 static dladm_status_t
467 chk_time_bound(net_time_t
*s
, net_time_t
*e
, net_time_t
*sns
,
470 if (s
!= NULL
&& e
!= NULL
) {
471 if (compare_time(s
, e
) == NET_TIME_GREATER
)
472 return (DLADM_STATUS_BADTIMEVAL
);
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
;
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
;
494 return (DLADM_STATUS_OK
);
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.
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
)
506 bzero(st
, sizeof (net_time_t
));
507 bzero(et
, sizeof (net_time_t
));
509 if (stime
== NULL
&& etime
== NULL
)
513 dissect_time(stime
, st
);
515 dissect_time(etime
, et
);
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
));
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.
532 get_starting_point(net_time_entry_t
*head
, net_time_entry_t
**start
,
533 net_time_t
*st
, char *stime
, uint64_t *lasttime
)
535 net_time_entry_t
*next
= head
;
543 *lasttime
= head
->my_time_stat
->net_stat_ctime
;
547 while (next
!= NULL
) {
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
;
560 * Point entry (pe) functions
562 /* Clear all the counters. Done after the contents are written to the file */
564 clear_pe(net_plot_entry_t
*pe
, int entries
, int *pentries
)
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;
577 /* Update an entry in the point entry table */
579 update_pe(net_plot_entry_t
*pe
, net_stat_t
*nns
, int nentries
,
580 int *pentries
, uint64_t lasttime
)
584 for (count
= 0; count
< nentries
; count
++) {
585 if (strcmp(pe
[count
].net_pe_name
, nns
->net_stat_name
) == 0)
588 if (count
== nentries
)
591 if (pe
[count
].net_pe_totbytes
== 0)
592 pe
[count
].net_pe_lasttime
= lasttime
;
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
;
602 /* Flush the contents of the point entry table to the file. */
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
)
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);
628 * Net entry functions
631 get_ne_from_table(net_table_t
*net_table
, char *name
)
635 net_entry_t
*ne
= net_table
->net_table_head
;
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)
641 ne
= ne
->net_entry_next
;
646 /* Get the entry for the descriptor, if it exists */
648 get_ndesc(net_table_t
*net_table
, net_desc_t
*nd
)
652 net_entry_t
*ne
= net_table
->net_table_head
;
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
,
660 bcmp(nd1
->net_desc_edest
, nd
->net_desc_edest
,
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
)) {
678 ne
= ne
->net_entry_next
;
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.
693 update_stats(net_stat_t
*ns1
, net_entry_t
*ne
, net_stat_t
*ref
)
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
);
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
;
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
;
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
)
730 ne
= get_ne_from_table(net_table
, ns
->net_stat_name
);
732 return (DLADM_STATUS_NOMEM
);
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
;
745 ns
->net_stat_isref
= B_FALSE
;
746 update_stats(ns
, ne
, ne
->net_entry_sref
);
748 if (ne
->net_entry_shead
== NULL
) {
749 ne
->net_entry_shead
= ns
;
750 ne
->net_entry_stail
= ns
;
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
;
758 ne
->net_entry_stail
->net_stat_next
= ns
;
759 ne
->net_entry_stail
= ns
;
762 ne
->net_entry_scount
++;
763 return (DLADM_STATUS_OK
);
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
)
775 if ((nd
= calloc(1, sizeof (net_desc_t
))) == NULL
)
776 return (DLADM_STATUS_NOMEM
);
777 nd
->net_desc_newrec
= B_TRUE
;
779 for (count
= 0; count
< nobjs
; count
++) {
780 if (ea_get_object(ef
, &scratch
) == -1) {
782 return (DLADM_STATUS_NOMEM
);
784 add_desc_item(&scratch
, nd
);
786 if ((dnd
= get_ndesc(net_table
, nd
)) != NULL
) {
787 dnd
->net_desc_newrec
= B_TRUE
;
789 return (DLADM_STATUS_OK
);
791 if (add_desc_to_tbl(net_table
, nd
) != 0) {
793 return (DLADM_STATUS_NOMEM
);
795 return (DLADM_STATUS_OK
);
798 /* Make an entry into the time sorted list */
800 addto_time_list(net_table_t
*net_table
, net_time_entry_t
*nt
,
801 net_time_entry_t
*ntc
)
803 net_stat_t
*ns
= nt
->my_time_stat
;
805 net_time_entry_t
*end
;
809 t1
= &ns
->net_stat_time
;
811 net_table
->net_time_entries
++;
813 if (net_table
->net_time_head
== NULL
) {
814 net_table
->net_time_head
= nt
;
815 net_table
->net_time_tail
= nt
;
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
;
822 if (net_table
->net_ctime_head
== NULL
) {
823 net_table
->net_ctime_head
= ntc
;
824 net_table
->net_ctime_tail
= ntc
;
826 end
= net_table
->net_ctime_tail
;
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
) ==
835 if (strcmp(ns1
->net_stat_name
, ns
->net_stat_name
) ==
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
;
843 net_table
->net_ctime_tail
= ntc
;
845 end
->net_time_entry_next
= ntc
;
846 ntc
->net_time_entry_prev
= end
;
850 end
= end
->net_time_entry_prev
;
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
;
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
)
865 net_time_entry_t
*nt
;
866 net_time_entry_t
*ntc
;
868 if ((ns
= calloc(1, sizeof (net_stat_t
))) == NULL
)
869 return (DLADM_STATUS_NOMEM
);
871 if ((nt
= calloc(1, sizeof (net_time_entry_t
))) == NULL
) {
873 return (DLADM_STATUS_NOMEM
);
875 if ((ntc
= calloc(1, sizeof (net_time_entry_t
))) == NULL
) {
878 return (DLADM_STATUS_NOMEM
);
881 nt
->my_time_stat
= ns
;
882 ntc
->my_time_stat
= ns
;
884 for (count
= 0; count
< nobjs
; count
++) {
885 if (ea_get_object(ef
, &scratch
) == -1) {
889 return (DLADM_STATUS_NOMEM
);
891 add_stat_item(&scratch
, ns
);
893 if (add_stat_to_tbl(net_table
, ns
) != 0) {
897 return (DLADM_STATUS_NOMEM
);
899 addto_time_list(net_table
, nt
, ntc
);
900 return (DLADM_STATUS_OK
);
903 /* Free the entire table */
905 free_logtable(net_table_t
*net_table
)
911 net_time_entry_t
*thead
;
912 net_time_entry_t
*tnext
;
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
;
923 net_table
->net_time_head
= NULL
;
924 net_table
->net_time_tail
= NULL
;
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
;
935 net_table
->net_ctime_head
= NULL
;
936 net_table
->net_ctime_tail
= NULL
;
938 net_table
->net_time_entries
= 0;
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
;
946 ns1
= ns
->net_stat_next
;
950 head
->net_entry_scount
= 0;
951 head
->net_entry_sref
= NULL
;
952 free(head
->net_entry_desc
);
953 free(head
->net_entry_tstats
);
957 net_table
->net_table_head
= NULL
;
958 net_table
->net_table_tail
= NULL
;
959 net_table
->net_time_entries
= 0;
963 /* Parse the exacct file, and return the parsed table. */
965 parse_logfile(char *file
, int logtype
, dladm_status_t
*status
)
969 net_table_t
*net_table
;
971 *status
= DLADM_STATUS_OK
;
972 if ((net_table
= calloc(1, sizeof (net_table_t
))) == NULL
) {
973 *status
= DLADM_STATUS_NOMEM
;
976 if (ea_open(&ef
, file
, NULL
, 0, O_RDONLY
, 0) == -1) {
977 *status
= DLADM_STATUS_BADARG
;
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
));
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);
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);
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);
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);
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);
1026 (void) ea_free_item(&scratch
, EUP_ALLOC
);
1027 (void) bzero(&scratch
, sizeof (ea_object_t
));
1030 (void) ea_close(&ef
);
1031 return ((void *)net_table
);
1035 * Walk the ctime list. This is used when looking for usage records
1036 * based on a "resource" name.
1039 dladm_walk_usage_res(int (*fn
)(dladm_usage_t
*, void *), int logtype
,
1040 char *logfile
, char *resource
, char *stime
, char *etime
, void *arg
)
1042 net_table_t
*net_table
;
1044 net_time_entry_t
*start
;
1045 net_stat_t
*ns
= NULL
;
1047 uint64_t tot_time
= 0;
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
;
1057 /* Parse the log file */
1058 net_table
= parse_logfile(logfile
, logtype
, &status
);
1059 if (net_table
== NULL
)
1062 if (net_table
->net_entries
== 0)
1063 return (DLADM_STATUS_OK
);
1064 start
= net_table
->net_ctime_head
;
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
)
1072 while (start
!= NULL
) {
1073 nns
= start
->my_time_stat
;
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
;
1081 /* Find the first record */
1083 get_starting_point(start
, &start
, &st
, stime
,
1087 nns
= start
->my_time_stat
;
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
;
1107 return (DLADM_STATUS_OK
);
1111 * If this is a reference entry, just print what we have
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
;
1125 NET_RESET_TOT(tot_bytes
, tot_time
, tot_ibytes
,
1128 last_time
= nns
->net_stat_ctime
;
1129 start
= start
->net_time_entry_next
;
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
;
1151 NET_RESET_TOT(tot_bytes
, tot_time
, tot_ibytes
,
1153 last_time
= ns
->net_stat_ctime
;
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
;
1160 start
= start
->net_time_entry_next
;
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
;
1177 free_logtable(net_table
);
1182 * Walk the time sorted list if a resource is not specified.
1185 dladm_walk_usage_time(int (*fn
)(dladm_usage_t
*, void *), int logtype
,
1186 char *logfile
, char *stime
, char *etime
, void *arg
)
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
;
1194 net_plot_entry_t
*pe
;
1197 int nentries
= 0, pentries
= 0;
1199 dladm_status_t status
;
1201 /* Parse the log file */
1202 net_table
= parse_logfile(logfile
, logtype
, &status
);
1203 if (net_table
== NULL
)
1206 if (net_table
->net_entries
== 0)
1207 return (DLADM_STATUS_OK
);
1208 start
= net_table
->net_time_head
;
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
)
1215 get_starting_point(start
, &start
, &st
, stime
, &last_time
);
1217 * Could assert to be non-null, since get_time_range()
1218 * would have adjusted.
1221 return (DLADM_STATUS_BADTIMEVAL
);
1224 * Collect entries for all resources in a time slot before
1225 * writing to the file.
1227 nentries
= net_table
->net_entries
;
1229 pe
= malloc(sizeof (net_plot_entry_t
) * net_table
->net_entries
+ 1);
1231 return (DLADM_STATUS_NOMEM
);
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
;
1240 clear_pe(pe
, nentries
, &pentries
);
1242 /* Write header to file */
1243 /* add_pe_to_file(fn, pe, ns, nentries, arg); */
1245 t1
= &start
->my_time_stat
->net_stat_time
;
1247 while (start
!= NULL
) {
1249 nns
= start
->my_time_stat
;
1251 * We have crossed the time boundary, check if we need to
1254 if (compare_time(&nns
->net_stat_time
, t1
) ==
1256 /* return if we are out of the range */
1257 if (etime
!= NULL
&&
1258 compare_time(&nns
->net_stat_time
, &et
) ==
1261 add_pe_to_file(fn
, pe
, ns
, nentries
,
1263 clear_pe(pe
, nentries
, &pentries
);
1266 return (DLADM_STATUS_OK
);
1268 /* update the stats from the ns. */
1269 t1
= &nns
->net_stat_time
;
1270 last_time
= ns
->net_stat_ctime
;
1273 add_pe_to_file(fn
, pe
, ns
, nentries
,
1275 clear_pe(pe
, nentries
, &pentries
);
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.
1289 if (nns
->net_stat_isref
) {
1291 add_pe_to_file(fn
, pe
, ns
, nentries
, arg
);
1292 clear_pe(pe
, nentries
, &pentries
);
1296 update_pe(pe
, nns
, nentries
, &pentries
, last_time
);
1299 start
= start
->net_time_entry_next
;
1303 add_pe_to_file(fn
, pe
, ns
, nentries
, arg
);
1306 free_logtable(net_table
);
1308 return (DLADM_STATUS_OK
);
1312 dladm_usage_summary(int (*fn
)(dladm_usage_t
*, void *), int logtype
,
1313 char *logfile
, void *arg
)
1315 net_table_t
*net_table
;
1320 dladm_usage_t usage
;
1321 dladm_status_t status
;
1323 /* Parse the log file */
1324 net_table
= parse_logfile(logfile
, logtype
, &status
);
1325 if (net_table
== NULL
)
1328 if (net_table
->net_entries
== 0)
1329 return (DLADM_STATUS_OK
);
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
;
1336 if (ns
->net_stat_ibytes
+ ns
->net_stat_obytes
== 0) {
1337 ne
= ne
->net_entry_next
;
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 /
1350 usage
.du_last
= (count
== net_table
->net_entries
-1);
1353 ne
= ne
->net_entry_next
;
1356 free_logtable(net_table
);
1357 return (DLADM_STATUS_OK
);
1361 * Walk the ctime list and display the dates of the records.
1364 dladm_usage_dates(int (*fn
)(dladm_usage_t
*, void *), int logtype
,
1365 char *logfile
, char *resource
, void *arg
)
1367 net_table_t
*net_table
;
1368 net_time_entry_t
*start
;
1371 net_time_t
*lasttime
= NULL
;
1373 boolean_t gotstart
= B_FALSE
;
1374 dladm_status_t status
;
1375 dladm_usage_t usage
;
1377 /* Parse the log file */
1378 net_table
= parse_logfile(logfile
, logtype
, &status
);
1379 if (net_table
== NULL
)
1382 if (net_table
->net_entries
== 0)
1383 return (DLADM_STATUS_OK
);
1385 start
= net_table
->net_ctime_head
;
1387 while (start
!= NULL
) {
1388 nns
= start
->my_time_stat
;
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
;
1398 /* get the starting point in the logfile */
1400 get_starting_point(start
, &start
, &st
, NULL
,
1404 nns
= start
->my_time_stat
;
1408 if (lasttime
== NULL
||
1409 compare_date(&nns
->net_stat_time
, lasttime
) ==
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
));
1417 lasttime
= &nns
->net_stat_time
;
1420 start
= start
->net_time_entry_next
;
1424 free_logtable(net_table
);