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